Skip to contentSkip to navigationSkip to topbar
On this page

Studio Flow Definition


To represent the complex structure and requirements of a Studio Flow and its widget configurations, the Flow definition is expressed as a JSON schema. Thus, when creating or updating Flows via the REST API, the JSON sent in the Definition parameter must pass validation against that schema.

Below are sample Flow and widget definitions you can copy and paste into your application. Alternatively, use the Studio editor in Twilio Console(link takes you to an external page) to automatically create valid definitions and export them from the API to save them locally or to add them to version control. Refer to the JSON schemas for full details of widgets and properties.


Standalone Definitions

standalone-definitions page anchor

The following sample definitions can be used as standalone starters for your application. Include the complete JSON as the Definition parameter in your POST to the API.

Simple Flow Definition

simple-flow-definition page anchor

Simple Flow for handling Incoming Message, Incoming Call and REST API triggered Executions

1
{
2
"states": [
3
{
4
"transitions": [
5
{
6
"event": "incomingMessage",
7
"next": "send_message_1"
8
},
9
{
10
"event": "incomingCall",
11
"next": "say_play_1"
12
},
13
{
14
"event": "incomingRequest",
15
"next": "call_user_1"
16
}
17
],
18
"type": "trigger",
19
"name": "Trigger",
20
"properties": {
21
"offset": {
22
"y": 0,
23
"x": 0
24
}
25
}
26
},
27
{
28
"transitions": [
29
{
30
"event": "audioComplete"
31
}
32
],
33
"type": "say-play",
34
"name": "say_play_1",
35
"properties": {
36
"say": "Hello world!",
37
"loop": 1,
38
"offset": {
39
"y": 210,
40
"x": 130
41
}
42
}
43
},
44
{
45
"transitions": [
46
{
47
"event": "sent"
48
},
49
{
50
"event": "failed"
51
}
52
],
53
"type": "send-message",
54
"name": "send_message_1",
55
"properties": {
56
"body": "Hello world!",
57
"from": "{{flow.channel.address}}",
58
"service": "{{trigger.message.InstanceSid}}",
59
"to": "{{contact.channel.address}}",
60
"offset": {
61
"y": 210,
62
"x": -240
63
},
64
"channel": "{{trigger.message.ChannelSid}}"
65
}
66
},
67
{
68
"transitions": [
69
{
70
"event": "answered",
71
"next": "say_play_1"
72
},
73
{
74
"event": "busy"
75
},
76
{
77
"event": "noAnswer"
78
},
79
{
80
"event": "failed"
81
}
82
],
83
"type": "make-outgoing-call-v2",
84
"name": "call_user_1",
85
"properties": {
86
"trim": "true",
87
"machine_detection_silence_timeout": "5000",
88
"from": "{{flow.channel.address}}",
89
"recording_status_callback": "",
90
"record": false,
91
"machine_detection_speech_threshold": "2400",
92
"to": "{{contact.channel.address}}",
93
"detect_answering_machine": false,
94
"sip_auth_username": "",
95
"machine_detection": "Enable",
96
"send_digits": "",
97
"machine_detection_timeout": "30",
98
"timeout": 60,
99
"offset": {
100
"y": 210,
101
"x": 490
102
},
103
"machine_detection_speech_end_threshold": "1200",
104
"sip_auth_password": "",
105
"recording_channels": "mono"
106
}
107
}
108
],
109
"initial_state": "Trigger",
110
"flags": {
111
"allow_concurrent_calls": true
112
},
113
"description": "A New Flow"
114
}

Blank Flow with no widgets attached to the Trigger

1
{
2
"states": [
3
{
4
"transitions": [
5
{
6
"event": "incomingMessage",
7
"next": null
8
},
9
{
10
"event": "incomingCall",
11
"next": null
12
},
13
{
14
"event": "incomingRequest",
15
"next": null
16
}
17
],
18
"type": "trigger",
19
"name": "Trigger",
20
"properties": {
21
"offset": {
22
"y": 0,
23
"x": 0
24
}
25
}
26
}
27
],
28
"initial_state": "Trigger",
29
"flags": {
30
"allow_concurrent_calls": true
31
},
32
"description": "A New Flow"
33
}

