Skip to main content
Every production session your agents handle (from the API, the console, public agents, or phone calls) is queryable over REST: a lightweight list for browsing, a merged timeline of messages and tool activity per session, and per-speaker recordings when the agent records audio. You can query a session while the call is still in progress. This is a server-side API: authenticate with your API key. The client SDKs deliberately expose no history interface; fetch history from your backend and pass it to your frontend as needed.

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.
Recording defaults to on. Call-recording laws vary by jurisdiction. Make sure callers are informed and consent where required before going live.

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.
Each row carries the session facts:
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 returns 400): 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 assistant is the same speaker the SDK’s live events call agent.
  • Order: items are sorted by created_at ascending; a tool_result’s timestamp is its completion time, so long-running tools appear where they actually finished, with messages in between. Items sharing a timestamp order message, then tool_call, then tool_result.
  • tool_source: where the tool ran, one of client, 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), or unknown (calls recorded before source attribution). Treat it as an open set. See Tools.
  • Payloads: input and output are JSON strings, symmetric with the live SDK events, so one parser covers both. output and error are stored up to 256 KB; beyond that the text is cut and output_truncated is true.
  • Mid-call queries: you can fetch an active session; you get the items persisted so far, and analysis is null until post-call analysis completes.
  • A tool_call without a matching tool_result means the execution never resolved.
  • rejected_by: on a tool_result, "parameter_validation" when the tool validates its parameters and refused the call before it ran, so status is failed and error is the rejection the model was shown. null for every other result, including a tool that ran and failed. New values may be added.

LLM endpoint errors

Sessions on a custom LLM also carry llm_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_id is the turn the failed reply belonged to. It uses the same numbering as message items.
  • type is http_error, timeout, connection_error or invalid_response. See Failure behavior. New types may be added.
  • reason is set on connection_error and invalid_response only. 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) or unknown.
    • invalid_response: not_event_stream (a streamed request got something else back), stream_incomplete (the stream ended with no finish_reason, usage or output), empty_reply (no text and no tool call) or unknown.
  • status_code is set on http_error only.
  • response_body is the start of what your endpoint sent back, on http_error and not_event_stream only. It is at most 300 characters, and your API key is masked. It is null when the body was empty.
  • duration_ms is the time from the request to the response or the failure. It is null when it was not measured.
  • consecutive_failures counts failures in a row. The agent hangs up at the third.
items is a discriminated union on type, and it evolves additively. Ignore item types and fields you don’t recognize. New modalities and item kinds will appear without a version bump.

Transfer items

When the agent transfers a phone call, each attempt becomes one transfer 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 with end_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 null while 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_reason stays null.
  • Cold transfers on imported SIP numbers: the call record stays with your carrier, so the three fields stay null.
When 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)
Signed URLs are short-lived: store the 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, never 403. The recording endpoint additionally returns 404 for 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.