Skip to main content
Every phone call is a session. The state tells you where it is right now, the failureReason tells you why it ended badly, and the timestamps tell you what actually happened on the line. This page is the decoder. Poll session status while the call is live. After it ends, read the same fields plus the session detail.

The states of an outbound call

Three things to know before you build on these:
  • Every middle hop is optional. Carriers differ in what they report — a call can jump straight from dispatched to answered. Never wait for a specific intermediate state.
  • ringing means the carrier sent ring progress, not that a phone audibly rang. Some carriers report it milliseconds before a failure.
  • answered is not proof a human spoke. The line opening sets it — voicemail and phantom answers included. The proof of a human is the answeredAt timestamp, which is stamped only on real callee speech.
Web sessions run created → active → completed instead, and inbound phone calls arrive already active. Neither gets answeredAt or durationSec — those belong to outbound.

Reading the fields

While the call is live, poll session status and read state. Ignore outcome until the call is terminal: on a call that’s still dialing or ringing, it reads no_answer as a placeholder, not a verdict. After the call, the same endpoint carries the verdict: state, failureReason, outcome, connected, and endedBy. That last one is agent, callee, or system, or absent when nothing stamped the row. The Call end reasons page has the rules, and the campaign retry policy. For the transcript, tool calls, grade, and recording, read the session detail. There is no webhook for dial progress, and no call.failed event. call.completed fires when the attempt ends, failed dials included, so polling is the only way to watch a dial unfold.

The timestamps

The gap between connectedAt and answeredAt is where phantom answers live: connectedAt set with answeredAt absent means the line opened and nobody spoke.