Session
A Session represents one addressable participant in a voice interaction. It
can be a phone call or a WebSocket audio endpoint that your
application can inspect and control.
When to use
Use a Session to represent and control one participant in a voice interaction, such as an inbound caller, an outbound call, or a WebSocket voice agent.
A Room connects multiple Sessions in a shared conversation. Playback, DTMF collection, recording, and WebSocket audio operate on an individual Session. SIP and WebRTC participants are planned for a future release.
How Sessions are created
The platform creates Sessions in these ways:
Session lifecycle
The lifecycle field depends on the endpoint. Each item returned by
GET /sessions uses state, while
GET /sessions/{session_uuid}
uses status. Both use the same values: new, connecting, ringing,
early_media, answered, or ended.
Treat state and status as point-in-time snapshots for inspection and
reconciliation. Use lifecycle events to react to changes. Every Session ends
with exactly one terminal session.ended event, whether it was
answered, rejected, timed out, disconnected, or explicitly deleted.
Control a Session
Use the session_uuid to:
- Inspect one Session or list Sessions.
- Answer an incoming call, reject it before answer with an allowed reason, cancel outbound dialing before the callee answers, or request unconditional termination.
- Play audio and collect DTMF digits.
- Create and control multiple Session recordings by
recording_uuid. - Add the Session to a Room and control its participation.
Most control commands are asynchronous. An accepted command returns
202 Accepted with an operation_uuid. The reference for each command
identifies the event that confirms completion or reports the resulting
lifecycle change.
Methods
- Originate an outbound call
- Create a WebSocket Session
- List sessions
- Get session details
- Answer an incoming session
- Reject an unanswered incoming session with
POST /v1/sessions/{uuid}/reject - Cancel outbound dialing with
POST /v1/sessions/{uuid}/cancel - Delete a session
To use Session lifecycle, Playback, or DTMF commands, pass the
Session’s session_uuid in the {uuid} path parameter. The Recording
collection creates, lists, and controls the Recordings that
the Session owns. Recording control commands also require the exact
recording_uuid; there is no implicit current Recording.
Outbound caller ID and trunk selection
When you call POST /v1/sessions:dial, from must be an allowed caller ID
for your application. You can include caller_id_session_uuid to use the
caller ID from a live inbound phone Session owned by your application. That
Session’s caller ID must exactly match from. This option authorizes the
caller ID for that dial only.
You can include trunk_id to request a specific eligible outbound route. If
you omit it, the platform matches the caller ID and destination against each
enabled route, then selects the lowest-priced route with available capacity.
The platform uses the longest matching caller ID prefix and destination rate
prefix independently within each route. When a route is saturated, an unpinned
dial can use the next eligible route. A pinned route must support the
destination and caller ID and does not fall back to another route.
If no eligible route has a matching caller ID prefix and destination price,
the request returns 503 unavailable. If eligible routes are all saturated
or the application’s concurrent setup limit is full, it returns HTTP 529
with code unavailable, message overloaded, and a Retry-After: 5 header.
These capacity responses happen before monthly dial quota admission. A
202 Accepted response confirms Session admission, not that the outbound call
attempt has started or that the destination is ringing. The
platform starts call attempts asynchronously and enforces each route’s maximum
new setup attempts in a rolling one-second window. Each started attempt counts,
even if it fails or receives no response. Your application’s concurrent setup
limit applies across all outbound routes. Pending and
ringing Sessions count until answer, confirmed termination, or a definitive
no-send outcome; the default is one, and zero means unlimited. Check the
effective value in GET /api/v1/app. When that
application limit is full, the platform returns 529 immediately rather than
waiting for a slot. When trunk and application capacity remain, a new attempt
can start while an earlier one is still waiting for a response. Use Session
events to follow call progress and completion. After dispatch, the platform
does not retry or switch routes.
When no outbound catalog exists, the platform initializes an initial HOT route
without a manual catalog write. That route permits only its configured caller
ID prefixes; a dial with another caller ID remains unavailable unless an
operator adds another eligible route. Additional providers and their rates
remain operator-configured. An existing catalog, including an empty one, is
preserved rather than replaced with a default route.
Resource representation
Properties
When the session was created, as an RFC 3339 / ISO 8601 UTC string
(e.g. "2025-05-06T12:41:36.000Z"). Storage resolution is one second,
so the millisecond fraction is always .000. Empty string if the
originating timestamp is missing or unparseable.
Current lifecycle state of the session. connecting applies to a
websocket session whose WebSocket server connection has not yet been established.
The nature of the session:
phone_in: inbound phone call from a carrierphone_out: outbound phone call placed withdialsip: a registered SIP devicewebsocket: a WebSocket session with no phone legunknown: could not be determined or is not a supported public session type
Caller ID in canonical international E.164 format with leading +
(e.g. +972500000000). Empty for websocket sessions (no phone leg).
Called DID in canonical E.164 format with leading + (e.g. +972740000000).
Empty for websocket sessions (no phone leg).
Lifecycle events
Session lifecycle events track the creation, answering, and termination of voice sessions. The first lifecycle signal depends on the session type:
- Inbound phone sessions begin with
session.created. - For an outbound call, the dial request’s
202 Acceptedresponse confirms admission andsession.createdidentifies the admitted Session asphone_out. - WebSocket Sessions follow the separate startup sequence under Session lifecycle.
All webhook deliveries are signed. See Signature Verification for how to validate event authenticity.
Every payload schema below also includes the required aud string, which
identifies the application that should receive the signed event.
session.created
Fired automatically when an inbound phone, SIP, or WebRTC call arrives, or when
an outbound phone call creates a Session. WebSocket Sessions begin with
websocket.connected or
websocket.failed instead.
Payload schema
session.answered
Fired exactly once when the call is answered. In terms of SIP response
codes, this event
corresponds strictly to a 200 OK from the called party — 183 Session Progress
is reported separately as session.early_media_started
and is not an answer.
When you issue an explicit answer command, a SIP 200 OK emits one session.answered event. An unanswered call emits session.ended with answered: false. The event never includes a success field. It includes operation_uuid only when the platform can uniquely associate the lifecycle transition with one pending call-control command. If present, use it for correlation; do not treat it as proof that the command alone caused the answer.
Payload schema
session.ended
session.ended is the single terminal event for every session. It fires exactly once per session — whether or not the call was ever answered. Use the answered boolean to distinguish the two cases:
- Post-answer termination:
answered: true, a realduration_seconds, andhangup_byidentifying the party who ended the session. - Pre-answer termination (dial never connected, rejected, or cancelled before answer):
answered: false,duration_seconds: 0, andhangup_byidentifying the responsible party where it can be attributed (e.g."system"for a ring/dial timeout) or"unknown"when the originator is indeterminate.
The platform also emits this event synthetically when it cleans up an orphaned session after approximately 120 seconds without media or signalling activity (see Timing limits). Synthetic hangups carry answered: false and duration_seconds: 0.
The event includes operation_uuid only when the platform can uniquely associate the termination with one pending call-control command. If the association is unavailable or ambiguous, the field is omitted. When present, use it for correlation; do not treat it as proof that the command alone caused the termination.
Payload schema
Example — answered:
Example — pre-answer termination:
Hangup causes
Treat any unrecognized hangup_cause value as failed — the bucket vocabulary may gain values in future.
Hangup by
hangup_by identifies which party ended the session. It is orthogonal to hangup_cause (the reason): a single cause may originate from either side.
Hangup description
Human-readable English summary of why the call ended. Presentation only — the wording may change without notice, so branch on hangup_cause / hangup_by, never on this string.
session.ringing_started
Fired when the far end indicates ringing on an outbound dial.
Payload schema
session.early_media_started
Fired when the far end starts sending early media (audio before answer, e.g. ringback or carrier announcements). This corresponds to SIP 183 Session Progress.
Early media indicates that audio is flowing before the called party answers.
session.answered fires when the called party returns SIP 200 OK.
Payload schema
Lifecycle sequences
Every session moves through a sequence of lifecycle events and ends with exactly one terminal session.ended, including unanswered calls (answered: false). The optional states (?) may or may not fire depending on far-end signalling.
A type: "websocket" Session has no phone leg and does not emit
session.created. websocket.connected
advances it from connecting to answered without emitting
session.answered. The Session stays active while the WebSocket connection is
open. If the connection fails or closes, the Session ends. A session.ended
event follows the corresponding WebSocket event. See Create
Session for the full contract.
Ordering & dedup. The delivery worker preserves emission order for events with the same session_uuid, including across retries, but the same event may be delivered more than once. Process those events in delivery order and dedupe using the verified Webhook-Id. Use timestamp as event-time metadata, not as a sorting key. Exactly one terminal session.ended is emitted per session.
Outbound call origination lifecycle
A POST /v1/sessions:dial produces the following outbound lifecycle events. Every event below carries a timestamp (RFC 3339 UTC string). For this session, preserve the delivery order guaranteed above.
Each intermediate event is emitted at most once and only advances forward. After
session.created, a dial may skip straight to session.ended
(for example, after an immediate rejection), or stop at any intermediate stage.
session.early_media_started reports SIP 183
early audio; session.answered reports a SIP 200 OK answer.
Timing limits
Three platform limits surface as ordinary session.ended events:
- Outbound dial cap (
ring_timeout_sec). Every outbound dial supplies this value explicitly. If an outbound dial is not answered within that window, the platform stops the attempt and emitssession.endedwithanswered: falseandhangup_cause: "timeout". This window is measured from when dialing begins (when the dial request returns202 Accepted), not from when the far end starts ringing — see Ring timeout timing. - Bridge limit (~120 seconds of inactivity). A session that becomes orphaned (no media or signalling activity) for approximately 120 seconds is cleaned up and emits a synthetic
session.endedwithanswered: falseandduration_seconds: 0. - WebSocket connection cap. A
type: "websocket"Session whose WebSocket server never connects within the platform’s connection window is ended and emitssession.ended.
These windows are platform behaviour and may change; treat the exact values as approximate and branch on hangup_cause, not on elapsed time you measure yourself.
Answering machine detection (AMD)
The platform does not classify an outbound answer as human or machine. session.answered fires on every SIP 200 OK, including when a voicemail system picks up.
The platform can’t tell you whether a human or a machine answered, so you must infer it from lifecycle and interaction signals. That makes voicemail detection necessarily heuristic. Common signals an application can combine:
- Long uninterrupted single-party audio after answer. Voicemail greetings are continuous monologues. If, after
session.answered, the far end produces a long stretch of audio with no DTMF (nodtmf.received) and no natural pause where a person would respond to your prompt, treat it as a likely machine. - No interactive response to a prompt. If you use DTMF collection and consistently get
digits.collectedwithstatus: "no_input"or"partial_then_timeout", the answering party may be a machine that cannot respond. - Early media that never becomes an answer. A
session.early_media_startedfollowed by a long delay and then a hangup withanswered: falsetypically reflects ringback or a carrier announcement, not a person — this is the unanswered case, distinct from a machine that actually answers.
For reliable detection, analyze the call audio with a dedicated
answering-machine-detection algorithm. Stream an active recording from its
live_url, and combine
that analysis with lifecycle events and interaction signals appropriate for
your traffic. If the result indicates a recording, delete the
session and pass the event’s session_uuid as {uuid}.
Triggered by
These events are produced by the following endpoints and lifecycle triggers:
session.created— automatic: fires on incoming calls and outbound calls created byPOST /sessions:dial.session.answered— automatic or answer command.session.ended— automatic (single terminal event, answered or not) or session deletion.session.ringing_started— automatic on outbound ring indication.session.early_media_started— automatic on SIP 183 progress (NOT an answer).