What gets stored
The session record (status, timing, caller attribution, name,end_user_id, and metadata) and the conversation transcript are always kept for production sessions. Audio recording is a per-agent choice, on by default: set conversation.record_audio in the console under your agent’s Settings or in the conversation section of the config API, with a per-session override on the session request. Recording off means there is no audio to download.
The setting doesn’t affect the live call: transcript events still stream to connected clients in real time.
List sessions
GET /v1/agent/sessions returns session facts, newest first. The list is intentionally thin (no transcripts or tool details) so it stays fast at any volume.
caller_number and dialed_number are set for phone sessions only (E.164) and are null for web sessions. sip_call_id is the SIP Call-ID of the call’s INVITE. It is null on web sessions and on outbound calls that were never answered. See Match calls with your SIP records. call_sid is the carrier’s id of the call: Twilio’s CallSid on an inbound call through a Twilio number, the same value the inbound call webhook sends. It is null on other sessions. name is the display name you passed when creating the session (null when omitted), end_user_id is the end-user identifier you passed (null when omitted), and metadata echoes back whatever you attached when creating the session.
Status and end reason
status says whether a conversation happened: completed means the caller and an agent were both on the call and it is over (it is billed even if the runtime ended it), failed means no conversation took place (nobody connected, or the caller connected and no agent ever joined), active means it is in progress, pending means the session was created and no one has connected yet, and unknown means the platform temporarily lost track of a live call. On a failed session where the caller did connect, started_at, ended_at and duration_seconds describe how long they waited; nothing is billed.
end_reason says why it ended. It is null while a session is live, for a short moment after it ends, and on sessions that predate the field.
The same values appear on the
call.ended webhook and in the dashboard, so the three surfaces never disagree about a session.
Filters
pending sessions (a session was created but no participant ever connected)
are excluded by default. Pass status=pending explicitly to see them. Preview
calls made from the Builder never appear in this API.Pagination
Two modes, mutually exclusive (combining them returns400):
page_size defaults to 30, maximum 100; values out of range are rejected. In cursor mode total is null unless you pass include_total=true. Page mode is capped at an offset of 100,000 rows (requests beyond it return 400), and every page response still includes next_cursor, so you can switch to cursor crawling from any page for deep scans.
Get a session
GET /v1/agent/sessions/{session_id} returns the session facts plus a single items timeline: messages and tool activity merged in the order they actually happened.
API (curl)
Item types
Details worth knowing:
- Role vocabulary: the history API’s
assistantis the same speaker the SDK’s live events callagent. - Order: items are sorted by
created_atascending; atool_result’s timestamp is its completion time, so long-running tools appear where they actually finished, with messages in between. Items sharing a timestamp ordermessage, thentool_call, thentool_result. tool_source: where the tool ran, one ofclient,webhook,builtin(platform tools such as call transfer and hang-up),mcp(tools from a connected MCP server),background(work the agent delegated to a background task), orunknown(calls recorded before source attribution). Treat it as an open set. See Tools.- Payloads:
inputandoutputare JSON strings, symmetric with the live SDK events, so one parser covers both.outputanderrorare stored up to 256 KB; beyond that the text is cut andoutput_truncatedistrue. - Mid-call queries: you can fetch an
activesession; you get the items persisted so far, andanalysisisnulluntil post-call analysis completes. - A
tool_callwithout a matchingtool_resultmeans the execution never resolved. rejected_by: on atool_result,"parameter_validation"when the tool validates its parameters and refused the call before it ran, sostatusisfailedanderroris the rejection the model was shown.nullfor every other result, including a tool that ran and failed. New values may be added.
LLM endpoint errors
Sessions on a custom LLM also carryllm_endpoint_errors. It lists each failed generation of your endpoint, oldest first. It is empty when the agent runs on a platform model or your endpoint never failed.
turn_idis the turn the failed reply belonged to. It uses the same numbering asmessageitems.typeishttp_error,timeout,connection_errororinvalid_response. See Failure behavior. New types may be added.reasonis set onconnection_errorandinvalid_responseonly. New reasons may be added.connection_error:dns_failed,connection_refused,connection_reset,tls_failed,address_not_allowed(an address the platform does not call) orunknown.invalid_response:not_event_stream(a streamed request got something else back),stream_incomplete(the stream ended with nofinish_reason, usage or output),empty_reply(no text and no tool call) orunknown.
status_codeis set onhttp_erroronly.response_bodyis the start of what your endpoint sent back, onhttp_errorandnot_event_streamonly. It is at most 300 characters, and your API key is masked. It isnullwhen the body was empty.duration_msis the time from the request to the response or the failure. It isnullwhen it was not measured.consecutive_failurescounts failures in a row. The agent hangs up at the third.
Transfer items
When the agent transfers a phone call, each attempt becomes onetransfer item, including attempts that failed. Its call_id is the call_id of the _transfer_call tool_call that started it, and its created_at is when the attempt started, which is where it sits in the timeline. Every field is always present and is null when there is no value.
A warm transfer’s consult call is also recorded when the session records audio. Its tracks are under
transfers in the recording response, where transfer_id is the item’s call_id.
After the transfer
On a successful transfer the session ends withend_reason escalated, whatever happens on the call afterwards. The three transferred_call_* fields describe that call between the caller and your team member.
transferred_call_end_reason is one of:
When the values arrive depends on the mode:
- Warm: when the call ends. They are
nullwhile the caller and your team member are still talking. - Cold: the platform leaves the call at the handoff, so the end time and duration come from the carrier’s call record. They appear up to about an hour after the call ends. Who hung up is never known, so
transferred_call_end_reasonstaysnull. - Cold transfers on imported SIP numbers: the call record stays with your carrier, so the three fields stay
null.
transferred_call_ended_at is null, the other two are null as well. A referred item with a duration of 0 means the carrier’s record shows the destination never picked up, for example because the caller hung up while it was still ringing.
Correlate with live events
call_id is the same identifier your client receives in the SDK’s toolCallStarted / toolCallCompleted / toolCallFailed events. Log it live, and you can align in-call UI with the post-call record: jump from a tool chip in your interface straight to the matching tool_call / tool_result pair in history. See the Web SDK.
Download recordings
GET /v1/agent/sessions/{session_id}/recording returns the recording status and signed download URLs: one audio track per speaker, so you can play or process the agent and the user separately. For sessions that never recorded (recording turned off, or no audio produced), the endpoint returns 404.
API (curl)
session_id and request fresh URLs when you need the audio. For the same reason, recordings are not embedded in the session detail response.
Access semantics
- Your API key sees every production session across your team’s workspaces, from all sources.
- A session that doesn’t exist, or belongs to another team, always returns
404, never403. The recording endpoint additionally returns404for a session that exists but was never recorded.
Going further
Post-call analysis
What the embedded
analysis object contains and how to configure it.Webhooks
Push instead of poll: get notified when sessions end.
Web SDK
The live
toolCall* events that share call_id with history.Inbound calls
Where
caller_number and dialed_number come from.
