Build a Twilio + Zoho CRM SMS Integration

December 03, 2025
Written by
Reviewed by

Build a Twilio + Zoho CRM SMS Integration

Are you using Zoho CRM's webhook and workflow rules? If not, you're missing out on some powerful features. Zoho CRM helps you build lasting relationships by allowing you to respond to your customers when key events happen. For example, you can notify customers via SMS when scheduling a new meeting, creating a new campaign, or closing a deal.

Does that sound like something your customers would love?

When you're finished with this tutorial, your PHP-based applications will be able to notify meeting participants by SMS when they're involved in a new meeting, or when details of an existing meeting change.

Building the application can feel a little involved. However, once complete, I’m sure you'll be surprised at how much power you have available.

Prerequisites

Before you begin, make sure you have the following:

  • A Zoho CRM Professional account, or sign up for the 15-day free trial
  • A Twilio account. Sign up for free today if you don't have an account.
  • PHP 8.3 or above
  • Composer installed globally
  • Your preferred code editor or IDE
  • Some terminal experience is helpful, though not required
  • A phone that can receive SMS and an active phone number

Application overview

When complete, your application should work, as laid out in this activity diagram:.

Diagram illustrating the sequence of actions between Zoho CRM and a PHP app for retrieving event and participant details.

What does this diagram show? Once your application is complete, when your next a meeting is created or updated, here’s what will happen:

  1. Zoho CRM will send a webhook request to the PHP application containing details of the meeting, including the meeting owner and venue
  2. The PHP application will retrieve an OAuth2 access token using a Client Credentials grant for accessing Zoho CRM’s API
  3. If an access token is returned, the PHP app will make a series of requests to Zoho CRM’s API to retrieve details about the event, event organiser, and event participants
  4. With that information, the PHP app will use Twilio's Programmable Messaging API to notify the event's participants about the new or updated meeting that they are required to attend

Now you know how the application works, let’s build it.

Set up the project directory

The first thing that we need to do is to set up the project's directory structure by running the following commands:

mkdir zoho-twilio-integration-sms
cd zoho-twilio-integration-sms
mkdir -p public src/Entity/SearchResponse src/Provider src/Service
If you're using Microsoft Windows, you can drop the "-p" argument as Windows doesn't require it.

I'll explain the purpose of each directory throughout the tutorial.

Install the required packages

We need a number of packages to create the application. These are:

To install the packages, run the following command:

composer require \
    asad/oauth2-zoho \
    guzzlehttp/guzzle \
    guzzlehttp/psr7 \
    league/oauth2-client \
    mrbenosborne/json-unmarshal \
    php-di/slim-bridge \
    slim/psr7 \
    slim/slim \
    twilio/sdk \
    vlucas/phpdotenv

Then, add the following to composer.json to set a default namespace for the app:

"autoload": {
    "psr-4": {
        "App\\": "src/"
    }
},

Set the required environment variables

The application will use environment variables to keep configuration settings and sensitive data out of the code, storing them in .env via PHP Dotenv.

In the project's top-level directory, create a file named .env with the following command.

touch .env

Then, paste the following into it:

TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_PHONE_NUMBER=""
ZOHO_CLIENT_ID=
ZOHO_CLIENT_SECRET=
ZOHOCRM_URI=
ZOHOCRM_DC=
ZOHO_SCOPE="ZohoCRM.modules.contacts.READ,ZohoCRM.modules.events.READ"
ZOHO_SOID=

After that, set the value of ZOHOCRM_DC to the data center assigned to your account, and the value of ZOHOCRM_URI to the base URI most applicable to you, followed by "/crm/v8". As an example, I live in Australia, so I'd set ZOHOCRM_DC to "AU" and ZOHOCRM_URI to "https://www.zohoapis.com.au/crm/v8".

If you're not sure which data center is assigned to your account, open your account profile. Then, click the profile icon in the top right-hand corner. In the pop-out that appears, you'll see which data center your account is in.

