Customer Support via Twilio SMS with Zendesk v2

Customer Support via Twilio SMS with Zendesk v2

Recently I was challenged to integrate Twilio SMS channel via Zendesk. If you're not familiar with Zendesk, it's a helpdesk system that allows customers to create tickets by sending email which support staff can respond to and create certain workflows. More about Zendesk here.

For SMS part I decided to use Twilio since I am already familiar with their API. More about Twilio here.

As a starting point I took interesting blog post about Customer Support via SMS with Zendesk which was written for now deprecated Zendesk API v1.

In this tutorial I will explain how to do similar thing (and more) with Zendesk API v2.

Inside Zendesk you will have to create two custom ticket fields:

  • Hash
  • Phone number

Then on your server create config.php file and set your credentials here.

<?php 
//Zendesk credentials
define("ZD_SITE", 'yourzendesksite');
define("ZD_APIKEY", 'yourzendeskapikey');
define("ZD_USER", '[email protected]');

//Twilio credentials
define("TWILIO_SID", 'yourtwiliosid');
define("TWILIO_TOKEN", 'yourtwiliotoken');
define("TWILIO_NUMBER", 'yourtwilionumber');
?>

Next is inbound.php script. Within Twilio, we set the SMS URL for one of our phone numbers to the URL where our inbound.php lives. Then we can use the incoming From phone number to look up any active tickets from this user.

Note that you will need modified Zendesk PHP library, which I originally forked from https://github.com/jrenouard/Zendesk-API

<?php 
  $zendesk = new Zendesk(ZD_APIKEY, ZD_USER, ZD_SITE);
  $data =  $zendesk->call("/search.json?query=status<solved++order_by:created_at++sort:desc++fieldvalue:".md5($sender)."++fieldvalue:" . $sender, $create, "GET");
?>

Unfortunately new Zendesk API doesn't support query by custom ticket fields but instead lets you search all fields by using fieldvalue. Therefore searching for phone number is not enough as it might return false positive (for example if source number is mentioned somewhere in the ticket body). This is where we use our 2nd custom field called "hash". This field will be contain MD5 hash of the source number which is calculated by our inbound.php script when the ticket was created. This will ensure that indeed we got tickets only for this source number.

Next, if we don't have an Open ticket from that phone number, we create one using the SMS Body as the body of the ticket and the From in a custom field "Phone number" we've created.

<?php 
  $sender = $_REQUEST['From'];
  $sms = $_REQUEST['Body'];
    $create = json_encode(
      array(
        'ticket' =>
          array(
            'subject' => substr($sms, 0, 80),
            'description' => $sms,
            'requester' => array(
              'name' => $sender,
              'email' => $sender . '@twilio.com'
            ),
            'tags' => array('sms'),
            'fields' => array('FIELDID_OF_PHONE_NUMBER' => $sender, 'FIELDID_OF_HASH' => md5($sender)),
          )
      )
    );
    $data = $zendesk->call("/tickets.json", $create, "POST");
?>

Another restriction of Zendesk API here is that it doesn't allow users without email, so we have to generate fake one using source phone number @ twilio.com

However in the case if we have Open ticket from that phone number, we create new comment on behalf of the user instead of creating new ticket.

<?php 
if ($data->results) {
    $ticket_id = (string) $data->results[0]->id;
    //Use the end-user's email address, not an agent's email address as is the case with all other API end-points.
    $user_email = $sender . '@twilio.com';
    $zendesk = new Zendesk(ZD_APIKEY, $user_email, ZD_SITE);
    $update = json_encode(
      array(
        'request' =>
          array(
            'comment' => array(
              'body' => $sms,
            )
          )
      ));
    $data = $zendesk->call("/requests/" . $ticket_id . ".json", $update, "PUT");
  }
?>

In order to create comment on behalf of user in the Zendesk you will have to pass $user_email to Zendesk API, instead of admin email as in other requests. However this keep throwing {"error":"Couldn't authenticate you"}. After searching on Zendesk forums for similar issues I found out that this API call requires user email to be verified. Since our users are created via script we have to explicitly verify them using:

<?php 
  $requester_id = $data->results[0]->requester_id;
  $update_user = json_encode(
    array(
      'user' =>
        array(
          'verified' => TRUE,
        )
   ));
  $data = $zendesk->call("/users/" . $requester_id . ".json", $update_user, "PUT");
?>

That wraps the inbound (SMS to support) side of the process, but we still have to configure the outbound (support to SMS) processing. Within Zendesk, we have to configure what is called a Target and a Trigger: To create the Target, visit Settings->Extensions->Targets and click “add target”:

  1. Give the Target a useful name such as “Twilio Notification”;
  2. Fill URL box with: http://yoursite.com/outbound.php?To={{ticket.ticket_field_CCCC}}&Body={{ticket.latest_public_comment}} Use your Zendesk Custom field ID for "Phone number" field to fill CCCC
  3. Click save

To create the Trigger, visit Manage->Triggers & mail notifications and click “add trigger”:

  1. Create new trigger "Notify requester of comment update via SMS"
  2. Under Meet all of the following conditions add:
    • Ticket is Updated
    • Ticket comment is present and requester can see the comment
    • Ticket requester is not current user
    • Ticket tags contains at least one of the following: sms
  3. Under Perform these actions:
    • Notifications: Notify target -> Twilio notification
    • Message: ticket updated

As last part now we need to create outbound.php which will send SMS to user once support updates ticket status or adds new comment. For this we will need Twilio PHP API

<?php 
require_once("Services/Twilio.php");
require_once("config.php");

if (isset($_REQUEST['To']) && isset($_REQUEST['Body'])) {
  $client = new Services_Twilio(TWILIO_SID, TWILIO_TOKEN);
  $message = $client->account->messages->sendMessage(
    TWILIO_NUMBER, // From a valid Twilio number
    $_REQUEST['To'], // Text this number
    $_REQUEST['Body']
  );
}
?>

And thats it, now you should have Zendesk support via SMS channel!

Whenever someone sends an SMS to your Twilio number, a new ticket will be created. Your customer service team can respond as needed (Twilio supports messages up to 1600 characters!) which is delivered to the person via SMS. Then they can respond and the support thread stays connected throughout. The best part is that the customer service team doesn’t have to do anything differently.

Some ideas for improvement:

1. Zendesk Search API seems to cache the search dataset for 5-10 mins. So if you tried to immediately reply to newly created ticket, it might happen that search API still haven't picked it up in their results so the script can't see any open tickets and therefore creates new one instead of posting a comment reply to existing one. Perfectly we should be keeping temporarly mysql table with list of ticket ids by number for 5-10mins until we are sure the tickets is in search dataset.

2. Use the source number to get more information about user from CRM and possibly merge with existing user instead of creating fake [email protected] user

3. Add security check to inbound/outbound.php scripts so that we are sure requests are coming from Twilio and Zendesk respectively.

4. Add support for other providers like Tropo, Plivo, etc...

Full code coming soon on my github.

You can contact me for paid customizations and setup of your Zendesk with any SMS provider.