Using Twilio Studio and Conversations with SMS

March 19, 2020
Written by

Using Twilio Studio and Twilio Conversations with SMS

Twilio Studio now natively supports the Conversations API via the Incoming Conversation Trigger. Please refer to our guide for connecting Conversations to Studio.

Communication is complicated. No matter what your business is, it’s important to meet people where they’re at and engage with them on the communication platforms they’re already using.

Twilio Conversations enables you to build unified spaces where users on different platforms can communicate with one another. Think of it like a video conference bridge, but for text-based communication.

Twilio Studio is a visual application builder that allows you to write conditional logic for handling messaging flows without writing any code.

In this post you’ll learn to integrate Studio and Conversations with a few steps of configuration and a small amount of code. Doing so unlocks a few use cases, such as:

  •  Access to the status callbacks so you can keep track of message delivery status
  • Sending the entire chat transcript to a human operator

Prerequisites

Introducing a few concepts

Many advanced Twilio messaging features are powered by what’s called a messaging service, and we’ll need one of those here. Messaging Services allow you to specify behavior for groups of numbers at a time. Most importantly for our purposes, they let you decide to wrap end user communication in a Conversation.

Conversations creates a service for you, which is called the Default Conversations Service.   In this post we’ll configure your Twilio phone number to be associated with the Default Conversations Service, and to automatically create new Conversations when a new SMS message is sent to that number. When that Conversation is created, we’ll add a webhook that triggers a Twilio Function so we can add the Studio Flow to the Conversation, via what’s known as a conversation-scoped webhook.

End to end, it looks something like this:

a diagram of how Twilio Studio integrates with Twilio Conversations. The flow goes "Incoming SMS" to Twilio API to Default Conversations Service to "new conversation is auto-created, triggering onConversationAdded webhook" to "Twilio Function creates Conversation-scoped webhook to add Studio Flow in to Conversation" to "Studio Flow, backed by Default Conversations Service."


If you’re confused, don’t worry! Follow along and we’ll explain the whole thing in more detail.

Create a Studio Flow

You’ll need a Studio Flow to test with. You can create a new one.  Feel free to import the Free Puppy Hotline flow from the json below. Or you can use an existing Flow you’ve already created. The content of the Flow doesn’t matter too much for our purposes, as long as it can handle incoming messages so you can have a way to test things out.

Screenshot of a Twilio Studio flow. The first widget is "Send & Wait For Reply" with the text "Thanks for contacting the free puppy hotline! What kind of doggo should we send you?" When a reply is sent, a "Send Message" widget is triggered with the reply text "Thanks! Somebody will be in touch with you shortly."

To import this Flow, copy the code below and follow these instructions for importing Flows:

{
  "description": "A New Flow",
  "states": [
    {
      "name": "Trigger",
      "type": "InitialState",
      "properties": {
        "offset": {
          "x": -180,
          "y": 20
        },
        "flow_url": "https://webhooks.twilio.com/v1/Accounts/AC38649f1f6b47b696dbee5e5ff695a34d/Flows/FWb9b907bbfa3b377748fbf39118638af4"
      },
      "transitions": [
        {
          "event": "incomingMessage",
          "conditions": [],
          "next": "FF0d82672599ea6feee2d8d3886044637d",
          "uuid": "20dd2af6-ffe5-4923-83c3-716068f08757"
        },
        {
          "event": "incomingCall",
          "conditions": [],
          "next": null,
          "uuid": "b3712dc7-6f0c-4f51-bac2-1b8655215082"
        },
        {
          "event": "incomingRequest",
          "conditions": [],
          "next": null,
          "uuid": "4e582359-2655-41cc-a336-7de57cf1d053"
        }
      ],
      "sid": "FF1d188e594fd22d37b8be923ea5059cf0"
    },
    {
      "name": "send_and_reply_1",
      "type": "MessagePrompt",
      "properties": {
        "offset": {
          "x": -180,
          "y": 220
        },
        "body": "Thanks for contacting the free puppy hotline! What kind of doggo should we send you?",
        "from": "{{flow.channel.address}}",
        "timeout": 3600,
        "save_response_as": null,
        "media_url": null,
        "service": "{{trigger.message.InstanceSid}}",
        "channel": "{{trigger.message.ChannelSid}}",
        "attributes": null
      },
      "transitions": [
        {
          "event": "incomingMessage",
          "conditions": [],
          "next": "FF2097b0d2f88bb3eac1e3edcb5efdc276",
          "uuid": "c84b2557-49c9-4243-a873-eb24b9cc57c6"
        },
        {
          "event": "timeout",
          "conditions": [],
          "next": null,
          "uuid": "bace1c80-22de-4f16-bddf-96a45a93760f"
        },
        {
          "event": "deliveryFailure",
          "conditions": [],
          "next": null,
          "uuid": "2723304a-4a85-4e72-a8e1-46e5255c4ca7"
        }
      ],
      "sid": "FF0d82672599ea6feee2d8d3886044637d"
    },
    {
      "name": "send_message_3",
      "type": "Message",
      "properties": {
        "offset": {
          "x": -170,
          "y": 440
        },
        "body": "Thanks! Somebody will be in touch with you shortly.",
        "from": "{{flow.channel.address}}",
        "to": "{{contact.channel.address}}",
        "media_url": null,
        "service": "{{trigger.message.InstanceSid}}",
        "channel": "{{trigger.message.ChannelSid}}",
        "attributes": null
      },
      "transitions": [
        {
          "event": "sent",
          "conditions": [],
          "next": null,
          "uuid": "bffc1477-18ce-4987-9851-dabe908344cb"
        },
        {
          "event": "failed",
          "conditions": [],
          "next": null,
          "uuid": "b7acc5e7-8056-4eac-ac56-ec65b259b6c5"
        }
      ],
      "sid": "FF2097b0d2f88bb3eac1e3edcb5efdc276"
    }
  ]
}