Widgets are defined as "states" that the Studio workflow engine transitions to in response to events. The following JSON snippets can be used to add individual widgets as states to an existing Flow definition.

Add each widget snippet as a new item in the states array:

1
{
2
"states": [
3
// ADD WIDGET SNIPPETS HERE
4
],
5
"initial_state": "Trigger",
6
"flags": {
7
"allow_concurrent_calls": true
8
},
9
"description": "A New Flow"
10
}
11

Snippet of JSON to add a TwiML redirect

1
{
2
"transitions": [
3
{
4
"event": "return"
5
"next": null
6
},
7
{
8
"event": "timeout"
9
"next": null
10
},
11
{
12
"event": "fail"
13
"next": null
14
}
15
],
16
"type": "add-twiml-redirect",
17
"name": "redirect_1",
18
"properties": {
19
"url": "https://funny-name-1234.twil.io/",
20
"method": "POST",
21
"timeout": "14400",
22
"offset": {
23
"y": 0,
24
"x": 0
25
}
26
}
27
}

Snippet of JSON to capture payments

1
{
2
"transitions": [
3
{
4
"event": "success"
5
"next": null
6
},
7
{
8
"event": "maxFailedAttempts"
9
"next": null
10
},
11
{
12
"event": "providerError"
13
"next": null
14
},
15
{
16
"event": "payInterrupted"
17
"next": null
18
},
19
{
20
"event": "hangup"
21
"next": null
22
},
23
{
24
"event": "validationError"
25
"next": null
26
}
27
],
28
"type": "capture-payments",
29
"name": "pay_1",
30
"properties": {
31
"payment_token_type": "reusable",
32
"currency": "usd",
33
"postal_code": "true",
34
"timeout": 5,
35
"offset": {
36
"y": 0,
37
"x": 0
38
},
39
"valid_card_types": [],
40
"security_code": true,
41
"max_attempts": 2
42
}
43
}

Snippet of JSON to connect the caller to another number

1
{
2
"transitions": [
3
{
4
"event": "callCompleted"
5
"next": null
6
},
7
{
8
"event": "hangup"
9
"next": null
10
}
11
],
12
"type": "connect-call-to",
13
"name": "connect_call_1",
14
"properties": {
15
"noun": "number",
16
"to": "+14155551212",
17
"caller_id": "{{contact.channel.address}}",
18
"record": true,
19
"timeout": 30,
20
"offset": {
21
"y": 0,
22
"x": 0
23
}
24
}
25
}

Snippet of JSON to enqueue the call

1
{
2
"transitions": [
3
{
4
"event": "callComplete"
5
"next": null
6
},
7
{
8
"event": "failedToEnqueue"
9
"next": null
10
},
11
{
12
"event": "callFailure"
13
"next": null
14
}
15
],
16
"type": "enqueue-call",
17
"name": "enqueue_1",
18
"properties": {
19
"workflow_sid": "WW1111111111111111111111",
20
"offset": {
21
"y": 0,
22
"x": 0
23
}
24
}
25
}

Snippet of JSON to fork the audio stream to another endpoint

1
{
2
"transitions": [
3
{
4
"event": "next",
5
"next": null
6
}
7
],
8
"type": "fork-stream",
9
"name": "stream_1",
10
"properties": {
11
"stream_action": "start",
12
"stream_name": "stream",
13
"stream_transport_type": "websocket",
14
"stream_track": "inbound_track",
15
"offset": {
16
"y": 0,
17
"x": 0
18
},
19
"stream_url": "wss://funny-name-1234.twil.io/"
20
}
21
}

Snippet of JSON to gather digits or speech input

