GET /v1/calls/{session_id} — field reference on calls. Read, in order: snapshot.issues (every detected problem as {kind, message}), transcript (speech with every tool call and result inline), session.userdata (the state the call ended with), then events (the runtime trace) when the transcript looks fine but the call still went wrong.
Find calls worth opening by outcome (from call analysis) or close_reason:
endings from GET /v1/observability.
The agent said something wrong or made it up
Look at thefunction_call_output items above the bad line.
- A tool returned the wrong value → fix the tool, and reproduce it with
POST /v1/tools/{tool_id}/run(testing and publishing). - Nothing in the prompt or any tool result contained the fact → the model guessed. Give it a source, or tell it what to say when it does not know (prompting).
- A template resolved to nothing → check
session.userdataand the call’svars(variables).
The agent was slow
Look atsnapshot.response_latency.p95_ms (slow above snapshot.slow_response_threshold_ms, 3500) and the largest entry in snapshot.response_stages:
snapshot.slow_tool_calls_in_silence counts tools over 8 seconds with nothing spoken over them. Fix with a say operation before the slow step or long_running_task on the tool (speech operations, tools). A poor snapshot.connection.caller is the caller’s network, not your stack.
The agent called the wrong tool or sent bad arguments
Thefunction_call item’s metadata.data shows the tool name and the arguments the model chose. The model only knows a tool through its description and its json_schema property descriptions.
- Say when to use the tool, when not to, and each argument’s format (schema and description).
- Split a tool that does several things; remove overlaps between tools (or between a tool and a prompt section) that answer the same question.
- Put ordering and preconditions (“confirm the spelling before calling
create_ticket”) in the system prompt.
agent_start_failed never got to choose: two attached tools exposed the same name; session.error says which.
A tool failed
Thefunction_call_output has data.is_error: true and data.output is what the model was told; events has the tool.started/tool.ended pair. Counters: snapshot.tool_calls, tool_call_failures, unfinished_tool_calls (the call ended while it ran — not a failure).
- If the caller heard something misleading, fix the tool’s
on_errorhandling (tool failures). - Reproduce it with
POST /v1/tools/{tool_id}/run. - A missing
{{secrets.NAME}}raises rather than substituting empty (secrets).
snapshot.provider_failures / provider_recoveries are model-provider failovers, a separate problem (model fallbacks).
The agent hung up early
session.close_reason says who ended it:
If it was the agent, find the
end_call that fired and read the condition before it; a loose branch is the usual cause (end call). If the caller hung up during the greeting, analysis skips the call as too_short and it does not count against your success rate. Every value: close reasons.
The call never connected
The close reason’s bucket says whose problem it is:
No call record at all means the call never reached an agent: check the number’s
readiness is live and a published voice agent is assigned (phone numbers).
Someone was cut off mid-sentence
- The agent stopped for noise. High
snapshot.false_interruptionswith lowfalse_interruptions_resumedmeansresume_false_interruptionis off (turn handling). - The audio path degraded.
snapshot.noise_cancellation_failed: truemeans the call ran on raw audio (audio processing);snapshot.connectionpoororloston the caller side is their network. - The agent monologued. High
snapshot.pace.longest_agent_turn_msandtalk_over_turns: shorten replies in the prompt. - The whole call ended at three hours. Every call is capped at three hours.
The agent forgot a returning caller
config.conversation.contexton the answering agent defaults tonone(start clean). Setsummaryortranscript(conversation memory).- Both calls must have the same
contact_key;conversation_idchanges per call. summaryreads post-call analysis summaries; if analysis or its summary was turned off after publishing, summaries stop.initialize_userdata(defaulttrue) carries the person’s userdata intosummaryandtranscriptcalls.- Erased history (
session.content_deleted_at, or retention) is gone (data retention).
Costs are higher than expected
Start with thecost section of GET /v1/observability:
Also: voicemail is billed as an answered call;
snapshot.pace.hold_ms (transfer hold) is inside the duration and bill; each analysis backfill is another model call on your key. Your credit balance only falls by the platform fee — model spend goes to your providers. See costs.
The call record itself looks wrong
Calls
Every field this page reads.
Close reasons
Every ending and its fix.