Kaizen #92 - Bulk Read in PHP SDK

Kaizen #92 - Bulk Read in PHP SDK

Heya! Welcome back to the PHP SDK fold in our Kaizen series.

In this post, we will explore how to use bulk read APIs and how to structure the criteria format for different field data types in PHP SDK based on v4 APIs. This will ease your process of handling large volumes of data in an efficient and streamlined manner. 

What does Bulk Read API do?

The Bulk Read API helps you to fetch large volumes of data that meet specific criteria in a single request. Unlike the GET Records API , which has a limitation of 200 records per call, the Bulk Read API allows you to extract bulk data, 200k records, for data synchronisation purposes. This prevents the need for you to fire multiple API calls which can easily drain your daily credits.

This Bulk Read is asynchronous, thus the result for your request will not be available immediately. So, you will have to either poll the bulk read job status API to know the status or specify a callback URL for Zoho CRM to send notifications regarding the process. 

The CRM system processes your request and returns the results in either a CSV or ICS (for Meetings/Events module) zipped file. To download your result, use the job ID given in the response of bulk read job API .

This entire process can be wrapped into three stages as given below, 
  • Creating a Bulk Read Job 
  • Checking the status of a Bulk Read Job
  • Downloading the CSV/ICS data zip

Create a Bulk Read Job

1. Adding Callback URL

To get notified of the progress and results of the bulk read job, add a callback URL to the request body using the following format. 
$requestWrapper = new RequestWrapper();

$callbackVar = new CallBack();

$callbackVar->setUrl(" your_callback_url ");

$callbackVar->setMethod(new Choice("post"));

$requestWrapper->setCallback($callbackVar);
The method for callback URL is typically set to POST as it helps the client to send the required data along with the url to handle the callback appropriately.

2. Structuring a Query

A query comprises multiple parameters such as your desired module, fields, custom view ID, page value, file-type and criteria.

A criterion is typically a filter/condition used in a query to narrow down the data to your desired set of records. Criteria can include one or more conditions and you can combine them using group operators like 'and' and 'or' to create more complex filtering logic. 

Each condition in the criteria comprises three components.
  • Field - It refers to the field API name of the record, which you want to use for filtering purpose.
  • Comparator - It determines how the value of the specified field has to be evaluated.
  • Value - It represents the value of the field against which the record has to be evaluated.
This helps you to retrieve only the relevant records, thereby optimising the performance and reducing the amount of data transferred.

To enhance the understanding of how to structure a criterion, it is necessary to address these group operators and comparators before delving into it.

Group Operators 

Group Operators are used to perform logical operations on multiple conditions within a query. They help you in grouping multiple conditions together to create complex search criteria.The possible group operators are 'and' and 'or'.
  • and - It specifies that all conditions within the group must be satisfied for a record to be included in the query result.
  • or - It specifies that any of the conditions within the group can be satisfied for a record to be included in the query result.
These operators provide flexibility in constructing queries and retrieve the desired set of records from your CRM system with ease.

Comparators

Comparators are used in a query condition to specify the type of comparison that should be performed between a field and a value. They define the relationship between the field and the value and determine how the records are filtered. Refer to this help document to find the possible comparators with their field datatypes. 

An example of each datatype is given in PHP for your better understanding. 
Data Type
Example
  Number

$conditionVar = new Criteria();
$field = new MinifiedFields();
$field ->setAPIName("Amount");
$conditionVar>setField($field);
$conditionVar->setComparator(new Choice("greater_than"));
$conditionVar->setValue(10000);

  Multi-select 

$conditionVar = new Criteria();
$field = new MinifiedFields();
$field ->setAPIName("owner");
$conditionVar>setField($field);
$conditionVar->setComparator(new Choice("in"));
$owner = ["5545974393001", "5545974393011"]
$conditionVar->setValue($owner);

  DateTime                                   

$conditionVar = new Criteria();
$field = new MinifiedFields();
$field->setAPIName("Created_Time");
$conditionVar>setField($field);
$conditionVar->setComparator(new Choice("between"));
$createdTime = [date_create("2023-05-01T17:58:47+05:30")->setTimezone(new \DateTimeZone(date_default_timezone_get())), date_create("2023-06-01T17:58:47+05:30")->setTimezone(new \DateTimeZone(date_default_timezone_get()))];
$conditionVar->setValue($createdTime);

  Boolean

