Stats
Query daily and hourly time-series rollups for trend analysis via MCP.
Time-Series Rollups are pre-aggregated daily and hourly count tables for the four event-shaped data sources — events / metric_events / funnel_events / questionnaire_responses. They power the subtle sparkline charts on dashboard cards and arbitrary date-range trend queries that survive raw-event retention pruning.
Kinds: events, users, sessions, metric_completions, funnel_completions, questionnaire_responses. Grains: daily, hourly. Buckets are UTC; the in-progress bucket is excluded by default so a partial day or hour can't render as a misleading dip. Reads always hit the project-rollup row (one row per (project, is_dev, bucket)) unless a single-app filter is supplied — never a SUM across apps at query time.
Two background jobs maintain the tables — stats_aggregate_hourly at :05 UTC and stats_aggregate_daily at 00:30 UTC, each re-aggregating the trailing 3 buckets to absorb late-arriving events from offline SDKs. Historical backfills (e.g. first-time rollout or repair after a gap) are an operator action — run pnpm backfill in the monorepo against the production DB. It re-aggregates the trailing 365 days using DELETE+INSERT … SELECT … GROUP BY in a single transaction per bucket range, so re-running over the same window is idempotent and stale rows never survive. unique_users and unique_sessions are per-bucket distincts and non-additive across buckets — sparkline value lines plot the additive count columns.
query-stats-bucketed
Returns one zero-padded point per bucket in the requested window. Use it to power sparklines, trend dashboards, or year-over-year comparisons — every kind of dashboard card on /dashboard is backed by this endpoint.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind | enum | Yes | events / users / sessions / metric_completions / funnel_completions / questionnaire_responses |
grain | enum | Yes | daily or hourly |
project_id | UUID | One of | Project ID. Mutually exclusive with team_id |
team_id | UUID | One of | Team ID — aggregates across every accessible project in the team |
app_id | UUID | No | Narrow to one app within the chosen scope |
days | number | No | Trailing UTC days for grain=daily (1–365, default 30). Ignored when from/to are set |
hours | number | No | Trailing UTC hours for grain=hourly (1–2160, default 24). Ignored when from/to are set |
from | string | No | Explicit start (YYYY-MM-DD for daily, ISO 8601 for hourly). Pair with to |
to | string | No | Explicit end (inclusive at the bucket level) |
excluding_current | boolean | No | Drop the in-progress bucket. Defaults to true; set false to include it |
data_mode | enum | No | production / development / all. Defaults to production |
slug | string | No | Narrow metric_completions / funnel_completions to one definition |
Response shape (StatsBucketedResponse):
{
"kind": "events",
"grain": "daily",
"from": "2026-04-20",
"to": "2026-05-19",
"data": [
{ "bucket": "2026-04-20", "value": 1240 },
{ "bucket": "2026-04-21", "value": 1310 },
...
]
}events, users, and sessions all read the same events_{daily,hourly} rollup; the kind selects which column maps to value (event_count / unique_users / unique_sessions). metric_completions filters to phase='complete'. funnel_completions filters to each funnel's terminal step (resolved from funnel_definitions). questionnaire_responses returns submitted_count.
Backfill an older range
Backfilling the rollup tables is an operator action and is not exposed via MCP or REST. Run pnpm backfill in the monorepo on the production VPS — it re-aggregates the trailing 365 days against the database directly. Idempotent across re-runs. Aggregation tables are explicitly excluded from retention pruning, so historical buckets stay queryable indefinitely.
