Voice sequence node reference
Warning: Voice sequences are in preview. Steps, entry data, and the sequence definition format can change in ways that break existing sequences and API requests.
Use this reference to write the sequence.definition you send with Create Call. Bird runs an inline definition once and does not save it. To keep a sequence, build it in the dashboard editor.
Definition
| Field | Value |
|---|---|
schema_version | 1. |
expression_environment | "bird.cel.v1". |
nodes | Array of node objects. Array order has no effect on execution. |
settings | Omit it or send {}. A non-empty object fails with unsupported_contract; a value that is not an object fails with invalid_envelope. |
presentation | Optional editor layout and labels. It does not affect execution but counts toward size limits. |
Each node has these fields:
| Field | Value |
|---|---|
id | Unique in the definition. Starts with a lowercase letter, then lowercase letters, digits, or _, up to 64 characters. |
type | A node type from this page, such as voice.say. |
type_version | 1 for every node type on this page. |
config | Setup that fixes the node's shape, such as declared outcomes. Use {} when the node has none. |
input | Values the node uses when it runs. Use {} when the node has none. |
connections | Map from an outcome name to { "node_id": "<target>", "port": "input" }. |
Connections must not form a loop, and every node must be reachable from an entry. An outcome left out of connections is unconnected. Unconnected outcomes end the run normally, except where a node says otherwise.
Values and expressions
Most fields take literal JSON. Fields marked binds also accept $expr or $template; fields marked expr also accept $expr only:
{"$expr": "trigger.data.customer_name"}returns a typed value.{"$template": ["Hello ", {"$expr": "trigger.data.customer_name"}, "."]}returns text. Templates are arrays;{{name}}stays literal text.
Expressions use CEL and can read these values:
| Value | Contents |
|---|---|
trigger.data | The entry data, matching the entry's data_schema. |
trigger.node_id | The ID of the entry that started this visit. |
trigger.type | The type of the entry that started this visit, such as trigger.start_call. |
steps.<id>.output | Output of an earlier completed node in this visit. |
variables.<key> | Values saved by a data.set node in this visit. |
execution.id, execution.started_at | The run ID and its start time. Use string(execution.started_at) in speech. |
execution.call | The call's id, session_id, and original parties orig and dest, each of which can be null. |
Guard optional values before reading them, for example has(trigger.data.name) ? trigger.data.name : 'caller'. The has macro is available; map, filter, all, exists, and exists_one are not.
Entry
trigger.start_call
Where a call starts. entry_node_id in the Create Call request names this node.
| Field | Value |
|---|---|
config.data_schema | Optional JSON Schema (draft 2020-12) object schema for trigger_data. Only local $ref is supported. Without it, the entry accepts only {}. |
| Outcome | event. |
Entry data is limited to 16 KiB.
Speech and audio
voice.say
| Field | Value |
|---|---|
input.text | Required, binds. Text to speak, at most 160 bytes. |
input.language | Required. Speech language code, such as en. |
| Outcome | next. |
voice.play
| Field | Value |
|---|---|
input.asset_id | Required. ID of an audio asset available in this workspace. An unavailable asset can fail the call when the step runs. |
| Outcome | next. |
voice.tone
| Field | Value |
|---|---|
input.frequency_hz | Required. 100 to 3000. |
input.duration_ms | Required. 1 to 10000. |
| Outcome | next. |
logic.pause
Waits with the call connected.
| Field | Value |
|---|---|
input.duration_ms | Required. 1 to 60000. |
| Outcome | next. |
Keypad input
voice.gather
Plays prompts, which a key press interrupts, and collects keypad digits.
| Field | Value |
|---|---|
input.prompts | Required. Up to 4 prompts. Each is {"type": "say", "text", "language"}, {"type": "play", "asset_id"}, {"type": "tone", "frequency_hz", "duration_ms"}, or {"type": "pause", "duration_ms"}, with the same limits as the matching node. Tone and pause durations together are at most 60000 ms. Prompt text binds. |
input.max_digits | Required. 1 to 32. |
input.timeout_seconds | Required. Seconds to wait for the first key, 1 to 10. |
input.inter_digit_timeout_seconds | Required. Seconds to wait between keys, 1 to 5. |
input.finish_on_key | Required. One of 0–9, *, #, A–D that ends input, or null. |
input.matches | Required. Map from an outcome name to the exact digits that select it, such as {"hours": "1"}. Names and digits must be unique. A name cannot be input, timeout, or fallback. Digits are at most max_digits long and cannot contain finish_on_key. |
input.private | Optional, default false. When true, later steps cannot read the digits, and they are left out of the trace. |
| Outcomes | Each name in matches, timeout when no key is pressed, and fallback for any other input. |
| Output | digits and reason: initial_timeout, max_digits, finish_key, or inter_digit_timeout. |
Logic and data
logic.branch
Checks conditions in order and follows the first that is true.
| Field | Value |
|---|---|
config.cases | Required. At least one {"port": "<name>", "when": {"$expr": "<boolean>"}}. |
config.default_port | Required. Outcome name when no case is true. |
| Outcomes | Each port in cases, and default_port. |
| Output | branch: the selected outcome name. |
data.set
Saves values for later steps in the same visit.
| Field | Value |
|---|---|
input | Object of values, each of which binds. Each key becomes variables.<key>. Every value is computed from the variables as they were before this node. |
| Outcome | next. |
| Output | The saved object. |
Calls and transfers
voice.dial
Calls another number and connects it to the current call.
| Field | Value |
|---|---|
input.to | Required, binds. E.164 number, such as +12025550123. |
input.timeout_seconds | Required, expr. Seconds to ring, 1 to 120. |
| Outcomes | success, busy, no_answer, and failure. success follows after the connected call ends and the original call's audio is restored, not when the destination answers. An unconnected failure fails the run. |
logic.voice_goto
Starts another entry in the same definition. The new visit gets fresh trigger.data and clears earlier step outputs and variables; the call continues.
| Field | Value |
|---|---|
input.entry_node_id | Required. ID of a trigger.start_call node in this definition. |
input.data | Optional, expr, default {}. Entry data for that node's schema. |
| Outcomes | None. |
Ending the call
logic.exit
Ends the call with a result.
| Field | Value |
|---|---|
config.status | Required. succeeded or failed. |
config.reason | Optional, expr. Stable code of lowercase letters, digits, and _, starting with a letter or digit, up to 64 characters. Omit it rather than sending null. |
input.output | Required object, which can be {}. Its top-level values bind. |
| Outcomes | None. |
voice.hangup
Ends the call without an authored result. It has no fields and no outcomes.
Early access steps
voice.webhook, voice.record_start, voice.record_stop, and logic.voice_goto with dependency_id for managed voicemail or call recording are in early access and are not documented here. Using them requires access for your workspace.
Example: transfer to a person
This definition greets the recipient, dials a support number, and records whether the transfer connected:
{
"schema_version": 1,
"expression_environment": "bird.cel.v1",
"nodes": [
{
"id": "start",
"type": "trigger.start_call",
"type_version": 1,
"config": {},
"input": {},
"connections": { "event": { "node_id": "greeting", "port": "input" } }
},
{
"id": "greeting",
"type": "voice.say",
"type_version": 1,
"config": {},
"input": { "text": "Connecting you to our support team.", "language": "en" },
"connections": { "next": { "node_id": "support", "port": "input" } }
},
{
"id": "support",
"type": "voice.dial",
"type_version": 1,
"config": {},
"input": { "to": "+12025550123", "timeout_seconds": 30 },
"connections": {
"success": { "node_id": "connected", "port": "input" },
"busy": { "node_id": "unavailable", "port": "input" },
"no_answer": { "node_id": "unavailable", "port": "input" },
"failure": { "node_id": "unavailable", "port": "input" }
}
},
{
"id": "connected",
"type": "logic.exit",
"type_version": 1,
"config": { "status": "succeeded", "reason": "transferred" },
"input": { "output": {} }
},
{
"id": "unavailable",
"type": "logic.exit",
"type_version": 1,
"config": { "status": "failed", "reason": "support_unavailable" },
"input": { "output": {} }
}
]
}Send it as sequence.definition with "entry_node_id": "start" and "trigger_data": {}.
Related resources
Continue with the documentation, guides and examples for this topic.