Skip to main content


Understanding Twilio Studio Flow

Twilio Studio offers a canvas where a masterful contact center designer can capture a flow that can be readable and provide a great customer experience.  There are many great tutorials offered by Twilio and what I hope to offer in this article is how I have come to understand flows in general, their execution, and debugging.  I will include a set of reference material at the end as well as directing to Twilio documentation.


Trigger AKA The Entry Point

Twilio Studio Trigger with an incoming message and incoming call set

Each flow contains a single Trigger widget which will be the entry point into the system.  The trigger is designed to handle chat channels, voice, and web requests.  Twilio uses the term Omnichannel to describe the ability to target all these various entry points.  Flows benefit from this mindset as they can be designed to handle more than just one specific target.  A flow could be initially designed for SMS and later have voice capabilities added in without much disruption.  As Twilio adds channels, more persons/devices are able to reach a flow.  If you have a need for integrating a new technology there is always the option of talking via web requests.


Thinking in Terms of Flow

The building blocks of any flow are the widgets.  The goal of a widget is to operate on information and potentially generate a response.  This can be as simple as sending an SMS or saying a voice prompt, but can get more complex with requests to Twilio Functions which can make requests to external systems.  I prefer to approach widgets with the mind set: What are my inputs, outputs, and where do we go next?



I categorize my inputs into the following categories:

  1. Static – Hard-coded data
  2. Internal – data provided by the flow
  3. External – data retrieved from an external system such as database, Web API, CRM system
  4. Blended – building a better experience by including the internal/external with static data

Starting with a simple goal of greeting the texter/caller, we quickly run through a demonstration of each style and see how blending together can make a more better overall experience.

  1. Hello from Perficient’s Development Team.  What can we help you with?
  2. Hello from Perficient’s Development Team, we notice that you are calling from #+1312….. and will connect you with our Chicago-based team.  What can we help you with?
  3. Hello Shelby from Perficient’s Development Team, What can we help you with?
  4. Hello Shelby from Perficient’s Development Team, we notice that you are calling from #+1312….. and will connect you with our Chicago-based team.  What can we help you with?

It should feel like a no-brainer if you have an external system with customer data, do a lookup first before attempting to start the interaction with the customer.  If the data is there it can add to the overall experience.  Keep in mind that the flow should also handle the case where a customer is not present.  To avoid responding with: Hello undefined from Perficient’s Development Team…



A subset of widgets provide information back to the flow that can be consumed in later widgets.  Using HTTP Request/Run Function widgets to pull data from external sources and transforming that data into either plain text or JSON can bring context to the person calling.  There is a large opportunity to start leveraging Natural Language Understanding, NLU, services to process responses, either chat or voice, looking for intent and related context.  This can trim down complex flows into a matter of gathering, NLU with processing, and skipping through the traditional IVR system.  Getting the customer where they want faster.



Example gather with fallback gather to capture a customer's name

Each widget has transitions to dictate where to go next.  If a customer fails to provide enough information during a gathering step should the flow continue as normal?  Avoid sending the customer through an infinite loop of asking for information if their response does not fit what the flow is expecting.  If a widget supports multiple transitions make sure to evaluate it to ensure that in a failure case the flow does not abruptly leave a customer in the abyss.  Dead ends in a flow can be missed opportunities..


Flow Execution

Executions page with two listed executions

Each run through a flow is captured in a set of logs per Flow called Executions.  It is handy to dive into them to see how a flow went and verify the data used in widgets.  I have written a few Twilio Functions only to later learn that I was not correctly referencing that data inside the flow.


Simple Execution

Execution page loaded with debugging information loaded for the Say widget

In a simple flow that has uses the Say/Play widget we might expect an execution to look like the above.  The trigger entry point always occur followed by steps attached to that channel.  In this case it was a call and we can see that the flow transition to a widget named Say.  Expand a widget out to see further information about what the widget achieved and the transition.  I did not expand out the Widget & Flow Properties because it contains all information about the flow up to the point after executing the widget.  This is the place to look when a HTTP Request/Run Function widget feels like it is not working as intended.


Execution Cleanup

Executions log with Stop Execution circled in red

Rarely, an execution may get into a state where the flow does not appear to be processing.  If there is an execution active for a telephone number it will be unable to restart the flow and typically provide an error in the debugger.  Look at the specific execution on the Executions page and make sure it is does not have a Stop Execution option available.  While a flow is actively running it will be possible to end execution via that link.  Data prior to stopping is available by clicking into that execution.


General Debugging

The following are what I have been using to debug flows:

  1. Locate the execution in question via Logs/Executions for the specific flow
  2. Find the Widget that is not behaving and expand out the Widget & Flow Properties
  3. Start looking at what are the inputs/outputs of the widget
  4. If the widget is an Run Function, I load up the specific function’s page via Manage and re-execute the flow looking for any expected console output
    • Stop the execution if necessary
    • Add more console logging if necessary
  5. The Debugger can also provide some context as a last resort


Common Task

Aggregate flow with customer lookup, name gathering, and aggregating branches into hello person

The most common task I have done in a flow by far is aggregating data from two branches back into one.  If the flow branches to get a new customer’s name, it may make sense that at a later point this branch can rejoin the main.  To rejoin a branch it will involve aggregating data between the two branches.  There are two ways to think about this aggregation:

  1. Check multiple parameters in the next widget
  2. Send multiple parameters into a single widget and get A or B out

In either of the above options the widget must support multiple parameters or have a complex statement.  Without an aggregation step a flow can quickly increase in size to support duplicate widgets in each branch.  We decided to create a function to handle choosing between two JSON parameters as follows:

exports.handler = function(context, event, callback) {
    let response = new Twilio.Response();
    response.appendHeader('Content-Type', 'application/json');
    if (event.a) {
    } else if (event.b) {
    } else {
        console.log('a and b both undefined');
    callback(null, response);

As the flow executes we would expect that A represents a new customer’s name and B represents an existing customer’s name.  We retrieve B at the start during the customer lookup.  If we do not find them we need to execute the path get A.  Once we have both we can aggregate into our function and we expect that the flow will only reach the function with one of the two values defined and return that result as the aggregated data.  The aggregated data then feeds into the main branch again and we greet both A and B with the same message.





Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Shelby Hagman

More from this Author

Follow Us