Integrate Twilio Flex and Calendly

Team analyzing the benefits of integrating Twilio Flex and Calendly
October 13, 2022
Written by
Reviewed by
Paul Kamp
Twilion

In this article, I'll demonstrate how to integrate Calendly with Twilio Flex. This integration will allow you to allow customers to schedule callbacks from your call center agents by using Calendly’s API V2 within Flex. The resulting union of the two is an effective tool for improving the experience of your contact center agents and building enduring relationships with customers.

This tutorial will teach you:

  • A way for clients to schedule callbacks from contact center representatives using the appointment scheduling service in Calendly

If you want to jump ahead and grab the full working code for this, visit the github repositories for this project

Let's get started!

Solution: Integrating Calendly’s V2 API with Flex

This integration will allow you to automate the ability for your contact center agents to call back customers. Before an integration of this kind, an event was added to either the agent's calendar or a shared calendar whenever a customer scheduled a callback, implying a manual step in which the agent checks to see which calls have been scheduled before bringing that information back into the contact center application to initialize it. This is a limited solution that depends on the skill and availability of the agent. The Calendly API V2 upgrade has made it possible to integrate Twilio Flex with the strength of Calendly's scheduling features.

The solution has the following components:

  • A Calendly webhook subscription setup to trigger any time an event is scheduled.
  • A Twilio Function used to ingest and parse webhooks.This will also create an agent task, which is passed to our Flex Agent Desktop.
  • A Flex plugin that renders the event information for the agent. 

Image representing the flow of an incoming event webhook. The data received from Calendly can be used to give the agent more context to better serve the customer.

Prerequisites

You will need:

Get Started

Create the event type

First, sign into your Calendly account and, from the dashboard, create a new One-on-One event type.

Screen Pop showing how to create new one-on-one event in Calendly

Enter the details required for your event. Here are some example details I used, but feel free to change them:

  • Event name - Calendly x TwilioFlex Demo
  • Location - Phone call
  • Description/Instructions - Book a quick chat with our friendly team to discuss how we can help unlock the power of customer engagement. We look forward to speaking with you. Twilio
  • Event link - calendly-x-twilioflex-demo
  • Event color - Red

You can also see the input below for an example:

Screen Pop showing how to insert event details in Calendly

Set up the timing for your event. Let’s assume 15 minutes is long enough for your agents to wrap up with each customer. 

 

Screen Pop showing how to setup event timing details in Calendly

Utilize these additional choices to include some additional inquiries about their call-in. Select the Invitee Questions option and add in a few questions. I used the inputs below but feel free to use whatever you like:

  • Question 1  
    • Question - Are you a new or existing Twilio user?
    • Answer Type - Radio Buttons
    • Answers - New; Existing
    • Required - Yes
  • Question 2
    • Question - What Twilio products do you currently use?
    • Answer Type - Checkboxes
    • Answers - Twilio Flex; Twilio Engage; Twilio Conversations; None of the above
    • Required - No

Screen Pop showing addition questions created for event in Calendly

Now that your event type has been created, you can share it with your clients. Remember to click Save & Close once you're finished.

Create Webhook Subscription

To have Calendly notify Twilio of any new events that are scheduled, you must configure a webhook subscription in your account.

Get your personal access token for the Calendly API first by following these instructions. This is crucial because as we go along, we'll be making a few web requests to Calendly that require authentication using this token.

Next, you need to grab your organization and user URI from Calendly by following these instructions.

Using the organization and user URIs you collected, make another GET request to Calendly using Postman, this time for the URI of the event type we just created.

curl --request GET \
  --url 'https://api.calendly.com/scheduled_events?user={userURI}&organization={oganizationURI}' \
  --header 'Authorization: Bearer {myAuthToken}' \
  --header 'Content-Type: application/json'

Don’t forget to replace the placeholders for {myAuthToken}{organizationURI} and {userURI} with the values collected from the previous steps.

The Calendly response will be a collection of event types created by your user. Go through this list and retrieve your created event type URI. It should look like the example below  

Calendly API response from retrieving event types created by user.

Finally, you need to create a publicly addressable function where the Calendly webhooks will be sent. Going back to your Twilio Console, go here and create a service named Calendly. Next, add a new function named webhookHandler. Once that is done,  get the functions URL by clicking on the 3 dots beside it and selecting the copy URL option

Copy URL of function location in console

