Stats & Analytics
Pre-aggregated counters that power dashboards without scanning raw data.
Stats are pre-aggregated counters that answer questions like "how many messages did this tenant deliver today?" or "what has this campaign cost?" instantly, without scanning message or phone records.
Stats are a derived read model - messages, phones, and events remain the source of truth. If stats and raw data ever disagree, raw data wins.
In the dashboard: every chart and stat card, from the home page down to individual campaigns and phones, is powered by these counters.
How they relate
Stats are updated asynchronously by the stream worker: when a message settles or a phone changes state, the change fans out into counter increments across every relevant dimension.
Dimensions and windows
Every counter is bucketed by a dimension (whose number is this?) and a window (over what period?):
| Dimensions | GLOBAL, TENANT, PROJECT, CAMPAIGN, COMMUNICATION, PHONE, COUNTRY |
| Windows | HOUR, DAY, MONTH, ALL_TIME |
So one delivered message increments SMS_DELIVERED_COUNT for the global, tenant, project, campaign (if any), communication (if any), and phone dimensions - each across all four time windows.
Metrics
Message metrics (recorded when a message settles): SMS_COUNT, SMS_COST, SMS_PARTS, SMS_DELIVERED_COUNT, SMS_FAILED_COUNT, SMS_BLOCKED_COUNT - across all windows and message dimensions.
Phone metrics (all-time only, for global/country/tenant/project): PHONE_COUNT, VALID_PHONE_COUNT, SMS_OPT_IN_COUNT, SMS_OPT_OUT_COUNT.
A message only counts after it settles (DELIVERED, FAILED, or BLOCKED) - in-flight messages don't appear in stats, and a message that racks up multiple failed attempts in its history before settling is still counted exactly once.
Accuracy guarantees
Recording is at-least-once with a bounded, rare over-count: no increment is ever lost, and two layers of idempotency protection keep queue redeliveries and concurrent workers from double-counting. Dimensions can briefly lag each other by a few in-flight operations. For accounting-grade numbers, the raw message records are authoritative.
Reading stats
- Curated overview endpoints power the dashboard: per-tenant, per-project, per-communication, and per-campaign overviews with totals and zero-filled chart series, plus message-volume, phones-by-country, and top-communications/campaigns endpoints.
POST /v1/stats/query(admin) is the free-form endpoint: fetch one value, a time series for one dimension value, an all-time total, or one metric across many dimension values (e.g. top tenants by cost).
API reference
POST /v1/stats/query- free-form stats queries (admin)GET /v1/stats/global/overview- system-wide overviewGET /v1/stats/tenants/:tenantId/overview- per-tenant overviewGET /v1/stats/projects/:projectId/overview- per-project overview
Calling from TypeScript? See Generating a Typed Client.