$conditionVar = new Criteria();
$field = new MinifiedFields();
$field ->setAPIName("Email_Opt_Out");
$conditionVar>setField($field);
$conditionVar->setComparator(new Choice("equal"));
$conditionVar->setValue(false);

  Lookup

$conditionVar = new Criteria();
$field = new MinifiedFields();
$field ->setAPIName("owner.last_name");
$conditionVar>setField($field);
$conditionVar->setComparator(new Choice("equal"));
$conditionVar->setValue("Boyle");

Structuring Simple Criteria

Below is a query format with a simple criterion of two conditions to filter your records from the given module. 

$criteriaVar = new Criteria();

$criteriaVar->setGroupOperator(new Choice("operatorValue"));

$conditionVar1 = new Criteria();

$fieldVar = new MinifiedFields();

$fieldVar->setAPIName("Field_API_Name");

$conditionVar1->setField($fieldVar);

$conditionVar1->setComparator(new Choice("comparatorValue"));

$conditionVar1->setValue("Field_Value");

$conditionVar2 = new Criteria();

$field = new MinifiedFields();

$field->setAPIName("Field_API_Name");

$conditionVar2->setField($fieldVar);

$conditionVar2->setComparator(new Choice("comparatorValue"));

$conditionVar2->setValue("Field_Value");

$criteriaVar->setGroup([$conditionVar1,$conditionVar2]);

$query->setCriteria($criteriaVar);

$requestWrapper->setQuery($query);

In the above format, the group operator is used for two different conditions.

Following is the json format of this simple criteria.
{
    "query": {
        "criteria": {
            "group_operator": "operatorValue",
            "group": [
                {
                    "comparator": "comparatorValue",
                    "field": {
                        "api_name": "Field_API_Name"
                    },
                    "value": "Field_Value"
                },
                {
                    "comparator": "comparatorValue",
                    "field": {
                        "api_name": "Field_API_Name"
                    },
                    "value": "Field_Value"
                }
            ]
        }
    }
}

Structuring Complex Criteria

To structure a criterion with four different conditions follow the below format.

//top-level group criteria with 2 different groups comprising of 2 different conditions each

$criteria = new Criteria();

$criteria->setGroupOperator(new Choice("operatorValue"));

//group1 with 2 different conditions

$groupVar1 = new Criteria();

$groupVar1->setGroupOperator(new Choice("operatorValue"));

$conditionVar11 = new Criteria();

$field11 = new MinifiedFields();

$field11->setAPIName("Field_API_Name");

$conditionVar11->setField($field11);

$conditionVar11->setComparator(new Choice("comparatorValue"));

$conditionVar11->setValue("Value");

$conditionVar12 = new Criteria();

$field12 = new MinifiedFields();

$field12->setAPIName("Field_API_Name");

$conditionVar12->setField($field21);

$conditionVar12->setComparator(new Choice("comparatorValue"));

$conditionVar12->setValue("Value");

$groupVar1->setGroup([$conditionVar11, $conditionVar12]);

//group2 with 2 different conditions

$groupVar2 = new Criteria();

$groupVar2->setGroupOperator(new Choice("operatorValue"));

$conditionVar21 = new Criteria();

$field21 = new MinifiedFields();

$field21->setAPIName("Field_API_Name");

$conditionVar21->setField($field21);

$conditionVar21->setComparator(new Choice("comparatorValue"));

$conditionVar21->setValue("Value");

$conditionVar22 = new Criteria();

$field22 = new MinifiedFields();

$field22->setAPIName("Field_API_Name");

$conditionVar22->setField($field22);

$conditionVar22->setComparator(new Choice("comparatorValue"));

$conditionVar22->setValue("Value");

$groupVar2->setGroup([$conditionVar21, $conditionVar22]);

//pushing the groups to the top-level group criteria

$criteria->setGroup([$groupVar1, $groupVar2]);

In the above format, two group variables ($groupVar1 and $groupVar2) are created, each representing a nested group of conditions. Both the groups are further assigned to the top-level group variable ($criteria). 