You now have all the prerequisites to set up the webhook subscription. Going back to Postman, you want make to following request:

curl --request POST \
  --url https://api.calendly.com/webhook_subscriptions \
  --header 'Authorization: Bearer {myAuthToken} \
  --header 'Content-Type: application/json' \
  --data '{
  "url": "https://calendlyxfelx-xxxx.twil.io/webhookHandler",
  "events": [
    "invitee.created"
  ],
  "organization": "https://api.calendly.com/organizations/AAAAAAAAAAAAAAAA",
  "user": "https://api.calendly.com/users/BBBBBBBBBBBBBBBB",
  "scope": "user"
}'

Once this is successful you get a 201 response with the body looking like this:

{
    "resource": {
        "callback_url": "https://calendlyxflex-xxxx.twil.io/webhookHandler",
        "created_at": "2022-09-20T15:27:23.714478Z",
        "creator": "https://api.calendly.com/users/BBBBBBBBBBBBBBBB",
        "events": [
            "invitee.created"
        ],
        "organization": "https://api.calendly.com/organizations/AAAAAAAAAAAAAAA",
        "retry_started_at": null,
        "scope": "user",
        "state": "active",
        "updated_at": "2022-09-20T15:27:23.714478Z",
        "uri": "https://api.calendly.com/webhook_subscriptions/CCCCCCCCCCCCCCCC",
        "user": "https://api.calendly.com/users/BBBBBBBBBBBBBBBBBBBBB"
    }
}

Awesome! Every time an event is scheduled, you will get a webhook sent to your function. 

At this point, you may be wondering if you will get all the webhooks for all the events scheduled that don’t necessarily relate to this integration coming into the function. The answer is yes. Calendly webhook subscriptions are at a user level, so any other event types you have that are actively being used will trigger webhook calls. Later on, you’ll be making sure to only deal with our specific event type using the URI obtained earlier.

Add functionality to our function

Only a few values are needed from the webhook payload. These values are going to be used to give as much information as needed to the agent when the task gets to them. Take a look at an example of a webhook below, within the payload section:

{
 "created_at": "2020-11-23T17:51:19.000000Z",
 "created_by": "https://api.calendly.com/users/AAAAAAAAAAAAAAAA",
 "event": "invitee.created",
 "payload": {
   "cancel_url": "https://calendly.com/cancellations/AAAAAAAAAAAAAAAA",
   "created_at": "2020-11-23T17:51:18.327602Z",
   "email": "test@example.com",
   "event": "https://api.calendly.com/scheduled_events/AAAAAAAAAAAAAAAA",
   "name": "John Doe",
   "new_invitee": null,
   "old_invitee": null,
   "questions_and_answers": [],
   "reschedule_url": "https://calendly.com/reschedulings/AAAAAAAAAAAAAAAA",
   "rescheduled": false,
   "status": "active",
   "text_reminder_number": null,
   "timezone": "America/New_York",
   "tracking": {
     "utm_campaign": null,
     "utm_source": null,
     "utm_medium": null,
     "utm_content": null,
     "utm_term": null,
     "salesforce_uuid": null
   },
   "updated_at": "2020-11-23T17:51:18.341657Z",
   "uri": "https://api.calendly.com/scheduled_events/AAAAAAAAAAAAAAAA/invitees/AAAAAAAAAAAAAAAA"
,
   "canceled": false
 }
}

The values you’ll be extracting from the payload are name, email, event, and questions_and_answers.

The payload does not give you the event specific information such as start time, the customer number to be called, or the event type that this is created for. It does give you the URI for the event, so we can get the information from there. This means you are going to have to make one more call to the API. This time you’ll make the request from inside the function.

Axios is going to be used in the function to make requests, so you need to add a dependency for it and add the following required line in our function.

const axios = require('axios');

And add in the dependencies section Module axios with version latest. The modules you see there for lodash,  xmldom, and util are all added in by default and no need to modify them. You can see an illustration below.

npm dependencies in console

Set and get Environment Variables

Next, you will add in your environment variables. As a refresher, here are what they are:

  • AUTH_TOKEN_1 and AUTH_TOKEN_2 - The split (more on that in a second) of the Calendly auth token.
  • TWILIO_TASKROUTER_WORKSPACE_SID -Your Taskrouter Workspace SID, which you can retreive from the Taskrouter workspace in the console.
  • CALENDLY_EVENT_TYPE_URI - Your event type's URI, which you retreived in the Create Webhook Subscription section, above.

