Real-time business metrics for your billing operation. Track monthly recurring revenue (MRR), customer churn, revenue breakdown by plan, and other key indicators.
- MRR - the normalized monthly revenue from all active subscriptions
- Churn - the rate at which customers cancel or downgrade
- Revenue - breakdown by product, plan, currency, and time period
Break a metric down by one dimension
Slices a metric by one of its declared dimensions and reduces each slice over the window. It is a REDUCTION of the same series /analytics/series returns — the same source, the same fold, the same stock-versus-flow rules — so a breakdown and its series can never disagree about their own total. A slice's share is its value over total; compute it from those two fields, because a share cannot be re-aggregated. top_n folds the tail into a single 'Other' slice, flagged with is_other and always sorted last regardless of size.
Required scope: analytics.read (Read analytics).
query Parameters
metricMetric key.
dimensionOne of the metric's declared dimensions.
period.gteStart of the window, included. RFC 3339 or YYYY-MM-DD.
period.ltEnd of the window, not included. RFC 3339 or YYYY-MM-DD.
fromDeprecated: use period.gte instead. Removed on 2026-11-30.
toDeprecated: use period.lt instead. Removed on 2026-11-30.
grainBucket width used internally before reduction: day, week, month, quarter or year. Defaults to the metric's finest grain; it does not change the answer for a flow.
currencyISO 4217 code. Required for a money metric, except with dimension=currency, which separates the codes instead of mixing them.
top_nKeep the N largest slices and fold the rest into 'Other', ranked by total over the window.
timezoneIANA zone. Defaults to the organization's configured timezone.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Break a metric down by one dimension › Responses
OK
List the currencies this workspace has data in
Returns the ISO 4217 codes the workspace actually has analytics data in, ascending. Read from the DATA — an organization bills in whatever currencies its charges, invoices and payments carry — so the list cannot go stale the way a configured one does, and a currency that stops being used simply stops appearing.
It is the same union the rollup dispatcher enumerates and the series endpoint splits an all-currencies answer over, so a picker built from this list cannot offer a code /analytics/series would then answer nothing for.
An empty list is a real answer: a workspace that has billed nothing has no books. From 2026-11-30 this list answers {data: [...], pagination} instead of {data: {currencies: [...]}}.
Required scope: analytics.read (Read analytics).
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List the currencies this workspace has data in › Responses
OK
Invoice aging as of a date
Open receivable split into aging bands AS OF a given day, read from the nightly snapshot.
The bands are aged against the REPORT DATE, not against today. An invoice 90 days overdue now was 30 days overdue two months ago, and this endpoint says so — which is the whole reason the snapshot exists. invoices.amount_due mutates in place, so the same question asked live would return today's balances under an older date.
A day with no snapshot answers 404 with that explanation rather than five zeros: 'nothing was owed' and 'nobody has computed this' are different answers and must not render identically.
All five bands are always present, in aging order, zero-filled.
Required scope: analytics.read (Read analytics).
query Parameters
currencyISO 4217 code. Aging never mixes currencies.
as_ofReport date, YYYY-MM-DD. Defaults to yesterday — the most recent day the snapshot can be complete for.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Invoice aging as of a date › Responses
OK
List available metrics
Returns every metric this API serves, with its unit, grains, the closed set of dimensions it may be sliced by, and whether a coarser figure may be derived by summing finer buckets. Rendered from the server's own registry, so a client's metric picker cannot drift from what the server will actually answer. A metric that is declared but not yet served appears with available=false rather than being absent. From 2026-11-30 this list answers {data: [Metric], pagination} instead of {data: {metrics: [...]}}.
Required scope: analytics.read (Read analytics).
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List available metrics › Responses
OK
Get the dashboard overview
Returns the dashboard's headline tiles for a window, each with the same reduction over the immediately preceding window of equal length so the deltas are comparable.
Two things are explicit on every tile because they are not interchangeable. series_kind says how the window became one number: a flow is summed, a stock is the level at the end of the window and is never summed across buckets; adding up seven consecutive days of MRR gives a number seven times too large that looks entirely plausible. And available is false, with a reason, for a metric that is declared but not yet servable; the tile is still returned, because a missing number that says why is more useful than a plausible wrong one.
period is the window and previous_period the window each tile's previous is reduced over. A tile carries value and previous; compute a delta from them, and none when previous is zero: a change from nothing is undefined, not infinite and not +100%.
Required scope: analytics.read (Read analytics).
query Parameters
period.gteStart of the window, included. RFC 3339 or YYYY-MM-DD.
period.ltEnd of the window, not included. RFC 3339 or YYYY-MM-DD.
currencyISO 4217 code. Required: the money tiles cannot be aggregated across currencies.
fromDeprecated: use period.gte instead. Removed on 2026-11-30.
toDeprecated: use period.lt instead. Removed on 2026-11-30.
timezoneIANA zone to bucket in. Defaults to the organization's configured timezone.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Get the dashboard overview › Responses
OK
Get a metric time series
Returns one metric as a dense, gap-filled time series bucketed in the organization's local days. Three things about the response are worth reading before consuming it. The series is DENSE: a bucket in which nothing happened is present, because a chart must not be left to decide what a hole means. imputed distinguishes a CARRIED-FORWARD level from an observed one — only a stock is ever carried, since a level persists through a quiet bucket while a flow does not. And meta states the freshness and completeness of the answer, including any known qualification: a figure bounded by an accrual horizon says so rather than presenting an absent period as a zero. A window larger than the bucket cap is REFUSED rather than truncated, because a truncated series is indistinguishable from a complete one once it is drawn.
Required scope: analytics.read (Read analytics).
query Parameters
metricMetric key. GET /v1/analytics/metrics lists every one this API serves.
period.gteStart of the window, included. RFC 3339 or YYYY-MM-DD.
period.ltEnd of the window, not included. RFC 3339 or YYYY-MM-DD.
fromDeprecated: use period.gte instead. Removed on 2026-11-30.
toDeprecated: use period.lt instead. Removed on 2026-11-30.
grainBucket width: day, week, month, quarter or year (the grain enum). Defaults to the metric's finest declared grain. WEEK is the ISO week — Monday 00:00 in the bucketing timezone through the following Sunday — and it is DERIVED from the day grain rather than stored, so every metric defined at day grain answers it without appearing in that metric's grains list; QUARTER (from 1 January, April, July or October) and YEAR are derived from the month grain the same way. A window that does not begin on a bucket's first day opens with a PARTIAL bucket keyed to that bucket's start, and closes with a partial one likewise, exactly as a month-grain window clamps at its edges. A statistic (an amount-weighted mean such as dso) refuses every derived grain for the same reason it refuses month: a mean cannot be folded without its weights.
currencyISO 4217 code. REQUIRED for a money metric — an aggregate over mixed currencies has no meaning, so it is refused rather than guessed — EXCEPT with dimension=currency, which asks for one labelled series per code and therefore mixes nothing. Omitting it there answers every book the workspace has; GET /v1/analytics/currencies lists them.
dimensionSlice the series by one of the metric's declared dimensions.
top_nKeep the N highest-ranking series and fold the rest into a single 'Other'. Ranked by total over the whole window, not the last bucket, so the legend does not jump as the window moves.
timezoneIANA zone to bucket in. Defaults to the organization's configured timezone. Buckets are LOCAL days, not UTC days.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Get a metric time series › Responses
OK
Get wallet custody position
Returns how much customer money the organization currently holds (a liability, not revenue) and how a period's wallet activity split between revenue, discount, breakage, top-ups and refunds. Required currency filter — wallets hold one currency each. Reports how many ledger rows are still unclassified, so a partial answer is visible as one.
Required scope: analytics.read (Read analytics).
query Parameters
currencyISO 4217 currency code (e.g. EUR, USD). Wallets hold one currency each, so a summary is per-currency.
fromDeprecated: use period.gte instead. Removed on 2026-11-30.
toDeprecated: use period.lt instead. Removed on 2026-11-30.
period.gteStart of the reporting window, included (RFC 3339). Defaults to 30 days before the end.
period.ltEnd of the reporting window, not included (RFC 3339). Defaults to now.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Get wallet custody position › Responses
OK