When to use
Use this when you want to build a phone (IVR) or SMS/WhatsApp conversation as a visual flow instead of hand-coding TwiML and webhooks: a call menu, an appointment reminder that gathers a reply, a lead-capture bot, an after-hours router. Reach for it when the logic is branchy but expressible as widgets and you want product/ops people to edit it. Also use it to trigger, inspect, or debug a running flow via the Executions REST API.
Not for: skills-based routing of tasks to human agents/workers — use twilio-taskrouter-routing. Not for sending/checking OTP codes — use twilio-verify-send-otp (though a Studio flow can call Verify). Not for phone number intelligence — use twilio-lookup-phone-intelligence.
Method
- Pick the trigger. A Flow has three trigger entry points: Incoming Message, Incoming Call, and REST API. Decision point: reminders/outbound campaigns start from REST API (
POST .../Executions); customer-initiated support starts from Incoming Call/Message.
- Sketch the states as widgets. Map the conversation to widgets before dragging: Send & Wait For Reply, Send Message, Gather Input on Call (DTMF/speech), Say/Play, Run Function, HTTP Request, Split Based On, Set Variables, Connect Call To, Enqueue (to TaskRouter).
- Wire the branches. Each widget has transition events (e.g. Gather →
keypress, speech, timeout, no_match). Decision point: always wire timeout and no_match — unwired transitions dead-end the caller.
- Reference variables with Liquid. Read prior widget output and flow variables via Liquid, e.g.
{{widgets.gather_1.Digits}}, {{flow.data}}, {{trigger.message.Body}}.
- Call out for data/logic. Use HTTP Request or Run Function widgets for lookups, DB writes, or calling other Twilio APIs (Verify, Lookup). Keep heavy logic in a Function, not spread across many Splits.
- Publish the flow. Studio requires an explicit Publish to make edits live; a Flow has revisions. Decision point: test on the current draft/test number before publishing to production.
- Trigger programmatically when outbound.
POST /v2/Flows/{FlowSid}/Executions with To, From (a Studio-connected Twilio number), and Parameters (JSON injected as {{flow.data}}).
- Inspect and debug via REST. List/fetch Executions and read the ExecutionContext to see the variable state at each step; use it to diagnose where a flow branched wrong.
Example
# Trigger a flow (e.g. appointment reminder) via REST
curl -X POST "https://studio.twilio.com/v2/Flows/$FLOW_SID/Executions" \
--data-urlencode "To=+14155551234" \
--data-urlencode "From=+16505557890" \
--data-urlencode 'Parameters={"name":"Jane","appt":"Tue 3pm"}' \
-u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN"
# -> { "sid": "FN...", "status": "active", "context": {...} }
# Inspect the execution's variable context (debug where it branched)
curl "https://studio.twilio.com/v2/Flows/$FLOW_SID/Executions/$EXECUTION_SID/ExecutionContext" \
-u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN"
# Inside a Send Message widget body, referencing injected + captured data:
Hi {{flow.data.name}}, reply YES to confirm your {{flow.data.appt}} appointment.
# A Split Based On widget testing the reply:
Variable to test: {{widgets.reply_1.inbound.Body}}
matches "YES" (case-insensitive) -> confirm branch
no match -> reprompt branch
Pitfalls
- Unwired transitions dead-end users. Every Gather/Send & Wait widget has
timeout and no_match/no_input events — wire them all or the flow silently strands the caller.
- Forgetting to Publish. Edits stay in draft until you Publish; "my change didn't take" is almost always an unpublished revision.
- Widget variable scope confusion. Widget outputs are namespaced (
{{widgets.<name>.<field>}}); a value captured in one widget isn't a bare global. Use Set Variables or {{flow.data}} for cross-step state.
- Synchronous HTTP Request timeouts. The HTTP Request widget blocks the flow; slow endpoints stall the caller. Keep called services fast or offload to an async Run Function pattern.
- Outbound trigger needs a connected From number.
POST Executions requires From to be a Twilio number attached to that Studio flow, and messaging still obeys A2P/opt-in rules.
- Executions accumulate. Long-lived or stuck executions stay
active; monitor and end/expire them, and use ExecutionContext (not guesswork) to debug branch logic.
Output format
STUDIO FLOW PLAN
flow_name: <name>
trigger: <Incoming Call | Incoming Message | REST API>
widgets:
- <widget type> : <purpose> -> transitions [event -> target]
variables_used: <{{flow.data.*}}, {{widgets.*}}, {{trigger.*}}>
external_calls: <HTTP Request / Run Function targets>
publish_status: <draft | published rev N>
rest_trigger: POST /v2/Flows/{FlowSid}/Executions (if outbound)
Reference
- Product: Twilio Studio — visual, revision-based flow builder for voice and messaging. Host
studio.twilio.com, API version v2.
- Triggers: three entry widgets — Incoming Message, Incoming Call, REST API.
- Common widgets: Send Message, Send & Wait For Reply, Gather Input on Call (DTMF + speech recognition), Say/Play, Split Based On (branching), Set Variables, Run Function (invoke a Twilio Function), HTTP Request (call external APIs), Connect Call To, Enqueue Call (hand to a TaskRouter TaskQueue), Make Outgoing Call.
- Variables (Liquid templating):
{{trigger.message.Body}}, {{trigger.call.From}}, {{widgets.<widgetName>.<output>}} (e.g. .Digits, .inbound.Body), {{flow.data}} (Parameters injected at trigger), {{flow.channel.address}}.
- Execution REST:
POST /v2/Flows/{FlowSid}/Executions (start; body To, From, Parameters), GET .../Executions/{ExecutionSid}, GET .../Executions/{ExecutionSid}/ExecutionContext (full variable state — the debug view), GET .../Executions/{ExecutionSid}/Steps (per-widget step log).
- Publishing: flows are draft until explicitly Published; each publish creates a revision you can roll back to.
- Auth: Account SID + Auth Token or API Key SID/Secret. Webhook security: validate the
X-Twilio-Signature header (HMAC-SHA1 of the URL + sorted params, keyed by your Auth Token) on any Function/HTTP endpoint Studio calls, to confirm requests genuinely came from Twilio.
- Compliance: outbound messaging via Studio is subject to TCPA consent/opt-out (STOP/HELP), A2P 10DLC registration for US long codes, and WhatsApp template rules where applicable.
1---2name: twilio-studio-flows3description: Design and operate Twilio Studio drag-and-drop IVR and messaging flows — widgets, triggers, and the Execution REST API — without writing full call/message routing code.4---56## When to use78Use this when you want to build a phone (IVR) or SMS/WhatsApp conversation as a visual flow instead of hand-coding TwiML and webhooks: a call menu, an appointment reminder that gathers a reply, a lead-capture bot, an after-hours router. Reach for it when the logic is branchy but expressible as widgets and you want product/ops people to edit it. Also use it to trigger, inspect, or debug a running flow via the Executions REST API.910**Not for:** skills-based routing of tasks to human agents/workers — use twilio-taskrouter-routing. Not for sending/checking OTP codes — use twilio-verify-send-otp (though a Studio flow can call Verify). Not for phone number intelligence — use twilio-lookup-phone-intelligence.1112## Method13141. **Pick the trigger.** A Flow has three trigger entry points: Incoming Message, Incoming Call, and REST API. Decision point: reminders/outbound campaigns start from REST API (`POST .../Executions`); customer-initiated support starts from Incoming Call/Message.152. **Sketch the states as widgets.** Map the conversation to widgets before dragging: Send & Wait For Reply, Send Message, Gather Input on Call (DTMF/speech), Say/Play, Run Function, HTTP Request, Split Based On, Set Variables, Connect Call To, Enqueue (to TaskRouter).163. **Wire the branches.** Each widget has transition events (e.g. Gather → `keypress`, `speech`, `timeout`, `no_match`). Decision point: always wire `timeout` and `no_match` — unwired transitions dead-end the caller.174. **Reference variables with Liquid.** Read prior widget output and flow variables via Liquid, e.g. `{{widgets.gather_1.Digits}}`, `{{flow.data}}`, `{{trigger.message.Body}}`.185. **Call out for data/logic.** Use HTTP Request or Run Function widgets for lookups, DB writes, or calling other Twilio APIs (Verify, Lookup). Keep heavy logic in a Function, not spread across many Splits.196. **Publish the flow.** Studio requires an explicit Publish to make edits live; a Flow has revisions. Decision point: test on the current draft/test number before publishing to production.207. **Trigger programmatically when outbound.** `POST /v2/Flows/{FlowSid}/Executions` with `To`, `From` (a Studio-connected Twilio number), and `Parameters` (JSON injected as `{{flow.data}}`).218. **Inspect and debug via REST.** List/fetch Executions and read the ExecutionContext to see the variable state at each step; use it to diagnose where a flow branched wrong.2223## Example2425```bash26# Trigger a flow (e.g. appointment reminder) via REST27curl -X POST "https://studio.twilio.com/v2/Flows/$FLOW_SID/Executions" \28 --data-urlencode "To=+14155551234" \29 --data-urlencode "From=+16505557890" \30 --data-urlencode 'Parameters={"name":"Jane","appt":"Tue 3pm"}' \31 -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN"32# -> { "sid": "FN...", "status": "active", "context": {...} }3334# Inspect the execution's variable context (debug where it branched)35curl "https://studio.twilio.com/v2/Flows/$FLOW_SID/Executions/$EXECUTION_SID/ExecutionContext" \36 -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN"37```3839```40# Inside a Send Message widget body, referencing injected + captured data:41Hi {{flow.data.name}}, reply YES to confirm your {{flow.data.appt}} appointment.4243# A Split Based On widget testing the reply:44Variable to test: {{widgets.reply_1.inbound.Body}}45 matches "YES" (case-insensitive) -> confirm branch46 no match -> reprompt branch47```4849## Pitfalls5051- **Unwired transitions dead-end users.** Every Gather/Send & Wait widget has `timeout` and `no_match`/`no_input` events — wire them all or the flow silently strands the caller.52- **Forgetting to Publish.** Edits stay in draft until you Publish; "my change didn't take" is almost always an unpublished revision.53- **Widget variable scope confusion.** Widget outputs are namespaced (`{{widgets.<name>.<field>}}`); a value captured in one widget isn't a bare global. Use Set Variables or `{{flow.data}}` for cross-step state.54- **Synchronous HTTP Request timeouts.** The HTTP Request widget blocks the flow; slow endpoints stall the caller. Keep called services fast or offload to an async Run Function pattern.55- **Outbound trigger needs a connected From number.** `POST Executions` requires `From` to be a Twilio number attached to that Studio flow, and messaging still obeys A2P/opt-in rules.56- **Executions accumulate.** Long-lived or stuck executions stay `active`; monitor and end/expire them, and use ExecutionContext (not guesswork) to debug branch logic.5758## Output format5960```61STUDIO FLOW PLAN62 flow_name: <name>63 trigger: <Incoming Call | Incoming Message | REST API>64 widgets:65 - <widget type> : <purpose> -> transitions [event -> target]66 variables_used: <{{flow.data.*}}, {{widgets.*}}, {{trigger.*}}>67 external_calls: <HTTP Request / Run Function targets>68 publish_status: <draft | published rev N>69 rest_trigger: POST /v2/Flows/{FlowSid}/Executions (if outbound)70```7172## Reference7374- **Product:** Twilio Studio — visual, revision-based flow builder for voice and messaging. Host `studio.twilio.com`, API version v2.75- **Triggers:** three entry widgets — Incoming Message, Incoming Call, REST API.76- **Common widgets:** Send Message, Send & Wait For Reply, Gather Input on Call (DTMF + speech recognition), Say/Play, Split Based On (branching), Set Variables, Run Function (invoke a Twilio Function), HTTP Request (call external APIs), Connect Call To, Enqueue Call (hand to a TaskRouter TaskQueue), Make Outgoing Call.77- **Variables (Liquid templating):** `{{trigger.message.Body}}`, `{{trigger.call.From}}`, `{{widgets.<widgetName>.<output>}}` (e.g. `.Digits`, `.inbound.Body`), `{{flow.data}}` (Parameters injected at trigger), `{{flow.channel.address}}`.78- **Execution REST:** `POST /v2/Flows/{FlowSid}/Executions` (start; body `To`, `From`, `Parameters`), `GET .../Executions/{ExecutionSid}`, `GET .../Executions/{ExecutionSid}/ExecutionContext` (full variable state — the debug view), `GET .../Executions/{ExecutionSid}/Steps` (per-widget step log).79- **Publishing:** flows are draft until explicitly Published; each publish creates a revision you can roll back to.80- **Auth:** Account SID + Auth Token or API Key SID/Secret. **Webhook security:** validate the `X-Twilio-Signature` header (HMAC-SHA1 of the URL + sorted params, keyed by your Auth Token) on any Function/HTTP endpoint Studio calls, to confirm requests genuinely came from Twilio.81- **Compliance:** outbound messaging via Studio is subject to TCPA consent/opt-out (STOP/HELP), A2P 10DLC registration for US long codes, and WhatsApp template rules where applicable.