Building with Studio is pretty much the same whether you’re working with Conversations or plain old SMS. The only noticeable differences building Studio Flows for Conversations are:

  • flow.channel.address will be the Conversation SID (CHxxxxxxxxx) instead of the Twilio phone number
  • the Trigger SID will be a Conversation Message SID (IMxxxxxxxxx) instead of an SMS message SID.
  • Media URLs are not supported in Conversations-based Flows

Configuring Conversations for SMS

For each new Conversation created for a new SMS sender, that Conversation must be linked to the appropriate Studio Flow via the Conversation Scoped Webhook Resource.

First, configure Conversations to connect with Messaging Services. Flip the Locked toggle to Unlocked here so that you can handle inbound messages with Conversations. Don’t forget to click “Save” afterwards.

Screenshot of the configuration page for Conversations. There is a toggle called "Messaging Features" that needs to be set to "Unlocked" so that we can handle inbound messages with Conversations.

Configuring Messaging Services

Go to the Messaging Service page in the Twilio console to associate a Twilio number with the Default Conversations Messaging Service. Click on “Default Conversations Service.”

Screenshot of the Messaging Services dashboard. There is a "Default Conversations Service" configuration link which is where we need to navigate to.

On the next screen, click “Add an Existing Number.” If you’ve played with Conversations before, you may already have some numbers configured (you’ll see them under Numbers in the left-hand nav). In that case, you can use the numbers you’ve already added.

Screenshot of a "Number" dialogue prompting the user to associate a Twilio number with the Default Conversations Service.

You’ll see a pop up window with your Twilio numbers. Check the box next to the number you want to add, and then click “Add Selected.” If all goes well, you’ll see a little success message telling you the number was added.

Screenshot of the "Add a Number" dialog. It lists your Twilio numbers, with a button to "Add Selected."

Now we need to set the Messaging Service to automatically create a new Conversation for each new unique sender. From the page you were just on, click “Settings” on the left hand side.

Screenshot of the Programmable SMS dashboard. There is an arrow pointing towards the Settings page on the right, which is where we need to navigate to.

Select the radio button that corresponds to “Autocreate A New Conversation” and click Save.

Screenshot of the settings page for the Default Conversations service. There is a radio button to specify what happens when a new message arrives for a number associated with the service. "Autocreate a new conversation" must be selected here.

Hooking up Conversations with Studio

To dynamically generate the Conversations webhook when a new SMS comes in, let’s write a Twilio Function. You’ll need the SID of the Studio Flow you created earlier which can be found on the Studio Dashboard.

If you haven’t already, make sure you check the box here to enable ACCOUNT_SID and AUTH_TOKEN within Functions. Create a Twilio Function with the following code. Replace the configuration.flowSid placeholder with the one you copied from your Studio Flow:

