zoxaAI
Homepage

Call History

The Calls page — list view filters and columns, plus the per-call detail page with Transcript, Logs, Latency, Insights, Cost, and Config tabs. Every metric defined with what it means and how to use it.

Every call made through zoxaAI — outbound API dial, inbound binding, browser WebRTC test, campaign call — lands in Calls. The list page is your fleet view; clicking any row opens a deep per-call detail page with six tabs of observability.

This page walks through every UI element with what it shows, where the data comes from, and how to interpret it.


The Calls list page

Sidebar → Calls. Default view is the last 30 days of calls, newest first.

Filters (top bar)

Each filter resets pagination to page 1.

FilterOptionsNotes
Time rangeLast 24 hours, Last 7 days, Last 30 days, All timeSets from_date on the request.
Call IDtext inputExact match on callId. Press Enter to apply.
TypeInbound / Outbound / Websocket / Web (or All)The transport that delivered the call. WebRTC calls are stored as web.
Call sourceapi_outbound, api_inbound, dashboard_outbound, dashboard_inbound, webrtc, websocket, unknownWhere the call was initiated from. Useful to separate dashboard test-dials from production traffic.
Phone numbertext inputSearches platformNumber OR customerNumber. Press Enter to apply.

Columns

The Columns dropdown (top right of the table) lets you toggle each column. Preferences persist across sessions per user.

Default columns:

ColumnSource fieldWhat it shows
Call IDcallIdTruncated to first 8 + last 4 chars, with a Copy button on hover.
AgentagentNameBadge A followed by the agent name. — if no name is set.
Platform NumberplatformNumberThe business number — outbound caller-ID for outbound calls, dialed number for inbound.
Customer NumbercustomerNumberThe counterparty number.
TypetransportColored badge: Inbound (emerald), Outbound (blue), Websocket (purple), Web (zinc).
ConnectionconnectionStatusDid the call connect? Complete (green) on success, otherwise why it never connected. Colored badge — see outcome badges below.
Ended ReasonendedReasonWhy a connected call ended. — for calls that never connected. Colored badge — see outcome badges below.
TimestartedAtRelative ("3m ago"). Hover for the full ISO timestamp.
DurationdurationSecondsFormatted Xm Ys or Xh Ym. This is the call window (start → end), not the audio duration.
CostcostTotal cost in USD, 2 decimals.

Optional columns (toggle in the Columns dropdown):

ColumnSource fieldWhat it shows
ProvidertelephonyProvidertwilio, vonage, vobiz, etc.
CampaigncampaignNameCampaign that initiated the call, if any.
Tagstags[]Up to 3 tag badges; +N if more.
Created AtcreatedAtWhen the call row was created. Differs from startedAt for queued campaign calls.

Outcome badges

Call outcomes are two separate dimensions, each with its own badge:

  • Connection — did the call connect? Every terminal call gets exactly one value.
  • Ended Reason — why did the connected conversation end? Only exists once the call actually connected; — otherwise.

Connection badge colors:

Badge colorValuesMeaning
🟢 greenComplete (completed)The callee answered / the web client joined — the conversation started.
🟠 amberMissed (no answer), Busy, Rejected, Cancelled, No participant, Concurrency limit, Insufficient balanceOperational — the callee or a platform limit stopped the call. Not an error; investigate only if frequent.
🔴 redInvalid number, Unreachable, Connection failed, Dial failed, Initiation failed, Missing credentials, plus API rejections (Schema rejected, Config rejected, Agent not found, Invalid credentials, Tool rejected, ...)Something is wrong with the number, network, credentials, or request. Open the detail page — the rejection payload is preserved.
⚪ zincanything else (e.g. telephony_<raw> passthroughs)A provider status zoxaAI doesn't map — shown raw rather than hidden.

Ended Reason badge colors:

Badge colorValuesMeaning
🟢 greenUser hangupThe user ended the call — the normal outcome.
🟡 yellowAgent hangupThe agent ended the call (endCall tool).
🟠 orangeSilence timeout, Max duration, VoicemailLimit-driven endings — worth tuning if frequent.
🔵 blueTransferredHanded off to a human via transferCall.
🔴 redPipeline errorThe pipeline crashed mid-call. Check the Logs tab.
⚪ zincCancelled, Ended (unknown)System-cancelled mid-call, or ended without attribution (unknown — a rising count is a bug signal).

Sorting

Click a column header to sort. Active sort shows an arrow (↓ desc, ↑ asc). Sortable columns: Time (ended_at), Duration (duration_seconds), Cost (cost), Created At (created_at). Default is ended_at desc.

Pagination

Bottom right: page-size options (25 / 50 / 100), page indicator, prev/next buttons. Counts: "Showing X–Y of Z calls."

Export to Excel

The Export button (next to Refresh) downloads the calls currently shown in the table — the same rows your active filters, sort, and page-size limit produce — as an .xlsx file.

The export fetches the full detail for each call, so a progress popup appears while it runs; the ✕ button cancels it (no file is produced). Each row includes:

  • Every tracking ID — call ID, provider call SID, recording URL (the permanent CDN link when CDN storage is configured), agent ID, campaign ID, binding ID.
  • Call metadata — platform / customer number, provider, mode, type, call source, start, end, duration, status, connection, ended reason, cost, tags.
  • The full transcript — exactly as the Transcript tab renders it: user/agent messages with timestamps, plus every tool call interleaved chronologically with its status, duration, outcome, arguments, and raw result.

If a call's details can't be loaded, its row is still exported with the list-level fields and the transcript marked unavailable — a toast tells you how many were affected.

Behind the scenes

Every filter, sort, and page change maps to query params on GET /api/v1/calls. You can replicate the same view from the API — useful for piping calls into your own BI / reporting tool.


Per-call detail page

Click any row to open the call detail. The page has six tabs plus a header block with call-level metadata.

Shown above the tabs:

  • Call ID with Copy button.
  • Transport badge — same color as the list column.
  • Status badge — pending, active, completed, or error.
  • Ended Reason badge — the connected-call ending (User hangup, Agent hangup, ...). Hidden when the call never connected (no ending to show) — the connection outcome lives on the Call Info card below.

Metadata cards (below the header, above the recording player)

Two cards side by side:

Call Info card (left):

RowWhat it is
Call sourceHuman label for how the call started — API outbound, Dashboard inbound, WebRTC, WebSocket, etc. Maps to the callSource field.
Platform noBusiness number.
Customer noCounterparty number.
ProviderTelephony provider (twilio, vobiz, …).
Call connectionThe connectionStatus badge — Complete when the call connected, otherwise why it never did (Busy, Missed (no answer), Dial failed, a rejection reason, ...). Dialer calls use the dialer's words: Answered, No answer and Declined. Same colors as the list column. (The provider's own call SID is still on the API response as providerCallSid and in the Excel export.)

Activity card (right):

RowWhat it is
ModeAgent · <name>, with (campaign-name) appended in muted text if the call was part of a campaign.
StartstartedAt. If missing, derived from endedAt - durationSeconds.
EndendedAt.
DurationAudio recording duration if available; falls back to provider billed X or call window X with the source labeled in the tooltip. Often differs from the row Duration in the list because recording excludes pre-/post-call overhead.
Ended reasonThe connected-call ending as a plain label — "User hangup", "Agent hangup", "Silence timeout", etc. — when the call never connected (check Call connection on the Call Info card instead).

Recording player

Stereo waveform with play/pause, volume, scrubbable timeline. Spacebar toggles play/pause. Clicking any message in the Transcript tab seeks the recording to that timestamp.

If recording wasn't enabled or has not finished uploading, the player area is hidden.


Tabs

1. Transcript

The conversation as it actually happened, rendered as chat bubbles. Two views:

  • Rendered (default) — Chat-style timeline. User messages on the left, agent messages on the right. Each message has its wall-clock timestamp.
  • Raw — JSON dump of the transcript object with a Copy button. Use this when reproducing a bug or piping data into another tool.

Tool calls appear as amber boxes between messages, with:

  • Status badge: 🟢 completed, 🔴 failed, 🟡 pending.
  • Function name, JSON args, JSON result.
  • Latency chip on completion.

Per-message latency chips (shown on agent messages):

  • 🟠 STT — time-to-first-byte for transcription.
  • 🟡 LLM — time-to-first-byte for the LLM's first token.
  • 🔵 TTS — time-to-first-byte for the first audio frame.

Click any message or tool box to seek the recording to that timestamp (blue highlight on hover).

2. Logs

The raw event stream — every meaningful frame the pipecat pipeline emitted. Used for debugging.

Controls (top bar):

  • Search — full-text match on event body and JSON payload.
  • Level — All levels / Info / Warn / Error.
  • Category — auto-populated from event categories (STT, LLM, TTS, Execution, Reliability, …).
  • Time mode toggle — offset (ms since call start, default) or wall (HH:MM:SS.mmm wall-clock).
  • verbose checkbox — hides low-signal events like every interim STT transcript and per-frame token-usage micro-events.

Table columns: Time · Level · Category · Event (short summary).

Expand a row to see the event in detail — Fields tab (filterable table) and JSON tab (raw payload with Copy).

Common event kinds you'll see:

EventWhat it means
startup_completePipeline finished initializing. The TTFB shown is how long startup took.
llm_first_token / tts_first_audio / stt_first_transcriptPer-stage first-byte latencies for a turn.
llm_token_usageLLM prompt/completion token counts for a turn. Drives cost.
tool_call_started / tool_call_completed / tool_call_failedFunction-call lifecycle. The failed kind carries the error message.
turn_started / turn_endedA turn boundary. interrupted: true on ended means the user cut off the bot.
interruptionThe user started speaking while the bot was talking.
errorUnhandled exception. Almost always paired with endedReason=pipeline_error.

3. Latency

Where time went on every turn.

Summary tiles (top row):

TileMeaning
DurationTotal call duration.
TurnsHow many turns the call had.
InterruptedHow many turns the user cut off the bot.
ErrorsHow many error events fired.
StartupHow long the pipeline took to be ready for the first user input.
Total LLM costSum of every turn's LLM cost.
Avg turn latencyP50 user-finished-talking → bot-started-replying delay across turns.

Average TTFB breakdown — A horizontal stacked bar averaged across all turns. Color-coded so you can see at a glance which stage dominates: 🟠 STT, 🟡 LLM, 🔵 TTS, ⚪ text aggregation overhead.

Per-stage cards (3 columns):

  • STT (orange) — avg time the transcriber took to emit the first word of the user's utterance.
  • LLM (yellow) — avg time the LLM took to produce its first response token.
  • TTS (blue) — avg time the voice took to produce its first audio frame.

Per-turn table (expandable rows):

ColumnWhat it shows
Turn #Sequence number.
DurationFull turn (user start → bot finishes).
Interruptedyes badge if the user cut off this turn.
EndpointingUser-finished → bot-started delay. This is the perceived "thinking time" by the caller.
TranscriberSTT TTFB (ms).
LLMLLM TTFB (ms).
VoiceTTS TTFB (ms).
TokensLLM tokens in / out.
ToolsTool-call count + total ms spent in tools.
LLM $Per-turn LLM cost.

Expanding a row shows the user transcript, the bot transcript, the models that ran, and the raw LatencyBreakdown payload.

Tuning latency from this tab

  • Endpointing too high consistently → tune the transcriber's endpointing knobs (e.g. Soniox endpointSensitivity, Flux eotThreshold, Cartesia turnEndThreshold).
  • One stage dominates → switch model. E.g. swap gpt-4.1 → gpt-4.1-mini for cost-sensitive paths; pick a lower-latency voice model like eleven_flash_v2_5 or sonic-3.6 for snappier TTS.
  • LLM TTFB highly variable across turns → switch to a lower-variance model, e.g. qwen-flash or a smaller OpenAI model.

4. Insights

The "is this call healthy?" dashboard. Seven sub-panels, computed from turns + events.

Pacing & Interruptions

Timeline view of who was talking when.

  • Pacing strip — Amber bands for user speech, cyan bands for bot speech. Click anywhere to seek the recording.
  • Interruption strip — Red ticks where the user interrupted the bot. Click to jump to that moment.
  • Bottom legend — Interruption rate (%) and total interruption count.

What to look for:

  • Long cyan, almost no amber → bot is talking too much. Tighten the system prompt or lower max tokens.
  • Lots of red ticks → bot is too slow or too long-winded. Tighten the prompt, speed up turn endpointing, or set stt.interruptionMinWords to 2+ to filter accidental cuts.

Health score (Hero Verdict)

Big circular score (0–100) with a one-sentence verdict:

  • 🟢 70+ green — call was healthy.
  • 🟡 40–69 amber — degraded; one or two issues.
  • 🔴 Below 40 red — investigate.

Scoring (starts at 100, deducts):

  • −25 for any error event.
  • Up to −15 based on P95 user-to-bot latency (proportional to the gap from 0 → 2500 ms).
  • −10 × interruption rate (0..1).
  • −10 if endedReason is pipeline_error or unknown (the connected conversation itself went wrong).
  • −10 × tool-failure rate.
  • +5 bonus if LLM cache-hit ratio > 30%.

Also shown:

  • Summary — analysis.summary.
  • Sentiment badge — analysis.sentiment.label.
  • Resolution badge — analysis.resolution.
  • Topics — analysis.topics[].
  • Action items — analysis.action_items[].

Conversation

How the talk time was split.

  • Talk-time pie chart — User % / Bot % / Silence % of total call duration. Silence = duration - user_speaking_ms - bot_speaking_ms.
  • Per-turn turn-duration bar chart — Distribution of turn lengths.
  • Speech metrics (4 tiles):
    • User WPM — User words per minute (computed via chars ÷ 5 ÷ user-speaking-seconds).
    • Bot WPM — Bot words per minute, same method.
    • Avg bot words / turn — How verbose the agent is per response.
    • Turn cadence — Average turn duration in ms.

Tuning: if Bot WPM is too low/high for your brand, change TTS speed (e.g. Sarvam speed: 1.1 for slightly faster delivery). If avg bot words/turn is too high, tighten the system prompt with Be concise.

Latency (Insights version)

A summary view of the same data as the Latency tab, but with one extra:

  • Service contribution stacked bar — A 100% bar split into STT / LLM / TTS / text-agg, showing which stage owns the most latency budget.
  • Slowest turn callout — "Turn #N took 2.1s — LLM was slowest (1.2s)". Click to seek.
  • LLM variability (σ) — Standard deviation of LLM TTFB. High σ = unstable LLM; consider a different provider/model.

Cost (Insights version)

If costBreakdown is populated, you get:

  • Cost stack bar — % of total cost spent on LLM / TTS / STT / telephony.
  • Per-category card grid:
    • LLM card: provider, model, prompt tokens, cached tokens, completion tokens, $/1M tokens, total cost.
    • TTS card: provider, model, character count, $/1k chars, total cost.
    • STT card: provider, model, audio seconds, billed minutes, $/min, total cost.
    • Telephony card: provider, billed minutes, $/min, total cost.
  • Per-turn LLM cost step chart — cumulative cost as the call progressed.
  • Cost metrics (4 tiles):
    • Cost per minute — total ÷ duration × 60.
    • Cost per turn — total ÷ turn count.
    • Projected 1k calls — total × 1000. Useful for back-of-envelope unit-economics.
    • Cache hit ratio — % of LLM input tokens served from prompt cache.

Tools

Only shown when tools were called.

  • Stat tiles: Total tool calls (succeeded + failed), success rate %, most-called tool.
  • Per-tool latency table — name · call count · P50 latency · max latency. Sorted by call count desc.
  • Tool time % of response — share of total bot response time spent waiting on tools.

Tuning: if a tool's P50 is high but the tool is hot, reduce its timeout_ms and prefer caching upstream.

Reliability

Four tiles plus tags.

TileMeaning
ErrorsCount of error events. Green 0 ✓ if none, red if any.
First bot responseTime from call connect to first bot_speaking_started. Includes pipeline startup; high values point to slow VAD or first-message generation.
Hangup graceTime between last bot stop and pipeline finished — how long cleanup took.
Interruption rate% of turns the user cut off.

Tags panel — All tags[] shown as outline badges.

Config trace

Expandable JSON panels:

  • Config — two views, switched with the Resolved / Raw toggle. Resolved is the exact configuration the pipeline ran with (defaults filled, variables substituted). Raw is the literal body of the request that configured the call, with provider auth redacted — the POST /api/v1/call dial or registration body, or the latest binding update if you changed the binding via PATCH /telephony/inbound/bindings/{id}. Available for API calls (outbound and inbound) and rejected attempts. Dashboard web calls have no raw body; use Resolved for those.
  • Context Variables — variables substituted into {{...}} placeholders.
  • Analysis — the AI-generated summary, sentiment, resolution, topics, and action items.

5. Cost

Standalone version of the Insights → Cost panel, with one extra control: a USD / INR currency toggle (1 USD = ₹100 conversion). Useful for India-region operators billing in INR.

Only shown if costBreakdown is populated. If the call hasn't had cost computed yet (very recent or failed before finalization), the tab is hidden.

6. Config

Same content as Config trace, presented as a standalone tab with the Resolved / Raw toggle:

  • Resolved Config — the configuration the pipeline actually ran with (defaults filled, {{variables}} substituted). Always present for calls that started.
  • Raw Request — the literal body of the request that configured the call (the POST /api/v1/call body, or the latest binding PATCH if you updated it), with provider auth redacted. Present for API calls (outbound and inbound) and rejected attempts.
  • Context Variables — substituted variables.
  • Analysis — full analysis blob.

Use this tab to reproduce a call — copy the Raw request, tweak it, and send it back to the endpoint it came from.


Tweaking based on what you see

A field-by-field cheat sheet of "this insight is bad → change this setting":


API reference

The list page is backed by GET /api/v1/calls and the detail page is backed by GET /api/v1/calls/{call_id}. Every filter/column/panel on this page maps to a field documented in those API references.

Programmatic access example:

curl "https://dashboard.zoxa.ai/api/v1/calls?transport=outbound&from_date=2026-06-10T00:00:00Z&sort_by=cost&sort_order=desc&per_page=50" \
  -H "X-API-Key: zsk_..."
curl "https://dashboard.zoxa.ai/api/v1/calls/$CALL_ID" \
  -H "X-API-Key: zsk_..."

See also

  • Analytics — aggregate metrics across calls (volume, success rate, avg duration, cost).
  • Tracing — Langfuse pipeline-trace integration (per-call deep dive when enabled).
  • Webhooks — receive call.started, call.ended, call.completed events.

On this page