The json arrangement of the above format is given for your clarity.
{
    "query": {
        "criteria": {
            "group_operator": "operatorValue",
            "group": [
                {
                    "group_operator": "operatorValue",
                    "group": [
                        {
                            "comparator": "comparatorValue",
                            "field": {
                                "api_name": "Field_API_Name"
                            },
                            "value": "Field_Value"
                        },
                        {
                            "comparator": "comparatorValue",
                            "field": {
                                "api_name": "Field_API_Name"
                            },
                            "value": "Field_Value"
                        }
                    ]
                },
                {
                    "group_operator": "operatorValue",
                    "group": [
                        {
                            "comparator": "comparatorValue",
                            "field": {
                                "api_name": "Field_API_Name"
                            },
                            "value": "Field_Value"
                        },
                        {
                            "comparator": "comparatorValue",
                            "field": {
                                "api_name": "Field_API_Name"
                            },
                            "value": "Field_Value"
                        }
                    ]
                }
            ]
        }
     }
}

The default file type is CSV. To bulk read the Events module always prefer the ICS file type. Refer to this resource to fetch the custom view IDs, module and field API names. 

Below is a sample code for creating a bulk read job with compound criteria. The criteria follow this arrangement ((1 and 2) or (3 or 4)) of conditions.

<?php

use com\zoho\crm\api\bulkread\BulkReadOperations;
use com\zoho\crm\api\bulkread\CallBack;
use com\zoho\crm\api\util\Choice;
use com\zoho\crm\api\bulkread\Query;
use com\zoho\crm\api\bulkread\Criteria;
use com\zoho\crm\api\bulkread\RequestWrapper;
use com\zoho\crm\api\modules\MinifiedModule;
use com\zoho\crm\api\fields\MinifiedFields;
require_once "vendor/autoload.php";

class CreateBulkReadJob
{
public static function initialize()
    {
        // Add initialisation code
        // Refer to this article for more help
    }
    public static function createBulkReadJob(string $moduleAPIName)
    {
        $bulkReadOperations = new BulkReadOperations();
        $requestWrapper = new RequestWrapper();
        $callback = new CallBack();
        $callback->setUrl("https://www.callback.com/example");
        $callback->setMethod(new Choice("post"));
        $requestWrapper->setCallback($callback);

        //query
        $query = new Query();
        $module = new MinifiedModule();
        $module->setAPIName($moduleAPIName);
        $query->setModule($module);
        $query->setCvid("34770610087501");
        $query->setFields(["Last_Name"]);
        $query->setPage(1);

        //top-level group criteria with 2 different groups comprising of 2 different conditions each
        $criteria = new Criteria();
        $criteria->setGroupOperator(new Choice("or"));
        $criteriaList = array();

        //group1 with 2 different conditions
        $groupVar1 = new Criteria();
        $groupVar1->setGroupOperator(new Choice("and"));

        $conditionVar11 = new Criteria();
        $field11= new MinifiedFields();
        $field11->setAPIName("Last_Name");
        $conditionVar11->setField($field11);
        $conditionVar11->setComparator(new Choice("equal"));
        $conditionVar11->setValue("Boyle");
        
        $conditionVar12 = new Criteria();
        $field12 = new MinifiedFields();
        $field->setAPIName("Owner");
        $conditionVar12->setField($field12);
        $conditionVar12->setComparator(new Choice("in"));
        $owner = array("5545974000000393001","5545974000000393011");
        $conditionVar12->setValue($owner);
        $groupVar1->setGroup([$conditionVar11, $conditionVar12]);

         //group2 with 2 different conditions
        $groupVar2 = new Criteria();
        $groupVar2->setGroupOperator(new Choice("or"));

        $conditionVar21 = new Criteria();
        $field21 = new MinifiedFields();
        $field21->setAPIName("Company");
        $conditionVar21->setField($field21);
        $conditionVar21->setComparator(new Choice("equal"));
        $conditionVar21->setValue("Morlong Associates");
        
        $conditionVar22 = new Criteria();
        $field22= new MinifiedFields();
        $field22->setAPIName("Created_Time");
        $conditionVar22->setField($field22);
        $conditionVar22->setComparator(new Choice("between"));
        $createdTime = array(date_create("2023-04-15T17:58:47+05:30")->setTimezone(new \DateTimeZone(date_default_timezone_get())), date_create("2023-06-01T17:58:47+05:30")->setTimezone(new \DateTimeZone(date_default_timezone_get())));
        $conditionVar22->setValue($createdTime);
        $groupVar2->setGroup([$conditionVar21, $conditionVar22]);

         //pushing the groups to the top-level group criteria
        $criteria->setGroup($criteriaList);
        $query->setCriteria($criteria);
        $requestWrapper->setQuery($query);
        // $requestWrapper->setFileType(new Choice("ics")); for Events module
        $response = $bulkReadOperations->createBulkReadJob($requestWrapper);
        //Add your code to handle the response received in $response
        // For more details, refer here.
    }
}
CreateBulkReadJob::initialize();
$moduleAPIName = "Leads";
CreateBulkReadJob::createBulkReadJob($moduleAPIName);
?>

