dial
The dial verb originates an outbound leg from an in-progress Voice App
call and bridges it. Use it when the flow needs to reach a human (a
sales rep, on-call engineer, or IVR downstream) after the caller
lands.
Minimal example
Section titled “Minimal example”{ "action": "dial", "to": "+14155550123"}Returned from your flow_url when the caller is on the line. Teler
originates a leg to +14155550123, waits up to 30 seconds for pickup,
then bridges the two legs into a two-way call.
Full schema
Section titled “Full schema”{ "action": "dial", "to": "+14155550123", "timeout": 30, "record": "stereo", "custom_headers": { "X-Reason": "escalation" }, "status_callback_url": "https://example.com/webhook", "ringback": "passthrough", "dial_music": { /* nested action */ }, "confirm_sound": { /* nested action */ }, "on_no_answer": { /* nested action */ }, "on_busy": { /* nested action */ }, "on_failure": { /* nested action */ }}Fields
Section titled “Fields”| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
action | string | yes | none | Must be "dial". |
to | string | yes | none | E.164 phone number or SIP URI (sip:user@host). Single target only. |
timeout | integer (seconds) | no | 30 | 1..600. Time to wait for pickup before treating as no-answer. |
record | mixed | no | false | See Recording modes. |
custom_headers | object | no | {} | Extra SIP headers on the outbound INVITE. Keys must start with X-. Max 16 headers, values ≤ 256 bytes. |
status_callback_url | string | no | Voice App’s webhook_url | Override where lifecycle events for this dial land. |
ringback | enum | no | passthrough | passthrough = the caller hears the target’s ring; suppress = silence until bridge. Use with dial_music to play hold audio instead. |
dial_music | nested action | no | none | Plays to the caller while the target rings. See Nested actions. |
confirm_sound | nested action | no | none | Plays to the target immediately after they pick up, before the bridge. Useful for “press 1 to accept this call” patterns. |
on_no_answer | nested action | no | none | Runs on the caller’s leg if the target doesn’t answer within timeout. |
on_busy | nested action | no | none | Runs if the target rejected with busy (486). |
on_failure | nested action | no | none | Runs on any other failure (network, 404, 503, unreachable, etc.). |
Recording modes
Section titled “Recording modes”record accepts:
| Value | What gets recorded |
|---|---|
false (default) | Nothing. |
true | "stereo" | Both legs into a single stereo file. Caller on left, target on right. |
"mono" | Both legs mixed into a single mono file. |
"per_leg" | Two separate mono files, one per leg. Each leg has its own recording. |
Recording requires the account-level recording flag to be enabled. If it isn’t, the field is silently ignored and no recording is produced.
Recording lifecycle events (recording.completed / recording.failed)
land on the same webhook_url as the rest of the call.
Nested actions
Section titled “Nested actions”dial_music, confirm_sound, on_no_answer, on_busy, and
on_failure accept a single nested action: the same JSON shape as
a top-level flow response, one level deep. Nesting is bounded to a
depth of 10.
Supported nested actions today: play and hangup. (say is defined
in the schema but not yet implemented at runtime.)
{ "action": "dial", "to": "+14155550123", "dial_music": { "action": "play", "media_url": "https://cdn.example.com/hold.mp3", "loop": true }, "on_busy": { "action": "hangup" }}Behavior on each outcome
Section titled “Behavior on each outcome”| Target status | Bridge attempt | Fallback runs? | Events fired |
|---|---|---|---|
| Answers | Bridged | none | leg.created, leg.answered on the target leg |
No answer within timeout | No | on_no_answer | leg.created, leg.completed (reason: "no_answer") |
| Busy (SIP 486) | No | on_busy | leg.created, leg.completed (busy) |
| Rejected / any other failure | No | on_failure | leg.created, leg.completed (rejected | unreachable | …) |
| Caller hangs up first | Cancelled | none | leg.completed (canceled) |
If no fallback is provided for an outcome, the caller’s leg proceeds to the next action in the flow (or ends if the dial was the last action).
The full set of leg reason values is normal, no_answer, busy,
canceled, unreachable, rejected, flow_error, system_failure.
Custom SIP headers
Section titled “Custom SIP headers”custom_headers lets you pass call metadata through to a SIP-terminated
destination. Rules:
- Keys must begin with
X-. A non-conforming key is rejected with a validation error and the whole request fails. - Maximum 16 headers.
- Each value ≤ 256 bytes.
{ "action": "dial", "to": "sip:agent@pbx.example.com", "custom_headers": { "X-Ticket-Id": "12345", "X-Priority": "high" }}Events emitted
Section titled “Events emitted”dial emits per-leg lifecycle events on the target leg, alongside the
usual call-level events on the primary leg. All require the owning
Voice App to be on 2026-06-01.
Every event carries the target leg’s leg_id on the envelope. See
Call events for the full schemas.
Common patterns
Section titled “Common patterns”Dial with hold music and no-answer fallback
Section titled “Dial with hold music and no-answer fallback”{ "action": "dial", "to": "+14155550123", "timeout": 25, "dial_music": { "action": "play", "media_url": "https://cdn.example.com/hold.mp3", "loop": true }, "on_no_answer": { "action": "hangup" }}Dial with agent confirmation
Section titled “Dial with agent confirmation”{ "action": "dial", "to": "+14155550123", "confirm_sound": { "action": "play", "media_url": "https://cdn.example.com/press-one-to-accept.mp3" }, "dial_music": { "action": "play", "media_url": "https://cdn.example.com/hold-music.mp3", "loop": true }}Not yet supported
Section titled “Not yet supported”The following are reserved in the schema but not shipped today:
- Multiple targets (array on
to). Coming in a follow-up release asto: [...]with race-to-answer semantics. machine_detection. Reserved; will detect voicemail vs live human when it ships.- SIP + PSTN mixing in a single
toarray.