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, except 2 for voice.dial. |
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 a phone number, or a number on one of your workspace's inbound SIP trunks, and connects it to the current call. Use type_version 2.
| Field | Value |
|---|---|
input.type | Required. pstn to call a phone number, or sip_trunk to call a number through one of your inbound SIP trunks. |
input.pstn | Required when type is pstn, and omitted otherwise. Object with dest and optional orig. |
input.sip_trunk | Required when type is sip_trunk, and omitted otherwise. Object with trunk_id, dest, and optional orig. |
trunk_id | Required in sip_trunk. ID of an inbound SIP trunk in your workspace, such as spt_01jzj6c0p4f8b3x2m9k7q5r1tw. Literal value only. |
dest | Required, binds. For pstn, the E.164 number to call, such as +12025550123. For sip_trunk, a number, extension or name, such as 1001: 1 to 64 letters, digits and +*._-. The trunk's inbound routing delivers the call as if it arrived for this value. |
orig | Optional, binds. Caller ID to present. Omit it to present the current call's own number. For pstn, it must be an E.164 number your workspace can use as caller ID. For sip_trunk, it takes the same values as dest, such as sales. |
input.timeout_seconds | Required, expr. Seconds to wait for an answer, including failover between trunk gateways, 1 to 120. Unanswered calls take no_answer; an answered call is not limited. |
| 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. A SIP trunk that rejects or forwards the call selects failure. An unconnected failure fails the run. |
Version 1 takes the number in input.to and has no orig. Steps that use it keep running. When you upgrade a version 1 step in the editor, its to value moves to pstn.dest.
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. |
Automations
automation.send_event
Sends an application event to an Automation without waiting for a run. The call continues once the event is queued; whether a run starts is visible only in Automations.
| Field | Value |
|---|---|
input.automation_id | Required. ID of an automation in this workspace. |
input.event | Required. The automation's event name: 1 to 128 letters, digits, dots, underscores, or hyphens. |
input.data | Optional, default {}. The event's entire data. Its top-level values bind, or the whole object can be one $expr. Values nested deeper stay literal. The whole input is at most 16 KiB. |
| Outcomes | success once the event is queued, or failure if it cannot be. Neither reports the automation's run. An unconnected failure fails the run. |
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 is in early access and is not documented here. Using it requires access for your workspace. voice.record_start, voice.record_stop, and logic.voice_goto with dependency_id for managed voicemail or call recording are not documented here either.
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": 2,
"config": {},
"input": { "type": "pstn", "pstn": { "dest": "+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.