The Analytics Hub is the admin reporting surface at /admin/analytics. It
replaces the old fixed conversion/engagement/funnel dashboards with a single
report builder: pick a dataset, choose measures and a breakdown, filter,
visualise, then save / export / pin / schedule the result. Every fixed dashboard
that used to exist now ships as a built-in template you can clone and adapt.
This page is the operator/admin reference for the hub. For the data that feeds
it — telemetry ingestion, goal matching, the materialized views and their
refresh/retention crons — see Operate → Insights.
Permissions
The hub is gated by ACL leaves under the comerix.insights branch. Grant them
per role under Access → Roles:
| Resource | Grants |
|---|
comerix.insights.analytics | View the hub, run reports, view saved reports, export. |
comerix.insights.reports.manage | Build, save, duplicate, delete, pin, and schedule reports. |
comerix.insights.goals | View goals & conversions. |
comerix.insights.goals.manage | Create & edit goals (Insights → Goals). |
comerix.insights.telemetry | View raw chat telemetry (the Event Explorer). |
reports.manage is a child of analytics — a user who can build reports can
always run them.
The hub landing
/admin/analytics lists, side by side:
- Templates — the six built-in starting points (see Templates).
- Saved reports — every report this tenant has saved, with run / edit /
export / pin / schedule / duplicate / delete actions.
“New report” opens the builder on a blank definition; opening a template opens
the builder pre-filled with that template’s definition (nothing is saved until
you press Save).
Datasets
A report is built against exactly one dataset — a single fact grain with a
fixed, whitelisted set of dimensions, measures and filters. Definitions only
ever reference these by id, so a report can never inject arbitrary SQL.
| Dataset | Source | Dimensions | Key measures |
|---|
| Conversions | ins_goal_completions (+ flow lookup) | time, goal, channel, currency, flow | conversions, revenue, avg value, distinct customers, distinct conversations |
| Chat engagement | ins_conversation_stats_daily (MV) | time | conversations, messages, user/agent messages, engaged, messages per conversation |
| Token usage | ins_token_usage_daily (MV, + flow lookup) | time, flow | total / prompt / completion tokens, cost, avg tokens per message, avg tokens per conversation |
| Flow runs | fs_executions (+ flow lookup) | time, flow, status, trigger type | total runs, completed, failed, aborted, in-flight, success rate, avg & p95 duration |
| Telemetry | ins_telemetry_events | time, event name | events, distinct conversations |
| Funnel | computed | — | step-by-step drop-off for a chosen flow |
Measures carry a type that drives formatting everywhere (screen, export, email):
currency values render to two decimals against the row’s currency,
ratios render as percentages, durations in ms/seconds, decimals (such
as token cost) to fixed precision. Currency measures
on the Conversions dataset are only meaningful when the result is sliced by
currency (mixing currencies in one number is not).
Token usage & cost
The Token usage dataset reports LLM token consumption and estimated cost of
chat replies. It reads ins_token_usage_daily, a materialized view that rolls up
the token columns on chat messages by tenant, day and flow, so reports never have
to scan the message log. Break it down by time (with a grain) or by flow,
and filter by flow_id.
| Measure | What it computes |
|---|
| Total tokens | Sum of prompt + completion tokens. |
| Prompt tokens | Tokens sent to the model. |
| Completion tokens | Tokens generated by the model. |
| Cost (USD) | Estimated spend, summed from per-message cost and rendered to two decimals. |
| Avg tokens / message | Total tokens divided by token-bearing messages. |
| Avg tokens / conversation | Total tokens divided by distinct conversations. |
Only messages that recorded a token total are counted, so the dataset covers
LLM-backed replies — not every chat message. The rollup keeps the last
90 days; older history ages out of the view.
The Funnel dataset is special: it has no free-form measures/dimensions and
needs a flow chosen in the builder before it can run.
Building a report
The builder (/admin/analytics/reports/new) is a guided form. A report
definition is:
- Dataset — one of the above.
- Measures — one or more numbers to compute.
- Dimensions — zero or more breakdowns. Picking the time dimension turns
on a grain (
day / week / month).
- Filters — narrow the rows. Operators depend on the field: equality /
in
for categoricals, numeric comparisons for values, contains for event names.
Suggest-enabled filters autocomplete their values from the tenant’s own data
via /admin/analytics/reports/suggest.
- Date range — a preset (
7d, 30d, …) or an explicit window.
- Visualization — see below.
Run (/admin/analytics/reports/run) streams a live preview as you edit —
capped at 200 rows. Saved reports render up to 500 rows on screen;
exports and scheduled deliveries run up to 5000 rows. A definition is
validated before any query runs — an unknown id or operator is a 400, never a
query.
Visualizations
| Viz | Use for |
|---|
| Line | a measure over time |
| Bar | a measure across a categorical breakdown |
| Table | multiple measures / raw rows |
| KPI | a single headline number |
| Pie | share of a total across a small breakdown |
| Funnel | step drop-off (Funnel dataset only) |
Saved reports
Save (reports.manage) persists the definition as an ins_reports row
owned by the tenant. From a saved report you can:
- Edit — reopen the builder on the saved definition.
- Duplicate — clone it to
"<name> (copy)" and open the copy in the builder.
- Delete — remove it (also un-pins and un-schedules it).
- Export, Pin, Schedule — below.
Export
/admin/analytics/reports/{id}/export/{csv|xlsx} streams the saved report as a
download. Cells are formatted exactly as they render on screen (ratios as
percentages, durations in seconds/ms, money to two decimals). No grand-total
footer is written — summing ratios or mixing currencies across rows would be
meaningless. Exports use the 5000-row cap.
Pin to dashboard
Pinning (reports.manage) puts a report on the admin dashboard as a live
card. Two widgets render there:
- Pulse overview — a conversations hero with a 14-day chart, a flow-run
metric strip, flows that need attention, top flows by volume and a live
activity feed. The metric strip includes a Token spend KPI — the estimated
LLM cost over the selected range (with the token count beneath it), sourced from
the same token-usage rollup as the Token usage dataset. It renders as
— until
there is token data.
- Pinned reports — the customizable grid of your pinned reports, each run
live and rendered as a resizable, reorderable card.
/admin/analytics/dashboard/layout persists the grid: each card’s order and its
span (one of 4, 6, 8, 12 columns). Dragging a card off the grid
un-pins it.
Scheduled email reports
Scheduling (reports.manage) emails a saved report on a cadence. A schedule is:
- Frequency —
daily or weekly.
- Hour —
0–23, UTC.
- Weekday — ISO
1 (Mon) – 7 (Sun), weekly only.
- Recipients — valid email addresses, deduped, max 20.
ReportScheduleCronJob (5 * * * *) delivers due schedules: it runs the report
in export mode and emails an HTML summary with a CSV attachment, then stamps
last_sent_on so a re-run never double-sends within the same day. One report’s
failure never aborts the batch. Delivery needs MAILER_FROM set and the cron
connection running on the BYPASSRLS role (see
Operate → Insights).
Templates
The six built-in templates reproduce the curated views the hub replaced. Each is
a cloneable starting point — opening one pre-fills the builder; it is not itself
a saved report.
| Template | Dataset | Default viz |
|---|
| Conversions overview | conversions | line |
| Revenue by goal | conversions | bar |
| Chat engagement | chat | line |
| Flow success | flow_runs | table |
| Event explorer | telemetry | bar |
| Funnel | funnel | funnel (needs a flow) |
A new tenant can be seeded with default goals plus a starter set of saved/pinned
reports in one idempotent step — see insights:provision-defaults in
Operate → Insights.
Data freshness
The Conversions dataset’s headline counts read the live ledger, so they are
real-time. The Chat-engagement and Token-usage datasets read materialized views
(ins_conversation_stats_daily and ins_token_usage_daily), both refreshed
hourly by RefreshInsightsCronJob in the same ins_refresh_analytics() pass —
daily series and breakdowns (and the dashboard Token spend KPI) therefore lag by
at most the refresh interval. See
Operate → Analytics refresh.