Skip to main content

The stats surface map: dashboard workbench vs the analytics pipeline

Devotel Orbit exposes two distinct read surfaces for “numbers on screens,” and they are built differently on purpose. The stats workbench (/api/v1/stats/…) is a family of small, per-widget reads that power the dashboard’s overview cards, channel health tiles, and workspace headers — computed from raw rows at request time. The analytics pipeline (`/api/v1/analytics/…) serves pre-aggregated hourly series from the read replica, as described on the analytics pipeline concept. The sibling operator observability map frames WHEN to open each surface; this page owns WHERE each number a widget shows actually comes from, so you can predict which route family answers your question before you write any code.

1. Where the dashboard workbench fits

Every number on the dashboard’s home overview, workspace headers, and channel tiles resolves to one route under /api/v1/stats:
  • GET /stats/summary — the six home-overview cards (24-hour message volume, active agents, total contacts, 24-hour API calls, today’s spend, success rate).
  • GET /stats/channel-health — per-message-channel health tiles (SMS, WhatsApp, email, voice delivery quality).
  • GET /stats/usage — the daily usage timeline chart across channels.
  • GET /stats/activity — the recent-activity feed (messages, agent events, webhook deliveries, newest first).
  • GET /stats/dashboard — all four above in one parallel-fetched payload; the home-overview page prefers this to four separate polls, and each leg shares cache keys with its individual sibling.
  • Workspace header cardsGET /stats/sms, /stats/whatsapp, /stats/voice, /stats/numbers, /stats/agents, /stats/contacts, /stats/flows each power one workspace’s aggregate counters.
  • Operational healthGET /stats/gateway/sms renders the SMS workspace’s gateway availability card (status verdict, latency, rolling-24h counts); GET /stats/request-logs backs the Developer → Request Logs viewer (request-level drill-down, not an aggregate at all).
  • Conversation intelligenceGET /stats/conversation-intelligence powers the Conversation Intelligence page (sentiment, leaderboard, volume over a trailing window).
Two nested blocks sit under the same prefix:
  • /stats/analytics/* — the advanced-analytics block (full payload, summary, channels, volume, delivery funnel) plus the carbon endpoints (/stats/analytics/carbon, /stats/analytics/carbon/export). The whole block is feature-flagged per tenant; without the advanced-analytics flag it returns 403.
  • The “always-on” subset/summary, /channel-health, /usage, /activity, /dashboard, /request-logs that degrade to a graceful zeroed or empty 200 when a transient dependency fails, so the polled home widgets render a fresh-tenant zero state instead of an error. The remaining reads pass through the standard tenant-schema gate.

2. Read-surface semantics: stats vs analytics

The two surfaces differ on three axes, and the difference decides which one to call. Granularity of the question. The stats controller computes raw-event answers at request time — counts over the tenant’s message, contact, call, flow, and number tables for a short window (24 hours for the summary cards, a bounded day range for the usage timeline). It is a console over current rows: the right reach for “what is the dashboard showing right now” and “how many agents do I have.” The analytics surface answers trend questions over pre-aggregated hourly buckets — “how did delivery rate move over the last quarter” — and deliberately accepts a 1–3 second replica lag in exchange for cheap, index-friendly reads. Never answer a trend question by polling /stats/usage from your own system; use /api/v1/analytics (or its scheduled reports). Consistency guarantees. Stats reads run against the primary’s tenant schema and degrade to zeroed empties on transient failure — a widget that must render something. Analytics reads hold the read-replica invariant (reporting never competes with live traffic) and are served from roll-ups when they exist. One aggregate can legitimately disagree with the other for the lag window; the operator map covers which surface to trust for which question class. Access and quotas. API-key callers reach the stats workbench with the analytics:read scope — the same scope the /api/v1/analytics family enforces. Analytics endpoints run under a per-tenant analytics rate limit (60 requests per minute); the stats family runs under the general authenticated-read limit the dashboard’s foreground polling uses. The heavy /stats/analytics/* advanced block additionally requires the per-tenant advanced-analytics flag.

3. The pipeline underneath both

Neither surface invents its numbers. The usage metering pipeline is the event source: every message, call, and billable action writes an append-only usage record, and both the stats workbench’s counters and the analytics pipeline’s hourly roll-ups are projections over the same tenant schema. The analytics pipeline concept covers the read-replica invariant, the per-tenant scope and rate limit, and the background producers that pre-aggregate the /api/v1/analytics series. Because both surfaces share the source, a figure the dashboard home-overview shows (stats) and the figure an Insights chart shows (analytics roll-ups) count the same billed events — they differ only in aggregation window and freshness.

4. Which route family answers which question

The rule of thumb: widget and workspace-header questions go to /stats; trend and reporting questions go to /analytics or /insights; durability goes to exports and webhooks. When you cannot tell which family a question belongs to, return to the operator observability map — it covers the WHEN; this page is the WHERE.

See also