1
{
2
"transitions": [
3
{
4
"event": "keypress",
5
"next": null
6
},
7
{
8
"event": "speech"
9
"next": null
10
},
11
{
12
"event": "timeout",
13
"next": null
14
}
15
],
16
"type": "gather-input-on-call",
17
"name": "gather_1",
18
"properties": {
19
"stop_gather": true,
20
"gather_language": "en",
21
"say": "Please press any key to continue.",
22
"loop": 1,
23
"timeout": 5,
24
"offset": {
25
"y": 0,
26
"x": 0
27
},
28
"finish_on_key": "#"
29
}
30
}

Snippet of JSON to make an HTTP request to another API

1
{
2
"transitions": [
3
{
4
"event": "success",
5
"next": null
6
},
7
{
8
"event": "failed",
9
"next": null
10
}
11
],
12
"type": "make-http-request",
13
"name": "http_1",
14
"properties": {
15
"url": "https://funny-name-1234.twil.io/",
16
"parameters": [
17
{
18
"key": "count",
19
"value": "{{flow.variables.count}}"
20
}
21
],
22
"method": "POST",
23
"content_type": "application/x-www-form-urlencoded;charset=utf-8",
24
"offset": {
25
"y": 0,
26
"x": 0
27
}
28
}
29
}

Snippet of JSON to initiate an outbound call

1
{
2
"transitions": [
3
{
4
"event": "answered",
5
"next": null
6
},
7
{
8
"event": "busy",
9
"next": null
10
},
11
{
12
"event": "noAnswer",
13
"next": null
14
},
15
{
16
"event": "failed",
17
"next": null
18
}
19
],
20
"type": "make-outgoing-call-v2",
21
"name": "call_user_1",
22
"properties": {
23
"trim": "true",
24
"machine_detection_silence_timeout": "5000",
25
"from": "{{flow.channel.address}}",
26
"recording_status_callback": "",
27
"record": false,
28
"machine_detection_speech_threshold": "2400",
29
"to": "{{contact.channel.address}}",
30
"detect_answering_machine": false,
31
"sip_auth_username": "",
32
"machine_detection": "Enable",
33
"send_digits": "",
34
"machine_detection_timeout": "30",
35
"timeout": 60,
36
"offset": {
37
"y": 0,
38
"x": 0
39
},
40
"machine_detection_speech_end_threshold": "1200",
41
"sip_auth_password": "",
42
"recording_channels": "mono"
43
}
44
}

Snippet of JSON to record an inbound call

1
{
2
"transitions": [
3
{
4
"event": "success",
5
"next": null
6
},
7
{
8
"event": "failed",
9
"next": null
10
}
11
],
12
"type": "record-call",
13
"name": "call_recording_1",
14
"properties": {
15
"trim": "do-not-trim",
16
"offset": {
17
"y": 0,
18
"x": 0
19
},
20
"recording_status_callback_events": "completed",
21
"recording_status_callback_method": "POST",
22
"record_call": true,
23
"recording_channels": "dual"
24
}
25
}

Snippet of JSON to record voicemail from the caller

1
{
2
"transitions": [
3
{
4
"event": "recordingComplete",
5
"next": null
6
},
7
{
8
"event": "noAudio",
9
"next": null
10
},
11
{
12
"event": "hangup",
13
"next": null
14
}
15
],
16
"type": "record-voicemail",
17
"name": "record_voicemail_1",
18
"properties": {
19
"max_length": 3600,
20
"timeout": 5,
21
"offset": {
22
"y": 0,
23
"x": 0
24
}
25
}
26
}

Snippet of JSON to run a Twilio Function

1
{
2
"transitions": [
3
{
4
"event": "success",
5
"next": null
6
},
7
{
8
"event": "fail"
9
}
10
],
11
"type": "run-function",
12
"name": "function_1",
13
"properties": {
14
"url": "https://funny-name-1234.twil.io/",
15
"parameters": [
16
{
17
"key": "foo",
18
"value": "bar"
19
}
20
],
21
"offset": {
22
"y": 0,
23
"x": 0
24
}
25
}
26
}

