A call is one voice or video session — a web call, an inbound phone call or an outbound one. When an agent misbehaves, the call record is the single most useful thing in the product: it holds what was said, every tool the model called and what came back, the state the call ended with, and where the time and money went. Text conversations are not calls. They live under conversations.

Listing calls

GET /v1/calls returns calls newest first, in the standard page shape (items, has_more, limit, offset).
A list row carries enough to render without opening anything: 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.
cost is null until billing has computed it. A call in progress shows no cost at all — never a partial estimate. session.billing_status says which state it is in: pending, computed or unpriceable. Only computed has a cost object.
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.
Erasing a person’s last remaining call also erases what your agents remember about them. The next call from that number meets an agent that has never heard of them. This cannot be undone.
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.