Listing calls
GET /v1/calls returns calls newest first, in the standard page shape
(items, has_more, limit, offset).
status,
close_reason and its plain-English close_reason_label, duration_s,
cost, contact_key, recording.state, message_count, total_audio_ms,
and the analysis summary and outcome. It also carries plan — non-null
only when the call ran something other than an agent’s published version, with
members, inline and overridden — so a list can say Inline,
Overridden or Team · 3 without a second request.
One call, in full
GET /v1/calls/{session_id} returns everything. Start with snapshot: it
answers “did this call go well?” on its own. snapshot.issues is a list of
plain sentences naming every problem found, and snapshot.ok is true when
the list is empty.
Identity — what ran, and for whom
Timing and status
session.started_at, session.ended_at and session.duration_s (wall-clock
seconds; null while the call is still running). session.status is queued,
in_progress, completed, failed or canceled.
session.close_reason is the most specific fact about how it ended, and
session.close_reason_label is the same thing as a sentence — served rather
than mapped client-side, because there are around forty values written from
five places and a second copy of that list goes stale.
session.error is set only when the run died on an unhandled error, never on a
normal close.
The transcript
transcript is an ordered list of items. Every tool call and its result is in
there beside the speech, which is what makes it the first thing to read when a
model did something unexpected.
Each item stamps its own
agent_id and agent_version rather than inheriting
the session’s. The session names the agent that answered and never moves, so
on a call with a handoff these are the only way to tell who said what.
events beside it is the durable runtime trace — agent.ready,
tool.started / tool.ended, provider.failed / provider.recovered,
hold.started / hold.ended, transfer.briefing, screenshare.started,
session.error and more, each with a sequence number and a payload. It is
where you look when the transcript reads fine and the call still went wrong.
The final userdata
session.userdata is the state the call ended with — what its tools collected
and computed. It is the second thing to read after the transcript, because a
tool that wrote the wrong value usually shows up here before it shows up
anywhere else.
Post-call analysis results are deliberately not merged into it. See
call analysis.
Usage and cost
usage splits by kind: llm, stt, tts, realtime and avatar. A call
fills either llm + stt + tts or realtime — never both, because the agent
ran one pipeline or the other. Each LLM row carries purpose
(conversation or analysis) and priority, the flag that can make one line
cost double the standard rate.
cost carries provider_cost, platform_fee, total_charge, duration_s
and the full pricing_snapshot — the priced breakdown the biller froze, rates
included, never re-derived from today’s catalog.
See costs for what each line means and which half you
are actually charged for.
Latency, stage by stage
snapshot.response_latency is the number a caller feels: from the caller
finishing speech to the agent’s first audio. It carries avg_ms, p95_ms,
max_ms and samples — the p95 is what tells you whether a call was
sometimes slow, which a mean hides.
The stages that add up to it are averaged independently:
Anything left over is transport and orchestration. A realtime agent has no
separate STT or TTS leg, so those stages are null on one.
The thresholds ship on the response, so a client never hard-codes a line
the platform might move:
snapshot.slow_tool_calls counts every tool over the threshold;
snapshot.slow_tool_calls_in_silence counts the subset where nothing was being
spoken over it, which is the only version that is a defect. tool_calls,
tool_call_failures and unfinished_tool_calls sit beside them —
“unfinished” means the run died while the tool was still going, which is not
the same as one that reported a failure.
snapshot.pace describes how the call sounded rather than how fast the stack
was: longest_agent_turn_ms (the number that says your agent monologued),
talk_over_turns, and hold_ms — time parked on hold music during a bridged
transfer, which is inside the duration, inside the recording and inside the
bill, and silent.
snapshot.connection splits connection quality into agent and caller,
because a collapsed reading makes the caller’s own network look like a platform
fault.
Transfer
session.transfer is null on every call that never attempted one. When it is
set it is the ending, and it explains a short duration and a truncated
recording that would otherwise read as a call that failed early.
Both
duration_s and the recording measure our part of the call. The caller
may have stayed with the person for another fifteen minutes.
Recording and analysis
session.recording and session.screen_recording carry state and the sizes,
plus signed links on the detail view only. session.analysis carries the
summary, outcome and extracted fields. Both have pages of their own:
recordings and
call analysis.
session.content_deleted_at is the deletion receipt — when this call’s content
was erased, by your retention policy or by a delete. It is what tells an empty
transcript apart from a call where nobody spoke.
Aggregate counts
GET /v1/calls/stats gives range totals for the same window rules — start
defaults to 30 days ago, end to now, and agent_id narrows it.
Deleting a call
DELETE /v1/calls/{session_id} erases the call’s content. The call itself
stays, with everything an invoice is built from.
It returns
202: the work is scheduled rather than done, which is what stops a
request to erase two hundred thousand calls arriving as two hundred thousand
synchronous deletes. It normally completes within seconds. A call still in
progress is refused with a 409 — ask again once it has ended.
Retention runs exactly the same purge on a schedule. See
data retention and privacy.
To delete only the audio and keep the transcript, use
DELETE /v1/calls/{session_id}/recording instead — that one is immediate.
Debugging agents
Symptom first: what to open, which field answers it, and the fix.
Close reasons
Every value
close_reason can take, and what to do about each.