The base concept is very simple – run a campaign inviting prospective customers to text their email address to find out more about your product, receive some freebie, whatever. It turns out that the execution is almost as simple as the concept. Today I’ll walk you an Apex class that does exactly that, in less than 100 lines. (Here’s the code in its entirety).

First, I define the class, with a @RestResource annotation. This is a RESTful web service that will be deployed at the relative URL /smstolead

@RestResource(urlMapping='/smstolead')
global class SmsToLead {

I’ve already installed the Twilio library and configured a custom setting with my Twilio account credentials, so I can get a reference to my Twilio account by just calling TwilioAPI.getDefaultAccount():

static TwilioAccount account = TwilioAPI.getDefaultAccount();

Now I define a method that will accept an HTTP POST from Twilio:

@HttpPost
global static void incomingSMS() {

I’ll be sending an email, so I reserve capacity up front to do so. This ensures that, if I’ve already reached my daily email limit, an error will be thrown before any more processing is done.

// This will error out with System.LimitException if we would exceed
// our daily email limit
Messaging.reserveSingleEmailCapacity(1);

Having done that, since this is a web service deployed at a public endpoint, it’s important to verify that this POST really did come from Twilio. I do that by gathering the signature passed in via the X-Twilio-Signature header, the requested URL, and request parameters, and passing them to the validateRequest() method in the TwilioRestClient class. validateRequest() calculates an HMAC of the request data, using my Twilio auth token as the key (the auth token is a secret that I share with Twilio), and comparing it to the signature sent in the request. If the calculated HMAC matches the signature from the header, I can be confident that this request came from Twilio and it was not modified in transit. I’m setting the response body in the sample to aid debugging, but, in production, you would return the 403 status with no body.

I’ve added a custom field to the Campaign object so I can automatically associate the incoming SMS with a marketing campaign I’m running. Here I use the phone number from the SMS to locate the Campaign record. If there is no matching campaign, I send an SMS reply with an error (see below for the reply method). You would only expect to see this error in early testing, if a Campaign record had not been created, or was configured with the wrong number.

Now I have the Campaign, I insert a Lead. Note that Leads are not directly associated with a Campaign – I’ll need to create a CampaignMember record later. I set the Lead’s LastName and Company to ‘From SMS’, since these are required fields but I don’t have real data to put there. In a real marketing campaign, these would likely be updated later on as the sales team engages with the lead.

I don’t bother doing any validation on the email address from the SMS, since the platform will do that for me when I insert it, throwing a DmlException if the value in the Email field does not appear to be a valid email address. Note that well-formatted but non-existent email addresses will be accepted, so you might want to use a service such as StrikeIron’s Email Verification in a real production setting.

Let’s take a look at the reply() method that I define further down in the SmsToLead class. I need that @future annotation since sending an SMS invokes a callout to Twilio, and I can’t execute a synchronous callout, since I’ve made changes to the database that are not yet committed. @future solves the problem by making calls to reply() asynchronous – they go on a queue and are executed shortly afterward.

With all that done, all that remains is to send the texter an email. In a real campaign, I’d likely include a link, allowing the user to opt in to the campaign and provide more of their details. Here I send a simple acknowledgment. Using setTargetObjectId(), rather than setToAddresses(), allows the platform to record this email as an activity against the Lead. This is the default behavior when you specify a Lead, Contact or User Id this way, but I call setSaveAsActivity() to make it explicit in the code.

With the Twilio library installed and configured, and the SmsToLead class in place, you’ll need to configure a site and follow the instructions here to set up a publicly accessible endpoint of the form https://somesite.force.com/services/apexrest/smstolead. Create a phone number at Twilio, and set your new endpoint as its SMS Request URL:
You should be able to text your email address to the Twilio number, and see a new Lead in your org, complete with an email address, phone number, an email in its activity history and a campaign listed:
So, there you have it – SMS-to-Lead. How are you acquiring leads in your org? Share your experience in the comments…