Retrieve your Twilio credentials and phone number

You’ll need to retrieve your Twilio credentials so the app can authenticate with Twilio's Programmable Messaging API for sending SMS notifications, and also retrieve your Twilio phone number, so Twilio knows who's sending the SMS.

You can find this information in the Twilio Console:

  1. Log in to your Twilio Console
  2. From the Account Info panel, copy your Account SID, Auth Token, and phone number
The Account Info panel of the Twilio Console's Account Dashboard. The Account SID and phone number values have been redacted.
The Account Info panel of the Twilio Console's Account Dashboard. The Account SID and phone number values have been redacted.
  • Paste this information into your .env file as the values for TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_PHONE_NUMBER, respectively
  • Retrieve your Zoho SOID

    Now you need to retrieve your Zoho SOID. This is your Zoho organisation's unique ID, required for generating an access token. To retrieve it:

    1. Log in to the Zoho CRM Dashboard
    2. Click the profile icon near the bottom left-hand side (or upper right-hand side) of the navigation bar underneath the Settings icon
    3. Click the down arrow next to your organisation's name, and copy the Org ID that appears
The user details section of the Zoho CRM dashboard, with the organisation popup visible; the actual organisation's id has been redacted after the first three characters.
The user details section of the Zoho CRM dashboard, with the organisation popup visible; the actual organisation's id has been redacted after the first three characters.
  • Set your organisation's id as the value of ZOHO_SOID in your .env file
  • Generate an API Key and Secret

    Next, let’s generate your Zoho CRM API key and secret. These are required to retrieve an access token for accessing the Zoho CRM API. To do this:

    1. Make sure you’re still logged in to your Zoho CRM account
    2. Navigate to: Settings (Setup) > Developer Hub > APIs and SDKs > SDKs > "Server Side SDKs"
    3. There, click "Register New Client" to create a new client for accessing the Zoho CRM API
The SDKs page of the Zoho CRM Setting section. The image only has the Server Side SDKs  section, with the remainder of the page having been removed.
The SDKs page of the Zoho CRM Setting section. The image only has the Server Side SDKs  section, with the remainder of the page having been removed.
  • Enter your password to verify your identity
  • Click "GET STARTED"
The "Welcome to API Console!" form in Zoho CRM.
The "Welcome to API Console!" form in Zoho CRM.
  • Under "Choose a Client Type", click "CREATE NOW" under the "Self Client" option
The Zoho CRM API's New Client page with only the Self Client option visible. The remainder of the page has been cropped out.
The Zoho CRM API's New Client page with only the Self Client option visible. The remainder of the page has been cropped out.
  • Under "Create New Client" click "CREATE"
The Zoho CRM Create New Client dialog/window, with Client Type set to Self client.
The Zoho CRM Create New Client dialog/window, with Client Type set to Self client.
  • Click "OK" in the "Are you sure to enable self-client?" confirmation popup
  • Copy the Client ID and Client Secret
The Self Client popup in Zoho CRM on the Client Secret tab. The Client ID and Client Secret values have been redacted.
The Self Client popup in Zoho CRM on the Client Secret tab. The Client ID and Client Secret values have been redacted.
  • Set them as the values of ZOHO_CLIENT_ID and ZOHO_CLIENT_SECRET, respectively, in your .env. file
  • Create the PHP application

    Now, it's time to create the PHP application. Start by creating a file named index.php in the public directory. Paste the following code into the file:

<?php

declare(strict_types=1);

use App\Application;
use App\Service\ZohoCrmService;
use Asad\OAuth2\Client\Provider\Zoho;
use DI\Container;
use Dotenv\Dotenv;
use GuzzleHttp\Client;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Monolog\Handler\StreamHandler;
use Monolog\Level;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
use Twilio\Rest\Client as TwilioRestClient;

require __DIR__ . '/../vendor/autoload.php';

