Skip to main content

Read the cost-intelligence dashboards

The Analytics API reference documents the aggregate cost endpoints — this guide is the prose walkthrough for the cost-intelligence surfaces that sit on top of them in the Orbit console, and explains what the unit economics actually answer. Use it when a margin question lands: what does a message, a voice minute, an AI token, or a video minute actually cost us per unit, and where is the margin in that? Example requests in this guide use the APIs directly; most operators read the dashboards in the console and use the APIs for export or integration.

What a cost surface is

Every cost surface reads prices that were already recorded on your usage rows — the dashboards never re-price traffic. A message, call, or AI event is priced at the moment it is billed against your wallet, and the cost surfaces aggregate those recorded charges into spend, volume, and unit-cost views. Because the inputs are the same wallet charges in every case, the surfaces reconcile with each other: the “Total spend” headline on the Channel costs page is the same money the Billable usage records panel and the per-conversation P&L draw from. Access is role-scoped per surface. The money-family surfaces — Channel costs, LLM spend, Conversation P&L — restrict to the owner, admin, developer, and billing roles on the console, because they expose aggregate cost and margin analysis. The room-usage analytics API additionally admits the viewer role, as it reports volumes rather than money.

Channel costs — Insights → Channel costs

Console route: /insights/costs, page title “Channel costs”. This is the unit-economics hub: one window (24 hours to 12 months, or a custom range) applied to every outbound channel at once. What the page answers, top to bottom:
  • Headline tiles — total spend, billed messages, and average cost per message across all channels in the window. The average is total spend ÷ billed messages, computed at sub-cent precision, so a channel mix dominated by low-cost SMS still reports a truthful blended unit cost.
  • Spend over time — daily total spend for the window.
  • Cost by channel — per-channel spend, message volume, and average cost per message, with a totals footer. The footer sums every channel — SMS, MMS, voice, WhatsApp, email, and the rest — so it always equals the Total spend headline. Spot the expensive unit here: if WhatsApp’s cost per message runs five times SMS’s, that is the row to act on.
  • Billable usage records — carrier-parity counters (the data export sheets traditionally call usage records) for SMS, MMS, and voice, with an optional per-country split. Voice appears here in minutes; messaging in messages. This panel is deliberately carrier-scoped — it mirrors the bill a carrier would show — so it will not equal Total spend. The panel therefore shows a numeric reconciliation line for the non-carrier spend (email, chat, RCS, and similar) that is included in Total spend but not billed as carrier usage: the gap has an amount, not an apology.
Both panels are backed by endpoints you can export from:
The response carries three blocks under data: Sum by_channel[].total_spend and you get totals.total_spend exactly — that equality is the dashboard-versus-API check step later in this guide makes use of.

Billable usage records — carrier-parity counters

The Billable usage records panel reads GET /api/v1/messages/usage/records — a carrier-parity aggregate over the billed rows, shaped after the usage-records surface a carrier’s export would give you:
The response holds period_days (the resolved window) plus a records[] array. Each record is one bucket in the (sms | mms | voice) × (inbound | outbound) matrix: Two conventions in count explain every gap you will hit between this panel and the Billed messages headline:
  • SMS is counted by segment, not by message. A 240-character SMS is two segments, so it appears twice in sms-outbound.count but once as a billed message. The headline tile counts priced outbound messages; the records panel counts billed segments — it is expected to be higher, and it is the number a carrier bill would show.
  • Inbound traffic is included (sms-inbound, mms-inbound, voice-inbound), because inbound SMS/MMS bills on most carriers. The headline tile counts outbound charge rows only.
Do not “fix” the gap by filtering; reconcile it with the check in the verification section below.

AI spend — Insights → LLM spend

Console route: /insights/llm-spend, page title “LLM spend”. AI cost shows up as token spend, not message spend: the dashboards report tokens and cost per agent, per model, per feature, and per conversation. Read it alongside Channel costs — the blended cost of an AI-handled conversation is its telephony leg (Channel costs, above) plus its token leg (here). All money on every LLM-spend route is integer cents; format it with your org wallet currency. Four routes back the page, all under /api/v1/insights/llm-spend/: GET .../overview — the dashboard header in one roundtrip: GET .../summary — the breakdown table. Query parameters: from / to (ISO-8601 datetimes; default window is the last 30 days), groupBy (agent | model | conversation, default agent), and limit (top-N rows by cost, default 50). Each row in groups[] carries group_id, group_label, model, total_tokens, total_cost_cents, and conversation_count, and totals carries the window-wide tokens/cents/conversation count that back the share-of-cost column. Swap groupBy=model to answer “is the expensive agent actually expensive, or is it the model it runs on?” — the same spend, regrouped. GET .../timeseries — one bucket per time slot for the trend line. granularity is hour (capped at a 14-day window) or day (30-day default window); each bucket carries total_tokens and total_cost_cents. GET .../top-conversations — the runaway-conversation drill-down: the highest-cost conversations in the window (capped at 50), each with conversation_id, agent_id, agent_name, model, total_tokens, total_cost_cents, and started_at. Pair it with a budget: GET .../budget returns the org’s monthly/daily caps and per-channel thresholds, and PUT .../budget replaces them.