2. Status of a Bulk Read Job

To keep track of the status of your bulk read job, use the id provided in the details section of your bulk read job response. 

Here is a sample code for checking the status of a bulk read job.

<?php

use com\zoho\crm\api\bulkread\BulkReadOperations;

require_once "vendor/autoload.php";

class GetBulkReadJobDetails
{
public static function initialize()        
    {
        // Add initialisation code
        // Refer to this article for more help 
    }

    public static function getBulkReadJobDetails(string $jobId)
    {
        $bulkReadOperations = new BulkReadOperations();
        $response = $bulkReadOperations->getBulkReadJobDetails($jobId);
        //Add your code to handle the response received in $response
        // For more details, refer here.
    }
}
GetBulkReadJobDetails::initialize();
$jobId = "55459743108003";
GetBulkReadJobDetails::getBulkReadJobDetails($jobId);
?>

3. Download Bulk Read Result

To download your bulk read job result, use the job id mentioned in the bulk read job response. Additionally, provide the path of the destination folder where you would like to download the result. The result will be downloaded as a zip file containing CSV/ICS files.
Here is a sample code for downloading a bulk read job result.

<?php

namespace samples\bulkread;

use com\zoho\crm\api\bulkread\BulkReadOperations;

require_once "vendor/autoload.php";

class DownloadResult
{
public static function initialize()
    {
        // Add initialisation code
        // Refer to this article for more help  
    }
    public static function downloadResult(string $jobId, string $destinationFolder)
    {
        $bulkReadOperations = new BulkReadOperations();
        $response = $bulkReadOperations->downloadResult($jobId);
        $streamWrapper = $response->getObject()->getFile();
        fputs(fopen($destinationFolder . "/" . $streamWrapper->getName(), "w"), $streamWrapper->getStream());
        //Add your code to handle the response received in $response
        // For more details, refer here.
    }
}
DownloadResult::initialize();
$jobId = "55459743108003";
$destinationFolder = "/Downloads";
DownloadResult::downloadResult($jobId, $destinationFolder);
?>

We hope you found this post useful and engaging! 

If you have any queries, feel free to drop them in the comments section below or reach out to us directly at support@zohocrm.com. We value your thoughts and eagerly look forward to hearing from you.

Stay tuned for more enriching posts coming your way soon!