$dotenv = Dotenv::createImmutable(__DIR__ . '/../');
$dotenv->load();
$dotenv->required([
    'PUBLIC_URL',
    'TWILIO_ACCOUNT_SID',
    'TWILIO_AUTH_TOKEN',
    'TWILIO_PHONE_NUMBER',
    'ZOHOCRM_DC',
    'ZOHOCRM_URI',
    'ZOHO_CLIENT_ID',
    'ZOHO_CLIENT_SECRET',
    'ZOHO_SCOPE',
    'ZOHO_SOID',
])->notEmpty();

$provider = new Zoho([
    'clientId'     => $_ENV['ZOHO_CLIENT_ID'],
    'clientSecret' => $_ENV['ZOHO_CLIENT_SECRET'],
    'redirectUri'  => '',
    'dc'           => $_ENV['ZOHOCRM_DC'],
]);

$logger = (new Logger('name'))->pushHandler(
    new StreamHandler(
        __DIR__ . "/../app.log",
        Level::Debug
    )
);
try {
    $logger->debug("Attempting to retrieve access token.", [
        'scope' => $_ENV['ZOHO_SCOPE'],
        'soid'  => 'ZohoCRM.' . $_ENV['ZOHO_SOID'],
    ]);
    $accessToken = $provider->getAccessToken(
        'client_credentials',
        [
            'scope' => $_ENV['ZOHO_SCOPE'],
            'soid'  => 'ZohoCRM.' . $_ENV['ZOHO_SOID'],
        ]
    );
} catch (IdentityProviderException $e) {
    exit("Could not retrieve access token. Reason: " . $e->getMessage());
}

$client = new Client(
    [
        'base_uri' => $_ENV['ZOHOCRM_URI'],
        'debug'    => false,
        'headers'  => [
            "Authorization" => sprintf("Zoho-oauthtoken %s", $accessToken),
        ],
        'timeout'  => 2.0,
    ]
);

$twilioRestClient = new TwilioRestClient(
    $_ENV["TWILIO_ACCOUNT_SID"],
    $_ENV["TWILIO_AUTH_TOKEN"]
);

$container = new Container();
$container->set(LoggerInterface::class, function () use ($logger) {
    return $logger;
});
$container->set(Zoho::class, fn () => $provider);
$container->set(Client::class, fn () => $client);
$container->set(TwilioRestClient::class, fn () => $twilioRestClient);
$container->set(ZohoCrmService::class, fn () => new ZohoCrmService($client, $twilioRestClient, []));
$application = new Application(
    $container,
    [
        "TWILIO_PHONE_NUMBER" => $_ENV["TWILIO_PHONE_NUMBER"],
        "PUBLIC_URL"          => $_ENV["PUBLIC_URL"],
    ]
);
$application->setupRoutes();

$application->run();
For the above code to work, ensure that the value of variables_order in your PHP configuration contains "E", such as "EGPCS". Otherwise, PHP will not populate the $_ENV superglobal.

This is the bootstrap file where all requests will be sent. Here’s what this bootstrap files does:

  • In the beginning, two class constants are defined:
  • The first is the base URI for version 8 of Zoho CRM's API
  • The second is the scope for the OAuth2 access token

According to the Zoho CRM documentation, a scope:

...limits the level of access given to a client for protected resources. It enables a user to provide delegated access to a client. Zoho CRM's APIs use selected scopes, which control the type of resource that the client application can access.