Snippet of JSON to say a message to the caller

1
{
2
"transitions": [
3
{
4
"event": "audioComplete",
5
"next": null
6
}
7
],
8
"type": "say-play",
9
"name": "say_play_1",
10
"properties": {
11
"say": "This call is being recorded for quality assurance.",
12
"loop": 1,
13
"offset": {
14
"y": 0,
15
"x": 0
16
}
17
}
18
}

Snippet of JSON to send a text message and wait for a reply

1
{
2
"transitions": [
3
{
4
"event": "incomingMessage",
5
"next": null
6
},
7
{
8
"event": "timeout",
9
"next": null
10
},
11
{
12
"event": "deliveryFailure",
13
"next": null
14
}
15
],
16
"type": "send-and-wait-for-reply",
17
"name": "send_and_reply_1",
18
"properties": {
19
"body": "Hello. Please reply.",
20
"from": "{{flow.channel.address}}",
21
"service": "{{trigger.message.InstanceSid}}",
22
"timeout": 3600,
23
"offset": {
24
"y": 0,
25
"x": 0
26
},
27
"channel": "{{trigger.message.ChannelSid}}"
28
}
29
}

Snippet of JSON to send a text message

1
{
2
"transitions": [
3
{
4
"event": "sent",
5
"next": null
6
},
7
{
8
"event": "failed",
9
"next": null
10
}
11
],
12
"type": "send-message",
13
"name": "send_message_1",
14
"properties": {
15
"body": "Hello world!",
16
"from": "{{flow.channel.address}}",
17
"service": "{{trigger.message.InstanceSid}}",
18
"to": "{{contact.channel.address}}",
19
"offset": {
20
"y": 0,
21
"x": 0
22
},
23
"channel": "{{trigger.message.ChannelSid}}"
24
}
25
}

Snippet of JSON to route the contact to a Flex agent

1
{
2
"transitions": [
3
{
4
"event": "callComplete",
5
"next": null
6
},
7
{
8
"event": "failedToEnqueue",
9
"next": null
10
},
11
{
12
"event": "callFailure",
13
"next": null
14
}
15
],
16
"type": "send-to-flex",
17
"name": "send_to_flex_1",
18
"properties": {
19
"workflow": "WW11111111111111111111111111111111",
20
"channel": "TC11111111111111111111111111111111",
21
"offset": {
22
"y": 0,
23
"x": 0
24
}
25
}
26
}

Snippet of JSON to set Execution-level variables

1
{
2
"transitions": [
3
{
4
"event": "next",
5
"next": null
6
}
7
],
8
"type": "set-variables",
9
"name": "set_variables_1",
10
"properties": {
11
"variables": [
12
{
13
"index": "0",
14
"key": "count",
15
"value": "{% if flow.variables.count %}\n {{flow.variables.count | plus: 1}}\n{% else %}\n 1\n{% endif %}"
16
}
17
],
18
"offset": {
19
"y": 0,
20
"x": 0
21
}
22
}
23
}

Snippet of JSON to add branching logic

1
{
2
"transitions": [
3
{
4
"event": "noMatch",
5
"next": null
6
},
7
{
8
"conditions": [
9
{
10
"type": "equal_to",
11
"friendly_name": "If value equal_to 1",
12
"arguments": [
13
"{{widgets.gather_1.Digits}}"
14
],
15
"value": "1"
16
}
17
],
18
"event": "match",
19
"next": null
20
},
21
{
22
"conditions": [
23
{
24
"type": "equal_to",
25
"friendly_name": "If value equal_to 2",
26
"arguments": [
27
"{{widgets.gather_1.Digits}}"
28
],
29
"value": "2"
30
}
31
],
32
"event": "match",
33
"next": null
34
}
35
],
36
"type": "split-based-on",
37
"name": "split_1",
38
"properties": {
39
"input": "{{widgets.gather_1.Digits}}",
40
"offset": {
41
"y": 0,
42
"x": 0
43
}
44
}
45
}