Calls API
Call history, headline statistics, one call in full, and live switchboard state.
Four routes, and the last of them answers a different kind of question from the other three — see Live state and history before wiring a console to them.
Base: $LIVEGRID_API_URL. Every route is behind a customer JWT and scoped to one
organisation — Authorization: Bearer <customer JWT>. Implemented in
server/lg_calls_routes.py.
GET /public/calls#
This organisation's calls, newest first.
| Query | Default | Notes |
|---|---|---|
limit | 50 | capped at 200 |
outcome | — | equality match |
mode | — | equality match |
agent | — | matches agent_name |
since | — | started_at >= |
outcome and mode are passed through as equality matches rather than
validated against a list. The set of valid values is a product decision that
changes in the worker, and a stale allowlist here would silently hide new call
types.
{
"calls": [
{
"id": "...", "room_name": "support-8f3a21", "region": "sg",
"agent_name": "livegrid", "mode": "avatar",
"started_at": "...", "ended_at": "...", "duration_s": 214,
"participant_count": 2, "outcome": "completed", "source": "web",
"llm_model": "...", "avatar_provider": "livegrid", "cost_usd": 0.41
}
],
"count": 1
}
GET /public/calls/stats#
| Query | Default | Notes |
|---|---|---|
days | 30 | capped at 365 |
{
"window_days": 30,
"totals": { "calls": 812, "completed": 780, "failed": 19, "open": 13,
"total_seconds": 149204, "total_cost_usd": 318.44 },
"latency": { "median_ms": 1614, "p95_ms": 3980 },
"daily": [ { "day": "2026-09-09", "calls": 41, "seconds": 7210, "cost_usd": 15.02 } ]
}
Median, not mean, and it is the one number here worth arguing about. A single thirty-second timeout drags a mean far enough to make a good hour look bad, and the resulting alarm trains people to ignore the number.
open counts rows with no ended_at. That is not the same as "live" — see
Live state and history.
GET /public/calls/<call_id>#
One call, its transcript, and what the agent did.
{
"call": { "...": "every column of lg_calls" },
"events": [ { "seq": 1, "at": "...", "role": "user", "text": "...", "latency_ms": null } ],
"tools": [ { "at": "...", "tool_name": "lookup_order",
"arguments_json": "{...}", "result_summary": "...", "success": true } ]
}
A call belonging to another organisation and a call that does not exist both return 404 with the same body. Distinguishing them would tell an unauthorised caller which ids are real.
events may be empty on a call that certainly happened — the transcript is
best-effort. Render that as "no transcript", never as "no call".
Transcripts
GET /public/live#
What is on the wire right now, asked of the media plane itself. Nothing is read from the database.
{ "reachable": true, "rooms": [ { "room": "support-8f3a21", "participants": 2 } ] }
{ "reachable": false, "rooms": [],
"error": "UDK_SECRET is not configured, so the control plane cannot be asked." }
Always 200, even when unreachable — the request succeeded; what failed is a
dependency, and the body says which. reachable: false is not count: 0, and a
console must render them differently.