Thus, this scope will allow us to have read access to our contacts and events, nothing more. This makes the requests more discrete and secure.

  • Next, using PHP Dotenv, the code loads the required environment variables from .env, which were set earlier. An exception will be thrown if one or more of them is not set or empty. The code then initialises a number of variables (most of which will be services with the application's DI container:
  • The first is a Zoho OAuth 2.0 client provider, which is used to retrieve an access token
  • The second is a Guzzle client, which will be used to make requests to Zoho CRM's API to retrieve the meeting information
  • The third is a Twilio Rest Client, which simplifies requests made to Twilio's APIs
  • Then a new PHP-DI container instance is initialised, which is the application's DI or service container, and each of the objects are registered as services in the container, along with a ZohoCrmService object.
  • Finally, a new Application object is initialised with the container object and an array containing your Twilio phone number. This object encapsulates the core of the app's functionality. The object's setupRoutes() function is called to initialise the application's routing table, followed by run() to boot the application, where it will listen for incoming requests.

Create a class to simplify running the core web application

Let's create the Application class now that the PHP application is ready. In the src directory, create a file named Application.php.Paste the following code into the file:.

<?php

declare(strict_types=1);

namespace App;

use App\Entity\SearchResponse\Event;
use App\Service\ZohoCrmService;
use DateTimeImmutable;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
use Slim\Factory\AppFactory;
use Twilio\Exceptions\RestException;
use Twilio\Rest\Client as TwilioRestClient;

use function json_encode;
use function sprintf;

final class Application
{
    private App $app;

    public function __construct(
        private readonly ContainerInterface $container,
        private readonly array $options,
    ) {
        AppFactory::setContainer($container);
        $this->app = AppFactory::createFromContainer($container);
    }

    public function setupRoutes(): void
    {
        $this->app->post('/', [$this, 'handleDefaultRoute']);
    }

    public function handleDefaultRoute(
        ServerRequestInterface $request,
        ResponseInterface $response
    ): ResponseInterface {
        $logger = $this->container->get(LoggerInterface::class);
        $logger->debug('Webhook request data', (array) $request->getParsedBody());
        $meetingCreator = $request->getParsedBody()['Meeting_Creator'];
        $meetingVenue   = $request->getParsedBody()['Meeting_Location'];
        $zohoCrmService = $this->app
            ->getContainer()
            ->get(ZohoCrmService::class);
        $meeting = $zohoCrmService->getEventDetails($meetingCreator, $meetingVenue);
        $result  = $this->notifyMeetingParticipants($meeting);
        $response->getBody()->write(json_encode($result));
        return $response;
    }

    public function notifyMeetingParticipants(Event $event): array
    {
        $smsStatus = [];
        foreach ($event->participants as $participant) {
            $msgBody = <<<EOF
            You've been requested to attend a meeting (%s) at %s, starting at %s. 
            The meeting organiser is %s. 
            Email them at %s for more information.
            EOF;
            /** @var TwilioRestClient $twilioRestClient */
            $twilioRestClient = $this->app->getContainer()->get(TwilioRestClient::class);
            try {
                $message = $twilioRestClient->messages->create(
                    $participant->contactDetails->phone,
                    [
                        "body" => sprintf(
                            $msgBody,
                            $event->title,
                            $event->venue,
                            (new DateTimeImmutable($event->startsAt))->format("r"),
                            $event->organiser->name,
                            $event->organiser->email
                        ),
                        "from" => $this->options["TWILIO_PHONE_NUMBER"],
                    ]
                );
                $smsStatus[$participant->participant] = $message->status;
            } catch (RestException $e) {
                $smsStatus[$participant->participant] = $e->getMessage();
            }
        }
        return $smsStatus;
    }

    public function run(): void
    {
        $this->app->run();
    }
}

Here’s what this code does:

  • The class encapsulates the core of the app's functionality, which avoids having a large or bloated public/index.php. I find this approach to be more manageable and testable.
  • The class' constructor takes a ContainerInterface instance, $container, (which we initialised earlier in public/index.php) and a set of configuration options. It then initialises a Slim App object, setting $container as its container.
  • The setupRoutes() function defines a sole route that accepts only POST requests to / and is handled by the handleDefaultRoute() function
  • The handleDefaultRoute() function starts by retrieving the ZohoCrmService service from the DI container, then calls its getEventDetails() function to retrieve an event's details from Zoho CRM's API. The JSON in the response is marshalled as an Event object. We'll cover this in more depth shortly.
  • The event to be retrieved is determined by the Meeting_Creator and Meeting_Location form variables sent by Zoho CRM in the webhook request to the route

Once this code executes, the meeting participants are notified about the upcoming meeting via SMS. Here’s an example of an SMS sent to meeting participants:

You've been requested to attend a meeting (Let's Talk about Turtle Conservation) at Mon Repos Turtle Centre, Bundaberg, starting on Wed, 12 Nov 2025 11:30:00 +1000.
The meeting organiser is Matthew Setter. 
Email them at msetter@invalid.example for more information.

