Call Lifecycle
What happens from POST /v2/calls to the finished record — every stage, which status you see, and exactly when each field becomes readable.
This page traces one outbound call from the API request to the fully-populated record. Inbound calls join at the ringing stage; everything after that is identical.
The flow
Stage by stage
- Accepted.
POST /v2/callsmints theexecution_idbefore it checks anything, returns it in the body and in theX-Execution-Idheader, and answers one of three ways:201,status: "queued"— the dial was handed to the telephony network.202,status: "scheduled"— you sentscheduled_at; or the agent's calling window was shut (deferred_by_calling_guard: true); or every line was busy (reason: "waiting_for_a_free_line"). Nothing has dialled yet.202,status: "queued",dispatch: "pending_confirmation"— the dial went out and the acknowledgement did not come back. It may be ringing. Poll the id; never redial.
- Scheduled calls wait, and re-check the window. At fire time the agent's
Calling Window is evaluated again; a call whose window has closed stays
scheduledwith a newscheduled_atinside the next open window rather than being rejected. Where in that window it lands is the agent'scalling_guard.spreadsetting: by default held calls are scattered across the whole window, so a night's worth of them does not all ring at once. Set it to"asap"to ring every held call in the opening minute, or to a number of minutes to spread them across just that much of the window. A call waiting for a free line is dialled the moment one frees. - Dialling. The call is handed to the telephony network:
initiated, thenringing. A call nobody answers ends here asno-answerorbusy. A call the person answers but our voice agent never joins endsfailed, with anerror.messagebeginningagent_unavailable(the webhook sayssub_status: "agent_unavailable"). - Conversation. On answer the status is
in-progressand the voice pipeline runs — transcription, the agent's reasoning, speech synthesis — one loop per conversational turn. - Hangup ⇒ terminal status, immediately. When the call ends — whoever
ended it, and why, is the subject of Hangups &
Termination — the status moves straight to its terminal
value and
is_terminalbecomestrue. There is no intermediate "post-processing" status. - Enrichment continues after the status is terminal. Transcript assembly, cost, the recording and analysis land in the seconds that follow.
- The webhook fires when everything has landed. The
all_processing_completedevent waits on all of it and carries the full 47-field record — withprocessing_incompleteandmissing_fieldsnaming anything that never arrived.
Terminal means the call is over — not that every field has landed
This is the one place this platform differs from the pattern many
integrations expect. is_terminal: true the moment the line drops; cost,
recording_url and transcript may still be null on that same read. If you
poll, stop on is_terminal and the field you need being non-null. If
you can, don't poll — the consolidated webhook exists precisely so you never
have to guess when enrichment finished.
When the account runs out of credit
A call that has already connected is never cut off for credit: it runs to its end and is charged in full, even when that takes the balance below zero. What stops is everything new:
POST /v2/callsanswers402with error1400once the balance is zero or below — for an immediate call and for one withscheduled_atalike (see error1400in Errors).- A scheduled call that comes due while the account is out of credit is
held, not dropped: it stays
scheduled, is checked again every few minutes, and is placed as soon as credits are added. If the account is still out of credit 60 minutes after the call was first held, the call ends with statusbalance-lowand theall_processing_completedwebhook reportsstatus: "failed"withsub_status: "insufficient_balance".
When each field becomes readable
| Field | Available |
|---|---|
status, is_terminal | Immediately at every stage. |
duration_seconds | At hangup, from the carrier — the carrier figure is authoritative. |
transcript | Seconds after hangup, once assembly completes. GET /v2/calls/{execution_id}/transcript answers status: "unavailable" while it is pending — never a 404. |
cost.total | When billing runs, shortly after hangup. null means not yet billed; 0.0 means billed at zero (a voicemail, for example). |
recording_url | When the recording is stored. A call that was never answered produces none. |
summary, analysis | When post-call analysis completes. |
| per-turn timings | Together with the transcript. Read them at GET /v2/calls/{execution_id}/timing; see Execution Logs. |
A canceled call that never dialled is the one case where these fields never
arrive: nothing was placed, nobody was rung, no minutes are billed, and no
webhook is sent.
Statuses
The full fifteen-value enum, the terminal / in-flight / failure sets, and the spelling rules live on Errors & Statuses. The webhook's own nine-value vocabulary is on Webhook Payloads.