Cheers!

    • Recent Topics

    • Spam is Being Forwarded

      I am filtering a certain sender directly to the Trash folder. Those messages are still being forwarded. Is this supposed to happen?
    • IMAP Block

      My two accounts have been blocked and I am not able to unblocked them myself. Please respond to email, I am traveling and this is urgent.
    • "DKIM not configured"

      Hello. I have been attempting get the DKIM verified but Toolkit keeps sending the message that it is not configured, but both Namecheap and Zoho show it as configured properly. What am I missing?
    • Zoho mail with custom domain not receiving email

      i registered zoho mail with my own domain. I can login and access the mail app. I tried to send email from an outlook email account and an icloud email account. Both emails were not received. My plan is free. I also tried to send email from this zoho
    • Set connection link name from variable in invokeurl

      Hi, guys. How to set in parameter "connection" a variable, instead of a string. connectionLinkName = manager.get('connectionLinkName').toString(); response = invokeurl [ url :"https://www.googleapis.com/calendar/v3/freeBusy" type :POST parameters:requestParams.toString()
    • Waterfall Chart

      Hello, I would like to create a waterfall chart on Zoho analytics which shows the movement in changes of budget throughout a few months, on a weekly basis. Should look something like the picture below. Does anyone know how to?
    • Help Center and SEO: Any Benefit to My Domain-Mapped Website Ranking?

      First of, I love the Help Center which I've just decided to integrate into my website to replace its old-fashioned FAQs. So much more to achieve there now! Lots of new benefits to the site visitors and to me in terms of organizing and delivering all the
    • Issue with Importing Notes against Leads

      Hi, I am attempting to import some Notes into Zoho CRM which need to be assigned to various Leads. I have created a csv file which I am testing that contains just one record. This csv file contains the following columns: Note Id Parent Id       (I have
    • Trigger a Workflow Function if an Attachment (Related List) has been added

      Hello, I have a Case Module with a related list which is Attachment. I want to trigger a workflow if I added an attachment. I've seen some topics about this in zoho community that was posted few months ago and based on the answers, there is no trigger
    • Option to Hide Project Overview for Client Profiles

      In Zoho Projects, the Project Overview section is currently visible to client profiles by default. While user profiles already have the option to restrict or hide access to the project overview, the same flexibility isn’t available for client profiles.
    • Creator Add Records through API - Workflows not triggered ?

      Hi Everyone, I am trying to add records to my Creator application through a third party app that I am developing. Currently, I am testing this through Postman. The records I need to add have a lot of workflows to run to populate dropdowns, fields, use
    • Important Update: Changes to Google Translate Support in Zoho SalesIQ

      We’re updating our default chat translation options across all Zoho SalesIQ data centres to offer a more secure, in-house solution for your translation needs. What’s changing? We will be offering Zoho Translate (our in-house tool) as the default translation
    • Zoho CRM Community Digest - July P1 | 2025

      Hey everyone, The start of July 2025 marked a special milestone: 200 posts in our Kaizen series! For those new here, Kaizen is a go-to series for Zoho CRM developers, where we regularly share best practices, tips, and expert insights to help you build
    • What’s New in Zoho Expense (April – July 2025)

      Hello users, We're happy to bring you the latest updates and enhancements we've made to Zoho Expense over the past three months, which include introducing the Trip Expense Summary report in Analytics, extending Chatbot support to more editions, rolling
    • Weekly Tips: Insert frequently used phrases in a jiffy using Hotkeys

      You often find yourself using similar phrases in an email —like confirming appointments or providing standard information. Constantly switching between the mouse and keyboard interrupts your flow and slows you down.Instead of typing the same phrases over
    • Undo Command in Notebook

      It would be handy to have an Undo in the row of icons at the bottom of the notes.
    • Improved UI for a Seamless User Experience - Calls, Tasks, and Meetings

      Hello all, We are making UI unification across CRM so that the UI experience is seamless across the product. As part of that effort, we have made changes to the details page of activity-based module records—Meetings, Calls, and Tasks. Let's look at these
    • New Customization options in the module builder: Quick Create and Detail view

      Hello everyone, We have introduced two new components to the module builder: Quick create and Detail view. The Quick Create Component It is a mini form used to create a record and associate it to the parent record from a lookup field. For example, if you have a Deals lookup in the Contacts module, then you can associate existing deals or create a deal and associate it with the contact. You can customize this Quick Create form by adding standard as well as custom fields. There is no limit to the number
    • Narrative 9: GC—Meaningful conversations that benefit your customers

      Behind the scenes of a successful ticketing system - BTS Series Narrative 9: GC—Meaningful conversations that benefit your customers Customers often seek immediate solutions, which is why self-service options are essential. Guided Conversations provides
    • FSM App Oddity

      We recently rolled out FSM to our technicians, and only one technician is having an issue. He's using an iPhone with iOS 18.6 installed. When he goes to service appointments using the calendar icon at the bottom of the app, he gets a list view only. Typically,
    • Cliq Not Working !

      Zoho Cliq has been experiencing connectivity issues since this morning. The app is unable to establish a connection with the server.
    • Injecting CSS into ZML pages

      Is there a way to inject CSS into ZML pages? Use case: 1. Dashboard displays 'Recent Activities' card displaying a list of newly added records 2. Each item in list links to the record onClick 3. When a user points the cursor over an item in the list,
    • Power of Automation :: Automated Weekly Notifications for Unapproved Timesheets

      Hello Everyone, A custom function is a software code that can be used to automate a process and this allows you to automate a notification, call a webhook, or perform logic immediately after a workflow rule is triggered. This feature helps to automate
    • Extracting Data from Sitelink APIs

      Hi All, I'm working to pull data on Sitelink using API calls but i can't seem to extract it. If you click expand, you will see it has a lot of data in it. I tried extracting from the map but it's not giving me anything. Is there a workaround on this or
    • Export a list of fields for all modules in a spreadsheet with specific field data

      Many of my clients are using spreadsheets to create lists of fields for all modules when starting a new implementation or when updating an existing setup. This is a useful process but also a very time consuming one. It would be good a list of fields could
    • [Important announcement] Zoho Writer will mandate DKIM configuration for automation users

      Hi all, Effective Dec. 31, 2024, configuring DKIM for From addresses will be mandatory to send emails via Zoho Writer. DKIM configuration allows recipient email servers to identify your emails as valid and not spam. Emails sent from domains without DKIM
    • 【Zoho Backstage】2025年7月のアップデート紹介

      本投稿は、本社のZoho Desk コミュニティに投稿された以下の記事を翻訳し、一部抜粋したものです。 What's New - July 2025 | Zoho Backstage ユーザーの皆さま、こんにちは。コミュニティチームの中野です。 本投稿では、Zoho Backstage の直近のアップデートを3点抜粋して、ご紹介します。 目次 1.参加可否の確認:RSVP 2.証明書作成ツール:Certificate Builder 3.登録の承認 :Registration Approval 1.参加可否の確認:RSVP
    • Option to select location?

      As a business coach, I meet with clients at various public locations. I have two or three pre-determined locations that I meet at. I would like the client to choose the location when booking an appointment. Is there a way to do that with a single service, or is the best way to accomplish this by creating one service for each location offered?
    • Can we have Backorder Management ?

      Can we have Backorder Management ?
    • Display a field of an Account on the Ticket view

      Hi there, We would like to display of the Account of the user submitting a ticket on the ticket view. See for example: How can this be achieved? It doesn't really matter where it will be placed as long as it is shown there. Thanks, Snir
    • Two factor authentication for helpdesk users

      The company i work for wants use the helpdesk site in Zoho desk, as a place for their distribution partners to ask question and look for information about our product. The things there is suppose to go up there is somewhat confidential between my company
    • Remove "Invalid entries found. Rectify and submit again" modal

      Following up on a post from a few years back, but can the Zoho team consider either removing the 'Invalid entries found. Rectify and submit again' modal that displays for empty mandatory fields OR allow an admin to change it? I've built a custom error
    • Insurance Agencies

      I am reaching out to see if anyone has any experience in the Insurance Agency world with using Zoho exclusively for the CRM and commissions side of things. Lots of strong features like drip campaigns, meeting, calendars, emails, can all be found in here.
    • Problem with Egyptian internet ISP

      Dears,  We have problem with our ADSL internet SP, That your site not working with. So, we contact them and they asking us to contact you to solve this problem.  The problem that when we are connecting to Tedata ADSL your website not working (Business Email).  BR,, Mohamed Omar 
    • Emails not being received from a particular domain

      Cannot receive any emails sent from atco.com Domain is in the spam whitelist so should be no reason for it not to be coming through. Have filed a ticket and besides a generic response of we are looking at it - it seems there is no actual support workers
    • Mail Not Showing In Inbox or Sent Box

      Hi, there are mails that are not displaying in both my inbox and sent box. I just tried the iPad app and it is the same but there is a blank field where a mail should be and it refers to a server error. Please fix this.
    • Marketing Automation List Entry Criteria is no longer an option

      For a couple of years now we have used the "List Entry Criteria" option to filter our Journey recipient list. All of a suddent the option no longer exists for New Lists and I can only delete the option from existing lists but can no longer edit it. Anyone
    • GCLID arrives not in CRM with iframe integrated ZOHO Form

      Hello amazing community, I enabled Adwords integration in ZOHO CRM. I have a ZOHO Form integrated in a wordpress. I tested iframe and Javascript. I enabled the "handover" GCLID in the ZOHO Form. When I add a GLID with http://www.example.com/?gclid=TeSter-123.
    • Add Webhook Response Module to Zoho Flow

      Hi Zoho Flow Team, We’d like to request a Webhook Response capability for Zoho Flow that can return a dynamic, computed reply to the original webhook caller after / during the flow runs. What exists today Zoho Flow’s webhook trigger can send custom acknowledgements
    • Bidirectional sync between Zoho Bookings and Zoho CRM - Part 1

      Hey, community members! The default integration between Zoho CRM and Zoho Bookings is unidirectional, which means that any appointments booked, rescheduled, or deleted in Zoho Bookings will be reflected in Zoho CRM. However, any modifications made to
    • Next Page