Finally, notifyMeetingParticipants() function returns an array of each of the event's participants and if they were successfully notified.

Create a service class to simplify interacting with Zoho CRM

Let’s create a new file named ZohoCrmService.php under src/Service. In that file, paste the following code:

<?php

declare(strict_types=1);

namespace App\Service;

use App\Entity\SearchResponse\Contact;
use App\Entity\SearchResponse\Event;
use App\Entity\SearchResponse\EventParticipant;
use GuzzleHttp\ClientInterface;
use JSON\Unmarshal;
use Twilio\Rest\Client as TwilioRestClient;

use function json_decode;
use function rawurlencode;
use function sprintf;

final class ZohoCrmService
{
    public function __construct(
        private readonly ClientInterface $httpClient,
        private readonly TwilioRestClient $twilioClient,
        private readonly array $options,
    ) {
    }

    public function getEventDetails(string $creator, string $venue): Event
    {
        $response = $this->httpClient->request(
            'GET',
            'Events/search',
            [
                'query' => 'criteria=' . sprintf(
                    '((Created_By:equals:%s)and(Venue:starts_with:%s))',
                    rawurlencode($creator),
                    rawurlencode($venue),
                ),
            ]
        );
        $body     = $response->getBody();
        $jsonData = json_decode($body->getContents(), true);
        $event    = new Event();
        Unmarshal::decode($event, $jsonData['data'][0]);
        foreach ($event->participants as &$participant) {
            $participant->contactDetails = $this->getContactFromEventParticipant($participant);
        }
        return $event;
    }

    private function getContactFromEventParticipant(EventParticipant $participant): Contact
    {
        $response = $this->httpClient->request(
            'GET',
            sprintf('Contacts/%s', $participant->participant),
            [
                'query' => 'fields=Phone,Mobile',
            ]
        );
        $jsonData = json_decode($response->getBody()->getContents(), true);
        $contact  = new Contact();
        Unmarshal::decode($contact, $jsonData['data'][0]);
        return $contact;
    }
}

Here’s what this code does:

  • The class' constructor takes a Guzzle client, a Twilio Rest client, and an array of configuration options
  • The getEventDetails() function retrieves an event's details from Zoho CRM, based on its creator and venue location, and marshals them as an Event object. This includes the event's participants and their contact details retrieved by calling getContactFromEventParticipant().

The reason for the information being retrieved in two different API calls is because the entire set of details is not available from a single API call.

Strictly speaking, the event, event participant, and participant contact details don't need to be marshalled from JSON (which is what the Zoho CRM APIs return) to PHP objects. However, I find objects easier to work with than JSON, even if they're a little more verbose, and require a bit more to set up.

Create entities to store the data retrieved from Zoho CRM

Now, let's step through all of the entities into which the JSON data returned from the API calls is marshalled.

First, in src/Entity/SearchResponse, create four files:

  • Contact.php
  • Event.php
  • EventOrganiser.php
  • EventParticipant.php

Next, in src/Entity/SearchResponse/Contact.php, paste the following code:

<?php

declare(strict_types=1);

namespace App\Entity\SearchResponse;

use JSON\Attributes\JSON;

class Contact
{
    #[JSON(field: 'id')]
    public string $id;

    #[JSON(field: 'Phone')]
    public string $phone;

    #[JSON(field: 'Mobile')]
    public string $mobile;
}

This class stores a contact's phone and mobile phone number. These are required so the meeting participants can be contacted via SMS. The JSON attribute tells JSON Unmarshal which field in the provided JSON data to marshal (or hydrate) the field with. In the case of $phone, that field will be marshalled with the Phone field.

