Transcribing Voice Messages to Tasks with the Twilio Library for Salesforce

This week our friends at Twilio will be hosting TwilioCon, and we'll be there as an event sponsor. Twilio last year released their Helper Library for Salesforce, giving developers an easy way to work with their Voice and SMS APIs. Here's an example showing how to 'take a message' and post the transcription to Salesforce.

This week (Tuesday September 17 – Thursday September 19 2013), our friends at Twilio will be hosting TwilioCon – their annual cloud communications extravaganza. We’ll be there as an event sponsor, which prompted me to look back at my past integrations of Twilio with, and come up with a new one.

Twilio released their Helper Library for Salesforce back in February 2012, giving developers an easy way to work with their Voice and SMS APIs. Here’s an excerpt of the sample code I posted back then for sending an SMS from via the Twilio API:

TwilioAccount account = TwilioAPI.getDefaultAccount();
String myPhoneNumber = 
Map<String, String> params = new Map<String, String>{
    'From' => myPhoneNumber,
    'To' => phoneNumber,
    'Body' => message

TwilioSms sms = account.getSmsMessages().create(params);


Pretty straightforward! The more complex integrations demonstrate some nice features of – receiving and responding to SMS messages uses a public RESTful web service, while automatic call routing shows how Visualforce can generate XML just as easily as HTML.

This time round, I’m going to use Twilio’s Transcription feature to record a voice message, transcribe it to text, look up a contact based on the caller ID and create a Task with the message transcription.

Here’s how I got this working…

The first step was to create a simple TwiML file in Visualforce; this acts as the script when a user dials my Twilio number:

<?xml version="1.0" encoding="UTF-8" ?>
<apex:page sidebar="false" showHeader="false" contentType="application/xml">
        <Say>Hello! Please leave a message.</Say>
        <Record maxLength="120" transcribe="true" 


When the call connects, my app says “Hello! Please leave a message”, beeps, and the caller has two minutes of message time (this is the maximum message length if you want the message transcribed). When the call completes, the incoming caller ID and a transcription of the call is sent (via an HTML form post) to the given transcribeCallback URL.

The next step was creating an endpoint on to receive message transcriptions. First, I defined an Apex class as a REST endpoint. I’ll walk through it step by step here; you can also grab the complete Apex class, along with the Visualforce page.

Let’s start with the class definition:

global class MessageToTask {


That gives me an endpoint at /services/apexrest/messagetotask on a Site. Next I need to define a method that will handle the HTTP POST. Here is the signature, with the relevant annotation:

global static void incomingMessage() {


I verify that the message really came from Twilio, and was not tampered with in transit, via the TwilioClient.validateRequest() method (the signature validation process is described in more detail here).

String expectedSignature = 
String url = 'https://' + RestContext.request.headers.get('Host') + 
    '/services/apexrest' + RestContext.request.requestURI;
Map <String, String> params = RestContext.request.params;

// Validate signature
if (!TwilioAPI.getDefaultClient().validateRequest(expectedSignature, url, params)) {
    RestContext.response.statusCode = 403;
    RestContext.response.responseBody = Blob.valueOf('Failure! Rcvd '+expectedSignature+'nURL '+url);


Conveniently, the incoming form is parsed by the platform, so I just need to pull the relevant fields from the incoming params (RestContext.request.params).

// Extract useful fields from the incoming transcription
String leadNumber     = params.get('Caller');
String campaignNumber = params.get('To');
String transcription  = params.get('TranscriptionText');


The next step is to locate a Lead from this phone number, creating a new Lead if we don’t find an existing one:

// Look for a matching lead
List<Lead> leads = [SELECT Id 
                    FROM Lead 
                    WHERE (Phone = :leadNumber OR MobilePhone = :leadNumber) 
                    LIMIT 1];
Lead lead;
if (leads.size() > 0) {
    // We found a match!
    lead = leads[0];
} else {
    // Create and insert a new Lead
    lead = new Lead(LastName = 'From Transcription',
        Company = 'From Transcription',
        Phone = leadNumber);

    try {
        insert lead;
    } catch (DmlException dmle) {
        reply(campaignNumber, leadNumber, 'An error occurred. Sorry.');


I’m just matching on the main and mobile phone numbers for Leads here, but you could match on additional fields, match against Contacts, custom objects, whatever you like.

With a Lead ID in hand, it’s a snap to populate a Task record with the relevant data and insert it in the database:

// Create and insert a new Task
Task task = new Task(
    ActivityDate =,
    CallDisposition = 'Contact left a message',
    CallType = 'Inbound',
    Description = result.transcription + 
                'nnMessage recording at'+params.get('AccountSid')+'/Recordings/'+params.get('RecordingSid'),
    Status = 'Completed',
    Subject = 'Phone Message from '+phone,
    Type = 'Call',
    WhoId = contact.Id
insert task;

UPDATE: I added a link to the message recording at Twilio – very useful if the caller didn’t speak clearly and the transcription doesn’t make sense!

And that’s it – all done, in less than 40 lines of Apex.The last step was to create a Site, and make the MessageToTask endpoint publicly visible, as described in detail here. That gave me a publicly accessible endpoint with a URL of the form, which I set as the Voice Request URL in the Twilio developer console. A couple of quick calls confirmed that all was working as expected!

Here are a couple of messages logged against a Lead:

And here’s the detail of one of those:

Now that the Task is saved against the Lead, you’d likely want some Workflow or a Trigger to assign the Lead to a Queue for attention. I’ll leave that as an exercise for the reader 🙂

If you are at TwilioCon this week, stop by the Salesforce booth and you can try this out, ask questions about anything Salesforce-related, or just hang out with the Developer Relations team – we’d love to see you!