Call resources
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.
Call Session
Section titled “Call Session”Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
id | string (cs_<ULID>) | Stable ID. Use everywhere. |
account_id | string (acc_<ULID>) | Owning account. |
call_app_id | string (va_<ULID>) | null | Owning Voice App (null for SIP Trunk calls). |
state | enum | initiated | ringing | answered | completed | failed. |
direction | enum | inbound or outbound. |
from_number | E.164 | Calling party. |
to_number | E.164 | Called party. |
properties | object | Reserved for future metadata. {} today. |
created_at | ISO-8601 | When the call was originated / received. |
answered_at | ISO-8601 | null | Set when the callee picks up. |
ended_at | ISO-8601 | null | Set when the call terminates. |
reason | string | null | Terminate reason (see below). |
legs | array | Inline Call Legs (see Call Leg). |
Terminate reasons
Section titled “Terminate reasons”| Value | When |
|---|---|
normal | Ended after a successful conversation. |
no_answer | Rang out without pickup. |
busy | Callee is busy. |
canceled | Caller hung up before the callee picked up. |
unreachable | Network or SIP routing failure. |
rejected | Callee explicitly rejected. |
flow_error | Your flow_url returned an error or timed out. |
system_failure | An internal platform error terminated the call. |
Call Leg
Section titled “Call Leg”Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
id | string (cl_<ULID>) | Stable leg ID. |
call_session_id | string (cs_<ULID>) | Parent Call Session. |
direction | enum | inbound or outbound, relative to the leg, not the whole call. |
role | enum | primary | dial_target | transfer_target | monitor | parked. |
state | enum | created | ringing | answered | completed | failed. |
from_number | E.164 | This leg’s calling party. |
to_number | E.164 | This leg’s called party. |
parent_leg_id | string (cl_<ULID>) | null | Set on legs originated from another leg (e.g. dial targets). |
recordings | array of string (rec_<ULID>) | Recording IDs for this leg. [] if the leg has no recording. |
created_at | ISO-8601 | When the leg was originated. |
answered_at | ISO-8601 | null | When the leg was answered. |
ended_at | ISO-8601 | null | When the leg ended. |
reason | string | null | Leg-level terminate reason (same vocabulary as Call Session). |
ended_by | enum | null | caller | callee | system. |
List calls
Section titled “List calls”Voice App calls
Section titled “Voice App calls”GET /api/v1/voice/callsFilters:
| Param | Type | Notes |
|---|---|---|
state | enum | Filter by lifecycle state. |
from_number | E.164 | Exact match. |
to_number | E.164 | Exact match. |
created_after | ISO-8601 | Inclusive. |
created_before | ISO-8601 | Exclusive. |
limit | integer | 1-100, default 50. |
cursor_after | string | Cursor for the next page. |
cursor_before | string | Cursor 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}SIP Trunk calls
Section titled “SIP Trunk calls”GET /api/v1/sip/calls?trunk_id=42Same 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 one call
Section titled “Get one call”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.
List legs for a call
Section titled “List legs for a call”GET /api/v1/voice/calls/{call_id}/legsVoice 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}How resources tie back to webhooks
Section titled “How resources tie back to webhooks”Every webhook event carries the resource IDs on the envelope:
| Envelope field | Refers to |
|---|---|
call_id | The Call Session (or SIP Trunk Call) this event is about. |
leg_id | The specific leg, on per-leg events. |
voice_app_id / sip_trunk_id | The 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.