In src/Entity/SearchResponse/Event.php, paste the following code:

<?php

declare(strict_types=1);

namespace App\Entity\SearchResponse;

use JSON\Attributes\JSON;

class Event
{
    #[JSON(field: 'Venue')]
    public string $venue;

    #[JSON(field: 'Event_Title')]
    public string $title;

    #[JSON(field: 'Start_DateTime')]
    public string $startsAt;

    #[JSON(field: 'Owner', type: EventOrganiser::class)]
    public EventOrganiser $organiser;

    #[JSON(field: 'Participants', type: EventParticipant::class)]
    public array $participants;
}

This class stores the top-level details of an event, along with an EventOrganiser object. This object contains the details of the event's organiser, and an array of EventParticipant's, storing the details of each event participant (or meeting attendee).

Check out the CRM Platform Modules & Fields documentation for a full list of the fields for each module.

In src/Entity/SearchResponse/EventOrganiser.php, paste the following code:

<?php

declare(strict_types=1);

namespace App\Entity\SearchResponse;

use JSON\Attributes\JSON;

class EventOrganiser
{
    #[JSON(field: 'id')]
    public string $id;

    #[JSON(field: 'email')]
    public string $email;

    #[JSON(field: 'name')]
    public string $name;
}

This class stores the essential details (full name and email address) of the event organiser.

In src/Entity/SearchResponse/EventParticipant.php, paste the following code:

<?php

declare(strict_types=1);

namespace App\Entity\SearchResponse;

use JSON\Attributes\JSON;

class EventParticipant
{
    #[JSON(field: 'id')]
    public string $id;

    #[JSON(field: 'participant')]
    public string $participant;

    #[JSON(field: 'name')]
    public string $name;

    #[JSON(field: 'Email')]
    public string $email;

    public Contact $contactDetails;
}

This class stores the details of an event participant (including full name and email address) as well as their contact details in $contactDetails. This information is retrieved from a separate API call.

Now that we've finished the application, here's what the file and directory structure should look like.

./
├── composer.json
├── composer.lock
├── public
│   └── index.php
└── src
    ├── Application.php
    ├── Entity
    │   └── SearchResponse
    │       ├── Contact.php
    │       ├── Event.php
    │       ├── EventOrganiser.php
    │       └── EventParticipant.php
    └── Service
        └── ZohoCrmService.php

The code's available on GitHub, if you'd like to compare it to yours.

Start the application

Now that the application is built, let’s expose it to the public internet. Start the PHP built-in webserver by running the following command:

php -S 127.0.0.1:8080 -t public

Expose it to the public internet with ngrok, by running the following command:

ngrok http 8080
I've used ngrok in the above example, but feel free to use an equivalent tool if you prefer.

After ngrok starts, it should print output to the terminal, similar to this:

A screenshot of grok running in a terminal on macOS with the Forwarding URL highlighted.
A screenshot of grok running in a terminal on macOS with the Forwarding URL highlighted.

Make a copy of the Forwarding URL, as you'll need it shortly.

Create a webhook and associate it with a workflow

With the application started, let’s set up a webhook. There’s a lot of steps involved, so make sure you’re following correctly.

  1. From your Zoho CRM Dashboard, navigate through Setup > Automation > Actions > Webhooks
  2. On the Webhooks page, click Configure Webhook to start creating a webhook:
  3. In the New Webhook page:
  4. Set a value in the Name field
  5. Place the app's publicly accessible URL in the “URL to Notify” field. This is the Forwarding URL printed in the ngrok terminal output in Step 4, C above.
A screenshot showing how to create a webhook in Zoho CRM.
A screenshot showing how to create a webhook in Zoho CRM.
  • Set Module to "Meetings"
A screenshot showing how to add module parameters to a webhook in Zoho CRM.
A screenshot showing how to add module parameters to a webhook in Zoho CRM.

