OverviewInsight config reference

Insight config reference

The shape of the config object on POST /builder/insights — an explore spec under config.query plus a visualization type — with a working example per insight_type, the type-to-spec rules, and how to read the result.

Overview

An insight is a saved explore query plus a visualization. Its config is a JSON object with one required key:

{
  "name": "Daily visitors",
  "insight_type": "line",
  "config": {
    "query": {
      "kind": "trend",
      "date_range": { "preset": "30d" },
      "interval": "day",
      "series": [{ "event": "$pageview", "math": "dau", "label": "Visitors" }],
      "compare": "previous_period"
    }
  }
}
  • config.query is the same object you would POST /client/v1/data/query. The full grammar — series maths, filters, breakdowns, funnels, limits — lives on the Explore queries page and is not repeated here.
  • insight_type is only the visualization. It never changes what is computed.
  • Any other key in config is stored verbatim as display settings and never read by the runner.

config is validated on write. POST /builder/insights returns 400 { "error": "invalid config", "details": [...] } for a missing or invalid query, or a kind that doesn't fit the insight_type. Nothing invalid reaches the table, so a saved insight cannot fail to compile later.

Which spec per type

insight_typequery.kindTypical specRenders
numbertrendinterval, 1–5 series, optional comparea scorecard per series (total + delta vs previous period) over one shared trend chart
linetrendinterval, 1–5 series, optional breakdownone line per series × breakdown value
bartrendno interval, breakdown or several seriesone bar per breakdown value or series
pietrendno interval, breakdownshare per breakdown value
tabletrendwith or without intervalseries × buckets, or one row per series
funnelfunnel2–10 steps, optional window, grain, breakdownstep bars with conversion rates

The one hard rule: funnelkind: "funnel". Every other type must carry a kind: "trend" spec. A trend without interval returns one value per series; with interval it returns data[] per bucket and a total. bar and pie read the totals either way, so leaving interval off is cheaper for those. number renders either way too, but without an interval it has no buckets to draw and shows the scorecards alone.

Examples

number — conversions this week vs last

{
  "insight_type": "number",
  "config": {
    "query": {
      "kind": "trend",
      "date_range": { "preset": "7d" },
      "series": [{ "event": "purchase", "math": "total", "label": "Purchases" }],
      "compare": "previous_period"
    }
  }
}

line — visitors by device

{
  "insight_type": "line",
  "config": {
    "query": {
      "kind": "trend",
      "date_range": { "preset": "30d" },
      "interval": "day",
      "series": [{ "event": "$pageview", "math": "dau" }],
      "breakdown": { "property": "$device_type", "limit": 3 }
    }
  }
}

pie — traffic sources

{
  "insight_type": "pie",
  "config": {
    "query": {
      "kind": "trend",
      "date_range": { "preset": "30d" },
      "series": [{ "event": "$pageview", "math": "unique_sessions" }],
      "breakdown": { "property": "$referring_domain", "limit": 8 }
    }
  }
}

table — top pages

{
  "insight_type": "table",
  "config": {
    "query": {
      "kind": "trend",
      "date_range": { "preset": "7d" },
      "series": [
        { "event": "$pageview", "math": "total", "label": "Views" },
        { "event": "$pageview", "math": "dau", "label": "Visitors" }
      ],
      "breakdown": { "property": "$pathname", "limit": 20 }
    }
  }
}

funnel — checkout

{
  "insight_type": "funnel",
  "config": {
    "query": {
      "kind": "funnel",
      "date_range": { "preset": "30d" },
      "steps": [
        { "event": "$pageview", "filters": [{ "property": "$pathname", "op": "eq", "value": "/pricing" }], "label": "Pricing" },
        { "event": "checkout_started" },
        { "event": "purchase" }
      ],
      "window": { "value": 3, "unit": "day" }
    }
  }
}

Set "grain": "person" on a funnel whose steps cross from the browser into server-side or CRM events — those arrive under a contact id, not the visitor's distinct_id, so the default device grain cannot link them. See Explore queries for the cost.

Discovering events and properties

Don't hardcode names. The Data API answers, with the same key:

  • GET /client/v1/data/events/summary — event names with counts for a window.
  • GET /client/v1/data/properties?event=<name> — the property catalog (typed keys and how often they occur).
  • GET /client/v1/data/query/values?property=<key>&event=<name>&search= — top values of one property, for filter and breakdown pickers.

Validating and previewing

Validate on every edit

POST /insights/validate runs the write-path rules with no query: { "ok": true } or 400 { "ok": false, "errors": [...] }.

Preview a draft

POST /insights/preview runs { insight_type, config, dateRange?, granularity? } in memory and returns the same payload as the saved runner. Nothing is persisted; identical drafts are served from a 120-second cache.

Save

POST /insights stores it. Change the config later with the same call plus id; change name, description, tags or favorite with PATCH /insights/{id}.

Per-run overrides

POST /insights/{id}/query, POST /public/query and /insights/preview accept two optional overrides that are applied onto the stored spec for that run only:

  • dateRange{ "preset": "7d" } (24h, 7d, 14d, 30d, 90d, 365d) or { "preset": "custom", "from": "2026-08-01", "to": "2026-08-31" }. Anything else keeps the stored range.
  • granularityhour, day, week, month; replaces query.interval. Ignored for funnels.

Dashboards use these to apply one date range and interval to every tile.

Reading the results

The run payload is the explore response with the insight attached — see the Queries group for the full schema. In short:

FieldTrendFunnel
kind"trend""funnel"
buckets / previous_bucketsISO labels, present with interval
series[]{ label, math, event, breakdown_value?, data[] | value, previous[] | previous_value, total }
breakdown_values[]values in rank order, $$other last
steps[]{ step, label, event, users, conversion_from_first, conversion_from_previous }
breakdown[]{ value, steps[] } per breakdown value
metaresolved window, interval, breakdown settings, sql_ms, rows_readsame, plus grain
config, cached, sqlstored config, cache hit flag, compiled query (sql is omitted on /public/query)same

For a number, read each entry's total (with interval) or value for the scorecard, the previous* counterpart for the delta, and buckets against data for the chart beneath them. For a line, plot buckets against each entry's data. For bar/pie, one slice per series[] entry using total/value.

Dashboards: filters and theme

POST /builder/dashboards takes filters and theme objects, both {} by default.

filtersobject

Default date range and granularity applied to every tile on load. Shape: { "dateRange": { "preset": "7d" }, "granularity": "day" } (the per-run overrides above). Embed viewers can override via ?date_range=/?granularity= query params.

themeobject

Reserved; currently unused — leave it {}. Embed theming is driven by query params on the embed URL (theme, theme_primary, theme_secondary, theme_heading_font, theme_body_font, transparent, hideHeader, branding), not by this stored object.