Dashboards group widgets — counters, time-series, breakdowns, funnels, tables, top-n lists — over agent events. Combine system canonicals (agent_view, agent_atc, agent_purchase, …) with custom event types registered through the Custom Event Types registry.
Composition
A dashboard belongs to a single brand. Each widget owns:
- type — one of six starter types (
counter, time_series, breakdown_bar, funnel, table, top_n)
- config — type-specific options validated against the widget’s zod schema. Title and the per-widget aggregation parameters live inside
config (there is no separate title or aggregation column on the widget row)
- position — grid coordinates
(x, y, w, h) for react-grid-layout
The aggregation engine runs against DO Postgres agent_events with parameterized SQL — workspace_id and brand_id are always added to the predicate from the request context, regardless of user-supplied filters, giving brand isolation by construction. Results are cached in Redis for 60 seconds.
Roles
- Members can view dashboards
- Owners and admins can create, edit, and delete
Brand context
Every read and write requires brand context. Send it as either:
X-Active-Brand: <uuid> header (preferred), or
?brand_id=<uuid> query parameter (fallback for callers that cannot send headers)
For the POST /aggregate endpoint the header / query is the only source of brand context — the request body does not carry brand. This eliminates the class of bugs where a body field disagrees with the header.
See also
Last modified on April 25, 2026