6. Under Body set Type to "Form-Data" and add three Module Parameters:

  • For the first parameter, set Parameter Name to "Meeting Title" and Parameter Value to "Title"
  • For the second parameter, set Parameter Name to "Meeting Location" and Parameter Value to "Location"
  • For the third parameter, set Parameter Name to "Meeting Creator" and Parameter Value to "Created By"

7. Click Save

Associate a webhook with a workflow rule

Now you need to associate the webhook to a workflow rule, so that the workflow is triggered when a meeting is created or updated:

  1. Navigate to Setup > Automation > Workflow Rules
  2. In the Workflow Rules page, click Create Rule
A listing of workflow rules in Zoho CRM.
A listing of workflow rules in Zoho CRM.
  • In the Create New Rule dialog, specify workflow rule parameters:
  • Set Module to "Meetings"
  • Set Rule Name to "Meeting Notifications"
A screenshot showing how to create a new workflow rule in Zoho CRM.
A screenshot showing how to create a new workflow rule in Zoho CRM.
  • Then, click Next to continue
  • In Meeting Notifications:
  • Set "Execute this workflow rule based on" to "Record action"
  • Enable "Repeat this workflow whenever a meeting is edited"
  • Change Create to "Create or Edit"
A screenshot showing how to define a webhook in Zoho CRM.
A screenshot showing how to define a webhook in Zoho CRM.
  • Click Next
  • For Condition 1 check "All Meetings" and click “Done”
A screenshot showing how to apply a webhook condition in Zoho CRM.
A screenshot showing how to apply a webhook condition in Zoho CRM.
  • Set Instant Actions to "Webhook"
  • From the list of webhooks that appear, select the webhook that you created earlier and click Associate
A screenshot showing how to associate a webhook with a workflow in Zoho CRM.
A screenshot showing how to associate a webhook with a workflow in Zoho CRM.
  • Click Save

Test that the integration works

Now it’s time to test that your application works and sends an SMS when a meeting is created or updated:

  • Create the meeting in the Zoho CRM console:
  • In the left-hand side navigation bar, click Modules > Activities > Meetings.
  • Click Create Meeting, and fill out the meeting details form:
  • Set Title to "Let's Talk about Turtle Conservation"
  • Set Location to "Mon Repos Turtle Centre"
  • Click Save
A screenshot showing how to create a meeting in Zoho CRM.
A screenshot showing how to create a meeting in Zoho CRM.
  • Scroll down and next to Participants, click "+ Add"
  • Add at least one of the available participants to the meeting. If your Zoho CRM account is preloaded with default data, change the phone number(s) of the participant(s) that you add to the meeting to your phone number that can receive SMS. Otherwise, add a new contact and set their phone number accordingly.
  • Click Done
  • Click Save
A screenshot showing how to add participants to a meeting in Zoho CRM.
A screenshot showing how to add participants to a meeting in Zoho CRM.
  • Now that you have created the meeting, check the ngrok terminal session. You should see a POST request received.
  • You should receive an SMS, letting you know that you are a participant in the meeting that you just created, along with its location and start date and time:
An example of the SMS sent by the application mocked up with the Android operating system.
An example of the SMS sent by the application mocked up with the Android operating system.

Trying building a Twilio + Zoho CRM SMS integration

Now that you have integrated Zoho CRM's webhook and workflow rules functionality with Twilio, you’re able to notify meeting participants by SMS when they're involved in a new meeting or when details of an existing change.

Think of all the ways you can tailor this to your business, such as sending updates to your clients in real time, and creating happy customers because they never miss an appointment. There are endless ways you can build out these experiences for your customers. Try building new ones and increase the engagement with your customers.

What's Next?

If you'd like to learn more about Zoho CRM's or Twilio's APIs, check out these links:

Matthew Setter is (primarily) the PHP, Go, and Rust editor on the Twilio Voices team. He’s also the author of Mezzio Essentials and Deploy with Docker Compose. You can find him at msetter[at]twilio.com. He's also on LinkedIn and GitHub.