Per-conversation telephony P&L — API

The per-conversation economics endpoint, GET /api/v1/analytics/cost-per-conversation, groups priced message and call charges by conversation over a window you supply and returns the top conversations by telephony spend:
Each row is one conversation: its telephony cost-to-serve (integer cents at sub-cent precision — a fraction of a cent per SMS segment stays a fraction) and its message count, so cost per message per conversation is a division away. The window is capped at 90 days; limit caps the returned rows. Use it to answer “what does a resolved WhatsApp conversation actually cost us in carriage?” without exporting to a spreadsheet.

Unit-cost formulas

Two formulas cover most margin questions. Run them on the same 30-day window so your denominators share an accounting period. Cost per conversation — three legs, one denominator:
A conversation handled end-to-end by a human inbox operator has only the telephony leg; the same conversation handled by an AI agent in a video room carries all three. Compare the two totals against what the conversation’s outcome was worth to decide where the agent rollout pays. Cost per campaign — preview, then settle:
The average-cost-per-unit from analytics/costs and the settled rollup average answer different questions with the same denominator convention: the channel row tells you what the unit costs fleet-wide right now; the rollup tells you what the campaign’s units actually cost once they settled.

Verify: dashboard totals against API sums

Whenever a number on a cost surface is about to drive a pricing or carrier decision, spend two minutes proving the surface against its own API:
  1. Pick one window — say 30 days — and read the Total spend headline on /insights/costs.
  2. Pull GET /api/v1/analytics/costs?days=30&group_by=day and sum two ways: totals.total_spend, and Σ by_channel[].total_spend. All three numbers — headline, totals block, channel-footer sum — must match to the cent.
  3. Cross-check the Billable usage records panel: pull GET /api/v1/messages/usage/records?days=30 and confirm each console row equals the API record for the same category. Expect the records’ SMS count to exceed the Billed messages tile — segments versus messages, plus inbound — and expect the panel’s non-carrier reconciliation line to cover the rest of Total spend.
  4. On /insights/llm-spend, the by-agent table’s column sum must equal the window-wide totals.total_cost_cents from GET .../summary?groupBy=agent for the same window, and overview.month_to_date.spend_cents must equal summary?groupBy=agent totals run from month start to now.
A mismatch to the exact cent means you are comparing different windows or currencies — check period_days, from/to, and per-row currency before suspecting the surface. Both dashboards are cached (60 s and short poll intervals), so a fresh send can also lag a screen refresh by under a minute; the API is the tiebreaker.

Wire the spend to an alert

Cost surfaces are read-heavy; the tripwire around them should be alert-heavy. The landed patterns:
  • Set per-API-key usage ceilings with threshold badges per Per-API-key usage budgets and threshold alerts — the earliest signal that an integration’s traffic shape (and therefore its spend) has left the plan.
  • Grade spend velocity with the anomaly rules in Usage anomaly alert rules, and hard-cap the AI leg’s caps with the budget config behind GET/PUT /insights/llm-spend/budget (monthly/daily caps, per-channel thresholds, soft-alert percentage) instead of discovering a runaway agent on the month-end P&L.
  • For a custom check, schedule the verification recipe above in your own automation and alert when Σ by_channel[].total_spend for a day crosses a threshold you provisioned — the same equality check it does for reconciliation doubles as a spend-rate monitor. AI agent cost controls covers the hard caps for the AI spend leg.

Video usage — room minutes

Video does not bill per message — it bills per participant-minute, so its economics live on a usage surface of their own: GET /api/v1/video/rooms-analytics/usage, optionally with from / to datetimes (30 days by default). The response gives an org summary — total sessions, total room-minutes (room wall-clock, so a 10-minute room with 4 participants is 10 room-minutes ≈ 40 participant-minutes of metering), average and maximum peak participants, recording success rate, and join failures — plus a daily series for the window. Multiply room-minutes by your participant mix to sanity-check video spend against the rate card. Soft-deleted sessions are excluded. When a video-spend question is really a quality dispute — a degraded session the tenant blames the platform for — the per-session QoE report in Monitor in-call video quality is the evidence surface to pair with the usage numbers.

Put the legs together

Margin per interaction is the assembly: the interaction’s price on your rate card, minus the channel leg (Channel costs per-channel unit costs, or the per-conversation P&L for individual conversations), minus the AI leg (LLM spend per conversation when an agent handled it), minus the video leg (room minutes when the interaction happened in a room). Run the assembly on a 30-day window and two things fall out: which channels carry the volume at the widest per-unit margin, and which conversations cost more to serve than their outcome is worth.

See also