exports.handler = function(context, event, callback) {
  const client = context.getTwilioClient();

  const conversationSid = event.ConversationSid;

  console.log(`The Conversation Sid is ${conversationSid}`);

  client.conversations
    .conversations(conversationSid)
    .webhooks.create({
      "configuration.flowSid": "replace this with your Studio Flow Sid",
      "configuration.replayAfter": 0,
      target: "studio"
    })
    .then(webhook => {
      console.log(webhook.sid);
      callback(null, "Success");
    })
    .catch(err => {
      console.log(err);
      callback(error);
    });
};

Make sure your Function is configured to use a version of the twilio Node.js helper library that supports Conversations. We used version 3.39.5 here.

Copy the Function’s path since we’ll need it in a minute.

Next we’ll configure the Conversations webhooks. Head back to the Conversations dashboard.

Configure the Conversations Post-Event Webhook for onConversationAdded. This event will fire for every new SMS sender when the Conversation is autocreated.

Paste the path of your Twilio Function into the post-event URL box.  

Screenshot of the Conversations webhook config page. The URL of the Twilio Function we just created needs to go in the "Post-event URL" field. Under "Post-webhooks", "onConversationAdded" needs to be checked.

Test it out by sending a SMS to the phone number you configured earlier. It should trigger your Studio flow. 🎉

What’s even better, all of the messages you exchange in this Conversation get stored together in a single transcript that you can keep. See the section below on fetching a Twilio Conversations transcript.

Note that a new Conversation is only triggered for the first SMS message sent from a particular phone number.  For debugging purposes, if you want to re-trigger the Studio flow by sending messages from the same phone number, you’ll need to delete existing Conversations. Here’s a Function to delete the most recently created conversation. You can hit this URL from your browser to execute the code, as long as you un-check the box that says “check for a valid Twilio signature.”

exports.handler = function(context, event, callback) {
  const client = context.getTwilioClient();
  client.conversations.conversations.list({limit: 1}).then(response => {
    response.forEach(conversation => {
      client.conversations.conversations(conversation.sid).remove();
    });
    callback(null, "success");
  });
};

Accessing Studio SMS status callbacks

Normally in Studio, you cannot get all the SMS status callbacks because Studio overrides that setting.

By integrating Studio with Conversations for SMS, you can capture all outbound messaging status callbacks for messages sent via Studio’s message widgets (both Send Message and Send & Wait for Reply). These callbacks are useful if you want to monitor in real-time that your messages are being delivered, or take action if they’re not.

To receive those status updates, set the Status Callback URL in Outbound Settings to your own application:

Screenshot of the Programmable SMS dashboard. This is optional, but you can add the URL of an application in the "Status Callback URL" field if you want to take action in response to message status callbacks.

For more information, check out our quickstart guide on how to handle status callbacks from within your application.

Fetching a Twilio Conversations transcript

One great thing about Conversations is that you have access to the entire chat history. You can then send that chat history to a real live person.

First, we’ll create a Function to print the chat history. Name your Function conversation-transcript.

exports.handler = function(context, event, callback) {
 const conversationSid = event.ConversationSid;
 console.log('conversationSid', conversationSid);
 const client = context.getTwilioClient();
 client.conversations.
 conversations(conversationSid)
 .messages.list()
 .then(messages => {
  // make sure the messages arrive in order
    messages.sort((a, b) => a.index > b.index);
    messages.forEach(message => console.log(`body: ${message.body}, author: ${message.author}`));
    callback(null);
 })
};

Instead of printing, you could pass along the transcript via email, or even send it to one of your applications.

Head back to your Studio Flow. Drag a Run Function widget on to the canvas and connect it with your last message. Paste the Function URL into the widget. Select the conversation-transcript in the Function URL drop down. You’ll want to pass the conversationSid from Studio to your Function. Add a function parameter, where the key is conversationSid and the value is {{ flow.channel.address }}.

Screenshot of a "Run Function" studio widget. The function URL dropdown has "conversation-transcript" selected. Under Function Parameters, a parameter has been added. The key is "conversationSid" and the value is "{{ flow.channel.address }

Now when you test out your Flow by sending another SMS to that phone number, you should see messages logged by the conversation-transcript Function.

Screenshot of the logging output from the Twilio Function, which contains the conversation generated by testing the Studio Flow.

Initiating outbound Conversations with Studio

To initiate an outbound Flow with Conversations, you’ll need to start outside of Studio instead of using the Studio REST API widget. I recommend using the regular SMS API to send an initial message from the number you’ve configured for use with the Default Conversations Service following the above steps.

Next steps

If you’re curious about what else you can do with Conversations, we’ve got docs for that. Also, check out some cool things you can do with Studio such as: