Analytics¶
Aggregate volume, sentiment, and funnel trends across one agent or every agent, and across every channel, a phone call, a widget chat, an inbound email, or a silent background task. This is what backs the admin dashboard's Analytics page. Every call is also classified individually (see "Sentiment and outcome" below); this endpoint aggregates that per-call data rather than drawing from a separate data source.
Endpoint¶
Authentication and roles¶
Takes an admin session token (see Authentication). Any role can call it, including Viewer, the same read-only observability bar as the Calls log itself.
Query parameters¶
| Parameter | Type | Description |
|---|---|---|
agent_id |
integer | Scope to one agent. Omit for every agent combined. |
channel |
string | Scope to one channel: phone, web, email, or agent_task. Omit for every channel combined. Only scopes total_calls/completed_calls/abandoned_calls/avg_duration_seconds/sentiment/funnel/volume_by_day/top_agents, never volume_by_channel itself (see below). |
start_date |
date (YYYY-MM-DD) |
Inclusive lower bound on Call.created_at. |
end_date |
date (YYYY-MM-DD) |
Inclusive upper bound on Call.created_at. |
{
"total_calls": 128,
"completed_calls": 96,
"abandoned_calls": 22,
"avg_duration_seconds": 143.5,
"sentiment": { "positive": 61, "neutral": 30, "negative": 12, "unclassified": 25 },
"funnel": { "queued": 128, "ringing": 4, "answered": 106, "completed": 96 },
"volume_by_day": [
{ "date": "2026-01-01", "count": 12 },
{ "date": "2026-01-02", "count": 18 }
],
"top_agents": [
{ "agent_id": 1, "agent_name": "Support Bot", "call_count": 128 }
],
"volume_by_channel": [
{ "channel": "phone", "count": 90 },
{ "channel": "web", "count": 30 },
{ "channel": "email", "count": 6 },
{ "channel": "agent_task", "count": 2 }
]
}
Fields¶
| Field | Type | Description |
|---|---|---|
total_calls |
integer | Every call matching the filters (despite the name, this counts every channel, not just phone). |
completed_calls |
integer | status == completed. |
abandoned_calls |
integer | status in no_answer, failed, or busy. |
avg_duration_seconds |
number or null | Average of duration_seconds across calls that have one; null if none do. |
sentiment |
object | Counts by classified sentiment, plus unclassified for calls with no sentiment set yet. |
funnel |
object | queued (pending), ringing, answered (in_progress/completed/transferred), completed. Always scoped to the phone channel alone, regardless of the channel filter, the same way volume_by_channel always ignores it: queued/ringing are phone-only realities, a chat or email session never rings. |
volume_by_day |
array | One entry per day with at least one call, ascending by date. |
top_agents |
array | Up to 10 agents by call count, descending. Trivially one entry when agent_id is set. |
volume_by_channel |
array | Every channel always present, phone/web/email/agent_task, zero-filled rather than omitted. Reflects agent_id/start_date/end_date but never the channel filter itself, so a filtered view still reads in context of the whole. |
Sentiment and outcome¶
Every call is classified individually by a one-shot Gemini pass over its
transcript at finalize time, following the same "never block on failure"
pattern that warm transfer and every other call-finalize side
effect follows. This produces sentiment (positive, neutral, or
negative) and a short free-text outcome_tag (for example
"resolved" or "booked appointment"), both returned on the
Call resource itself. A call too short to have a
transcript worth judging simply stays unclassified.
Next¶
- Calls: the per-call record
sentimentandoutcome_taglive on. - Live architecture: what is happening right now, rather than how a date range went.