Skip to content

Call resources

Requires webhook version 2026-06-01

On the 2026-06-01 webhook version, every call you make or receive is addressable as a Call Session (cs_<ULID>) with one or more Call Legs (cl_<ULID>) hanging off it. These IDs appear in every webhook event and in the inspection API below. Use them to reconcile events, look up historical calls, and correlate multi-leg operations like dial and transfer.

A simple one-party call has one leg. A dial operation adds a dial_target leg; transfer, monitor, and supervisor operations add transfer_target, monitor, and parked legs.

FieldTypeDescription
idstring (cs_<ULID>)Stable ID. Use everywhere.
account_idstring (acc_<ULID>)Owning account.
call_app_idstring (va_<ULID>) | nullOwning Voice App (null for SIP Trunk calls).
stateenuminitiated | ringing | answered | completed | failed.
directionenuminbound or outbound.
from_numberE.164Calling party.
to_numberE.164Called party.
propertiesobjectReserved for future metadata. {} today.
created_atISO-8601When the call was originated / received.
answered_atISO-8601 | nullSet when the callee picks up.
ended_atISO-8601 | nullSet when the call terminates.
reasonstring | nullTerminate reason (see below).
legsarrayInline Call Legs (see Call Leg).
ValueWhen
normalEnded after a successful conversation.
no_answerRang out without pickup.
busyCallee is busy.
canceledCaller hung up before the callee picked up.
unreachableNetwork or SIP routing failure.
rejectedCallee explicitly rejected.
flow_errorYour flow_url returned an error or timed out.
system_failureAn internal platform error terminated the call.
FieldTypeDescription
idstring (cl_<ULID>)Stable leg ID.
call_session_idstring (cs_<ULID>)Parent Call Session.
directionenuminbound or outbound, relative to the leg, not the whole call.
roleenumprimary | dial_target | transfer_target | monitor | parked.
stateenumcreated | ringing | answered | completed | failed.
from_numberE.164This leg’s calling party.
to_numberE.164This leg’s called party.
parent_leg_idstring (cl_<ULID>) | nullSet on legs originated from another leg (e.g. dial targets).
recordingsarray of string (rec_<ULID>)Recording IDs for this leg. [] if the leg has no recording.
created_atISO-8601When the leg was originated.
answered_atISO-8601 | nullWhen the leg was answered.
ended_atISO-8601 | nullWhen the leg ended.
reasonstring | nullLeg-level terminate reason (same vocabulary as Call Session).
ended_byenum | nullcaller | callee | system.
GET /api/v1/voice/calls

Filters:

ParamTypeNotes
stateenumFilter by lifecycle state.
from_numberE.164Exact match.
to_numberE.164Exact match.
created_afterISO-8601Inclusive.
created_beforeISO-8601Exclusive.
limitinteger1-100, default 50.
cursor_afterstringCursor for the next page.
cursor_beforestringCursor for the previous page.

Response:

{
"data": [
{
"id": "cs_...",
"account_id": "acc_...",
"call_app_id": "va_...",
"state": "completed",
"direction": "inbound",
"from_number": "+91XXXXXXXXXX",
"to_number": "+91XXXXXXXXXX",
"properties": {},
"created_at": "2026-06-01T11:30:45.100Z",
"answered_at": "2026-06-01T11:30:48.412Z",
"ended_at": "2026-06-01T11:32:18.501Z",
"reason": "normal",
"legs": [ /* see below */ ]
}
],
"next_cursor": "...",
"previous_cursor": null,
"has_more": true
}
GET /api/v1/sip/calls?trunk_id=42

Same shape, with one difference:

  • Legs are not exposed (SIP Trunk product does not have per-leg addressability). SIP Trunk calls still use cs_<ULID> call IDs.
GET /api/v1/voice/calls/{call_id} # cs_<ULID>
GET /api/v1/sip/calls/{call_id} # cs_<ULID>

Returns the same shape as a list-response item. 404 if the ID doesn’t belong to your account.

GET /api/v1/voice/calls/{call_id}/legs

Voice App only. Returns the same legs that appear inline on the Call Session, as a cursor page.

{
"data": [
{
"id": "cl_...",
"call_session_id": "cs_...",
"direction": "inbound",
"role": "primary",
"state": "completed",
"from_number": "+91XXXXXXXXXX",
"to_number": "+91XXXXXXXXXX",
"parent_leg_id": null,
"recordings": ["rec_..."],
"created_at": "2026-06-01T11:30:45.100Z",
"answered_at": "2026-06-01T11:30:48.412Z",
"ended_at": "2026-06-01T11:32:18.501Z",
"reason": "normal",
"ended_by": "callee"
}
],
"next_cursor": "...",
"previous_cursor": null,
"has_more": true
}

Every webhook event carries the resource IDs on the envelope:

Envelope fieldRefers to
call_idThe Call Session (or SIP Trunk Call) this event is about.
leg_idThe specific leg, on per-leg events.
voice_app_id / sip_trunk_idThe owning resource.

To reconcile: when a call.completed event arrives, GET /api/v1/voice/calls/{envelope.call_id} returns the same state you’re notified about. The API is the source of truth; webhooks are a push notification for that truth.