API ReferenceCalls
Get a call
GET /api/v1/calls/{callId} — full lifecycle for one call. Includes request snapshot, transcript, recordings, cost, per-turn latency, and raw events.
GET /api/v1/calls/{callId}Full detail for one call — everything zoxaAI persists about that call's lifecycle. The endpoint resolves recording storage keys to signed URLs at read time so you can play audio directly from the response.
Authentication
X-API-Key: zsk_...Path parameters
| Param | Type | Description |
|---|---|---|
callId | string | The callId returned by POST /call (e.g. call_4e4e571f8c9b3cf8e99d). |
Response
Returns HTTP 200 OK. The full CallDetail shape:
{
"callId": "call_4e4e571f8c9b3cf8e99d",
"transport": "outbound",
"pipelineSource": "agent",
"status": "completed",
"connectionStatus": "completed",
"endedReason": "user_hangup",
"agentId": "550e8400-...",
"agentName": "Sales bot",
"campaignId": null,
"campaignName": null,
"platformNumber": "+14155550100",
"customerNumber": "+14155559999",
"telephonyProvider": "twilio",
"providerCallSid": "CAxxxxxxx",
"callSource": "api_outbound",
"telephonySnapshot": { "provider": "twilio", "fromNumber": "+14155550100", "toNumber": "+14155559999", "credentialsFingerprint": "a1b2c3" },
"bindingId": null,
"requestSnapshot": {
"type": "outbound",
"callConfig": { "provider": "twilio", "phoneNumber": "+14155550100" },
"toNumber": "+14155559999",
"agentId": "550e8400-...",
"contextVariables": { "customerName": "Aman" }
},
"startedAt": "2026-06-16T10:30:12Z",
"endedAt": "2026-06-16T10:32:01Z",
"durationSeconds": 109,
"createdAt": "2026-06-16T10:30:10Z",
"updatedAt": "2026-06-16T10:32:01Z",
"cost": 0.0185,
"costBreakdown": { "llm": 0.012, "tts": 0.005, "stt": 0.0015 },
"configSnapshot": { "name": "Sales bot", "systemPrompt": "...", "llm": { "provider": "openai", "model": "gpt-5.4-mini" } },
"contextVariables": { "customerName": "Aman" },
"recordings": [
{ "url": "https://signed-url...", "format": "wav", "storage_backend": "minio" }
],
"transcript": { "url": "https://signed-url..." },
"logs": { "telephony_status_callbacks": [/*...*/] },
"usageInfo": { "llm_tokens_in": 320, "llm_tokens_out": 410, "tts_chars": 1820, "stt_seconds": 47 },
"analysis": { "summary": "User confirmed appointment for next Tuesday." },
"latencySummary": { "userToBotMs_p50": 850, "userToBotMs_p95": 1320 },
"tags": ["api", "outbound"],
"turns": [/* per-turn rows, see CallTurn below */],
"events": [/* fine-grained pipeline events, see CallEvent below */]
}Top-level fields
Same as list item plus:
| Field | Type | Description |
|---|---|---|
connectionStatus | string | null | Did the call connect — completed, or the reason it never did. See connectionStatus values. |
endedReason | string | null | Why a connected call ended (user_hangup, agent_hangup, silence_timeout, max_duration, voicemail, transferred, pipeline_error, cancelled, unknown). null when the call never connected. See endedReason values. |
providerCallSid | string | null | Provider's own call id (Twilio CallSid, Vobiz call_uuid). Use it to cross-reference in the provider console. |
telephonySnapshot | object | null | Provider/number/fingerprint metadata. Credentials are stripped server-side — only the fingerprint remains. |
bindingId | int | null | Set on inbound API-first calls that matched an inbound binding. |
requestSnapshot | object | null | The original API request body (auth removed). For dashboard/WebRTC calls, a composed view of agent + contextVariables + provider info. The unified "Config Snapshot" panel in the dashboard reads this. |
configSnapshot | object | The resolved Agent Config that drove the call — prompt, greeting, llm/stt/tts, tools, call guards, etc. Provider auth is NOT included (telephony credentials live separately on telephonySnapshot and are redacted; LLM/TTS/STT API keys are resolved server-side and never serialized into the snapshot). |
contextVariables | object | null | Variables substituted into the snapshot. |
recordings | array | null | One entry per recording. url is a signed CDN URL valid for ~1 hour. |
transcript | object | null | { url: signed-url }. Format depends on storage backend; JSONL is standard. |
logs | object | null | Pipeline-level logs (telephony callbacks, etc.). |
usageInfo | object | null | Token counts, TTS chars, STT seconds. |
analysis | object | null | Post-call analysis when enabled. Includes summary and any custom extraction. |
latencySummary | object | null | Aggregate latency stats across all turns. |
turns | array | Per-turn latency + token breakdown. See below. |
events | array | Fine-grained pipeline events (turn start/end, interruptions, tool calls). See below. |
turns[] — per-turn breakdown
One row per conversation turn, ordered by turnNo.
| Field | Type | Description |
|---|---|---|
turnNo | int | Turn number, starting at 1. |
startedAt / endedAt | ISO 8601 | null | Turn boundaries. |
durationMs | int | null | End − start. |
wasInterrupted | bool | True if the user spoke over the agent. |
userToBotMs | int | null | Round-trip latency — user stops speaking → bot first audio. |
llmTtfbMs | int | null | Time-to-first-byte from the LLM. |
ttsTtfbMs | int | null | TTFB from the TTS provider. |
sttTtfbMs | int | null | TTFB from the STT provider on this turn. |
textAggMs | int | null | Time spent aggregating tokens into a sentence before TTS. |
tokensIn / tokensOut | int | null | LLM tokens this turn. |
tokensCacheRead / tokensCacheCreate / tokensReasoning | int | null | Detailed token accounting where the provider reports it. |
ttsCharsInput | int | null | Characters sent to TTS this turn. |
botSpeakingMs | int | null | How long the bot's TTS played. |
llmModel / ttsVoice / sttModel | string | null | Models used (lets you compare cost/latency across providers). |
toolCallCount | int | Function calls invoked this turn. |
toolCallTotalMs | int | Total time spent inside tools. |
payload | object | Raw turn metadata (transcript snippet, etc.). |
events[] — fine-grained pipeline events
Ordered by tMs (milliseconds since call start). Use for visualizing the timeline.
| Field | Type | Description |
|---|---|---|
tMs | int | Event timestamp in ms since call start. |
kind | string | Event type. Examples: turn_start, turn_end, interruption, tool_call_start, tool_call_end, error. |
processor | string | null | Which pipeline processor emitted it. |
turnNo | int | null | Associated turn, if any. |
payload | object | Event-specific data. |
Examples
curl https://dashboard.zoxa.ai/api/v1/calls/call_4e4e571f8c9b3cf8e99d \
-H "X-API-Key: zsk_..."const res = await fetch(
`https://dashboard.zoxa.ai/api/v1/calls/${callId}`,
{ headers: { "X-API-Key": "zsk_..." } },
);
const call = await res.json();
console.log(call.connectionStatus, call.endedReason, call.durationSeconds, call.cost);
if (call.recordings?.[0]?.url) {
// signed URL valid ~1h
playAudio(call.recordings[0].url);
}import httpx
call = httpx.get(
f"https://dashboard.zoxa.ai/api/v1/calls/{call_id}",
headers={"X-API-Key": "zsk_..."},
).json()
print(call["connectionStatus"], call["endedReason"], call["durationSeconds"], call["cost"])
for turn in call["turns"]:
print(turn["turnNo"], "user→bot:", turn["userToBotMs"], "ms")Errors
| Status | detail | When |
|---|---|---|
400 | "No organization selected" | Auth missing org context. |
401 | auth errors | See Errors. |
404 | "Call not found" | callId doesn't exist in your organization, or belongs to another org. |
{ "detail": "Call not found" }404 covers both "id doesn't exist" and "exists in a different org" — your code treats them the same.
Notes on behavior
- Signed URLs expire. Recording / transcript URLs are signed at read-time and expire within ~1 hour. Fetch fresh URLs by re-calling this endpoint.
- Snapshot redaction is unconditional.
telephonySnapshot.credentialsandrequestSnapshot.callConfig.authare stripped server-side before serialization — even in debug mode, you'll never see raw provider tokens here. turnsandeventscan be empty for calls without per-turn observability data.
Related
GET /calls— list with filters- WebSocket protocol — live-call alternative to fetching history
- Webhook events — push delivery of the same lifecycle data