Environmental variables in console

It may be unclear to you why the Calendly auth token is associated with two environment variables.

The Calendly auth token that we earlier retrieved is lengthy—it has about 263 characters. This goes over the 255-character maximum for the number of characters that can be stored in our environment variables, so it’s best to roughly split it into two parts and concatenate them into one inside the function code. Alternatively you can follow the instructions here to store and retrieve the token as a private asset.

Remove the rest of the initial boilerplate code from the webhookHandler function and add in the following:

exports.handler = async function(context, event, callback) {
// Create webhook response
const response = new Twilio.Response();

// Calendly Auth token
const authToken = context.AUTH_TOKEN_1 + context.AUTH_TOKEN_2;

// Extract Calendly event webhook payload from Twilio Event body
const eventWebhookPayload = event.payload;

// Setup the parameters for the API call
var options = {
    method: 'GET',
    url: eventWebhookPayload.event,
    headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${authToken}`      
    }
};

// Get request to the event URI
const getEventInformation = await axios.request(options);
// Store event information in the variable
const eventInformation= getEventInformation.data.resource;

}

That initial code allows you to make a GET request to the Calendly API using the event URI in the eventWebhookPayload.event field.

Limit processing to certain event types

Now that you have the event information, you need to make sure that you are only processing webhooks for this specific event type relating to this integration by doing a comparison with the event type URI you collected and stored in your environment variables. If it matches, then the task for the agent can be created using a combination of the information collected. See the code below to do this. Add this code right after the line with the event information.

// Check if this event is for the event type we are working with
if (eventInformation.event_type === context.CALENDLY_EVENT_TYPE_URI){
    
const taskRouterClient = context.getTwilioClient().taskrouter.workspaces(context.TWILIO_TASKROUTER_WORKSPACE_SID);
  
   //Create new event in task router
   await taskRouterClient.tasks.create({
     attributes: JSON.stringify({
       callerName: eventWebhookPayload.name,
       callerEmail: eventWebhookPayload.email,
       startTime: eventInformation.start_time,
       name: eventInformation.location.location,
       type: 'calendly',
       questions: {...eventWebhookPayload.questions_and_answers}
     })
   });
  
   response.setStatusCode(200);

   return callback(null, response);

}
else {
 response.setStatusCode(200);
 return callback("Error: This is not our event type", response);
}

You are done with your function, and now every time an event is created, you’ll get a new task initiated in our workspace ready to be picked up by an agent.

Save and deploy, ready for the next step.

Environment variables and dependencies in Functions

Twilio Flex plugin and Testing

You are going to need a plugin added to your Flex instance to allow your agents to see the details of the call back you've passed along on the screen.

You can grab the plugin from this repo here. Run this plugin locally by opening a terminal or command line window, navigating to the plugin-calendly-attributes folder, and running the following commands:

# Install Dependencies
npm install

# Start Flex Plugins
twilio flex:plugins:start

To test that it all works with the local instance of the plugin up and running, create a new Calendly meeting from the event link you created. Once you’re done filling out the form, head back to your local Flex instance, make your agent available, and you should see a new task available on screen like in the examples below:

Screen pop showing when a callback task has been added

Screen pop when task has been assigned to agent and callback button appears.

Beautiful!

After testing the plugin locally, you can deploy the plugin to your account using the Flex Plugins CLI.

Now that the agent has all the necessary information, they can call the client back from their Flex desktop and provide a wonderful customer experience.

So what comes next?

Congrats! Your callbacks have been automated using Twilio Flex and Calendly. This solution can be enhanced in a variety of ways. You could take advantage of Calendly webhook signatures to enhance the security of the data coming into Twilio. Use Twilio's Task Router, a skill-based routing system, to match tasks to the appropriate agents and make sure the right agents are returning the customer's calls. You can learn more about using Taskrouter with Twilio Flex here.

Can you think of any more? We can’t wait to see what you build.

Want to see Twilio Flex in action? Check out our interactive demo.

Ready to start building your contact center? You can start for free today! Sign up and start building with Twilio Flex!

Somto Eluwa is a Senior Solutions Consultant at Twilio. He enjoys speaking with customers and finding ways for them to get the most value out of Twilio. He can be reached at seluwa[at]twilio.com or on LinkedIn.