The Analytics area publishes 19 REST endpoints. Paths are relative to https://api.lepsto.com; see Authentication for the required header.
GET/analytics/catalog
Read the analytics semantic catalog: every entity this product may chart, with its declared metrics, dimensions and person-graph identity kinds. Call this FIRST to discover the exact `entity`, `metric` and `dimension` names analytics_metric_query and analytics_funnel_run accept — those names are resolved against this catalog and a name that is not in it is rejected. A metric `unit` describes raw money scale and its currency dimension; a dimension label link names the related entity and columns used for readable group labels. `dropped_metrics`, when present, lists metrics left out of `entities` that cannot be queried: `reason` is type_unknown (a metric type this analytics build does not support yet), aggregate_unknown (a ratio part with an unsupported aggregate) or column_missing (a count_distinct without a column).
MCP tool: analytics_catalog_get
Parameters
No parameters.
Response
Returns object.
| Field | Type | Always | Description |
|---|
cycle | object | null
| • | — |
entities | object[]
| • | — |
product_id | string
| • | — |
generated_at | string
| • | — |
dropped_metrics | object[]
| — | — |
GET/analytics/dashboards
List the saved dashboards of the current product, newest first. Narrow by an exact tag or by a case-insensitive substring of the name or slug. Returns definitions, not results.
MCP tool: analytics_dashboard_list
Parameters
| Name | In | Type | Required | Description |
|---|
tag | query | string
| — | Exact tag the dashboard must carry |
q | query | string
| — | Case-insensitive substring of the name or slug |
Response
Returns object.
| Field | Type | Always | Description |
|---|
dashboards | object[]
| • | — |
POST/analytics/dashboards
Save a named dashboard for the current product: up to forty tiles, each either a reference to a saved insight by its uuid (from analytics_insight_list) or a block of markdown, each placed on a twelve-column grid. The slug is the address the dashboard is read by and cannot be changed later. `filters` (up to 8) are the filters the board offers its viewers: {key, label, dimension, map?}. A filter applies to every series whose entity has a dimension named exactly `dimension`; for an entity whose field is named differently, `map` names it ({"googleads_campaign_daily": "campaign_id"}). Similar names are never matched. On a funnel a filter applies to its FIRST step only — it picks who enters, and later steps follow those people even where their entity has no such field (a signup with no utm_campaign) — so a funnel is filtered when its first step has the dimension and unfiltered otherwise. Values are supplied per run (analytics_dashboard_run). Build in order: headline numbers → pivot by campaign → daily dynamics. Example for guidance, not a template: 4–6 bold_number tiles for spend, clicks, visits, unique visitors, pricing and signups with compare; a pivot with cost by campaign_id and group_label campaign_name, clicks, page_view count and unique_visitors by utm_campaign, and a funnel visit → url starts_with /pricing → signup_completed grouped by utm_campaign, every step labelled; rows_label Кампания and formulas cost per click (f1 = A / B) and cost per signup (f2 = A / E). Then a daily line of visits. Offer filters for country and campaign; map campaign to utm_campaign on events and campaign_id on Google Ads. Returns the stored dashboard plus warnings[] {code, message, path} about the board itself: a hard-coded campaign id (an eq, or an in/not_in of up to 5 ids, on utm_campaign or campaign_id) when another tile groups by that field. It does not re-check the saved insights the tiles reference; their warnings come from analytics_insight_create and analytics_insight_update. Stores the arrangement only; it does not run it. Every insight a tile names must already exist in this product, so a stored dashboard always resolves.
MCP tool: analytics_dashboard_create
Parameters
| Name | In | Type | Required | Description |
|---|
slug | body | string
| • | — |
name | body | string
| • | — |
tiles | body | object[]
| • | — |
tags | body | string[]
| — | — |
filters | body | object[]
| — | — |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | — |
name | string
| • | — |
slug | string
| • | — |
tags | string[]
| — | — |
tiles | object[]
| • | — |
filters | object[]
| — | — |
warnings | object[]
| • | — |
created_at | string (date-time)
| • | — |
created_by | string | null
| • | — |
product_id | string (uuid)
| • | — |
updated_at | string (date-time)
| • | — |
DELETE/analytics/dashboards/:id
Permanently delete one saved dashboard of the current product. The insights its tiles referred to are not deleted.
MCP tool: analytics_dashboard_delete
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The dashboard's uuid; slugs are not accepted here |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | — |
deleted | booleanone of: true
| • | — |
PATCH/analytics/dashboards/:id
Change fields of a saved dashboard of the current product. Only the fields supplied are changed; the rest keep their stored values. Supplying tiles replaces the whole arrangement, so send the tiles you want to keep along with the new ones; supplying filters replaces them the same way. The slug cannot be changed — create a new dashboard instead. `filters` (up to 8) are the filters the board offers its viewers: {key, label, dimension, map?}. A filter applies to every series whose entity has a dimension named exactly `dimension`; for an entity whose field is named differently, `map` names it ({"googleads_campaign_daily": "campaign_id"}). Similar names are never matched. On a funnel a filter applies to its FIRST step only — it picks who enters, and later steps follow those people even where their entity has no such field (a signup with no utm_campaign) — so a funnel is filtered when its first step has the dimension and unfiltered otherwise. Values are supplied per run (analytics_dashboard_run). Build in order: headline numbers → pivot by campaign → daily dynamics. Example for guidance, not a template: 4–6 bold_number tiles for spend, clicks, visits, unique visitors, pricing and signups with compare; a pivot with cost by campaign_id and group_label campaign_name, clicks, page_view count and unique_visitors by utm_campaign, and a funnel visit → url starts_with /pricing → signup_completed grouped by utm_campaign, every step labelled; rows_label Кампания and formulas cost per click (f1 = A / B) and cost per signup (f2 = A / E). Then a daily line of visits. Offer filters for country and campaign; map campaign to utm_campaign on events and campaign_id on Google Ads. Returns the stored dashboard plus warnings[] {code, message, path} about the board itself: a hard-coded campaign id (an eq, or an in/not_in of up to 5 ids, on utm_campaign or campaign_id) when another tile groups by that field. It does not re-check the saved insights the tiles reference; their warnings come from analytics_insight_create and analytics_insight_update.
MCP tool: analytics_dashboard_update
Parameters
| Name | In | Type | Required | Description |
|---|
name | body | string
| — | — |
tiles | body | object[]
| — | — |
tags | body | string[]
| — | — |
filters | body | object[]
| — | — |
id | path | string (uuid)
| • | The dashboard's uuid; slugs are not accepted here |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | — |
name | string
| • | — |
slug | string
| • | — |
tags | string[]
| — | — |
tiles | object[]
| • | — |
filters | object[]
| — | — |
warnings | object[]
| • | — |
created_at | string (date-time)
| • | — |
created_by | string | null
| • | — |
product_id | string (uuid)
| • | — |
updated_at | string (date-time)
| • | — |
GET/analytics/dashboards/:idOrSlug
Read one saved dashboard of the current product by its slug or its id. Returns the tiles and their layout, not results.
MCP tool: analytics_dashboard_get
Parameters
| Name | In | Type | Required | Description |
|---|
idOrSlug | path | string
| • | The dashboard's slug, or its uuid |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | — |
name | string
| • | — |
slug | string
| • | — |
tags | string[]
| — | — |
tiles | object[]
| • | — |
filters | object[]
| — | — |
created_at | string (date-time)
| • | — |
created_by | string | null
| • | — |
product_id | string (uuid)
| • | — |
updated_at | string (date-time)
| • | — |
POST/analytics/dashboards/:idOrSlug/run
Run every tile of a saved dashboard of the current product and return one entry per tile, in board order: the tile itself, and what it produced. An insight tile carries exactly what analytics_insight_run answers for that insight; a text tile carries a null result, because its content is the tile. `from` and `to` override the window of every insight tile for this call only, changing nothing stored. `filters` gives values to the board's filters by key ({"country": {"op": "eq", "value": "DE"}}); a series the filter cannot reach runs unfiltered and is listed in its tile's `filters_skipped`. A funnel series takes a filter on its first step only, so it is filtered exactly when that step's entity has the dimension; otherwise it is listed with every step (on a pivot, every <key>.<step> column) as not filtered. `compare` also reads a second range, returned per tile as `compare` (totals, and points laid over the current buckets; a funnel compares its last step; a pivot compares its totals only), whose `compare.window` is always the exact range read: "previous_period" is the window right before with as many days as have elapsed of this one (a window running past today counts through today); "previous_week" is the same days one week earlier; {from, to} is any range of up to 366 whole UTC days (YYYY-MM-DD, inclusive, to not before from), clipped to today, starting no later than today, and it may overlap the window (each is read on its own, so shared days count in both). Points pair by position, day 1 with day 1: a compared range of another length leaves the current buckets past its end with value and prev_bucket null, and a longer one's tail is cut from the points (its total still covers the whole range). A range with no data is totals 0 (null for an average or ratio) and points 0, not a missing compare. To compare yesterday with today: from and to today with compare {from: yesterday, to: yesterday}, or from yesterday to today with "previous_period" (yesterday and today against the two days before). A tile that fails carries an error with an HTTP status and does not stop the rest of the board, so check each tile's result before reading numbers. The call answers only when every tile is done; a heavy board (many tiles, many series) is better loaded tile by tile with analytics_dashboard_tile-run, after analytics_dashboard_get for the layout.
MCP tool: analytics_dashboard_run
Parameters
| Name | In | Type | Required | Description |
|---|
from | body | string (date-time)
| — | Inclusive window start; overrides the stored window |
to | body | string (date-time)
| — | Inclusive window end; overrides the stored window |
filters | body | object
| — | Values for the dashboard's filters, by filter key: a filter without its dimension, e.g. {"country": {"op": "eq", "value": "DE"}}. A key the dashboard does not declare is a 400 |
compare | body | string | objectone of: previous_period, previous_week
| — | Also read a second range; each insight tile then carries `compare`, whose `window` is the exact range read. "previous_period": as many days as have elapsed of this window, ending the day before it starts. "previous_week": the same days one week earlier. {from, to}: any range of up to 366 whole UTC days (YYYY-MM-DD, inclusive), clipped to today; it may overlap this window |
idOrSlug | path | string
| • | The dashboard's slug, or its uuid |
Response
Returns object.
| Field | Type | Always | Description |
|---|
tiles | object[]
| • | — |
dashboard | object
| • | — |
POST/analytics/dashboards/:idOrSlug/tiles/:tileId/run
Run one tile of a saved dashboard of the current product, by the tile's id (from analytics_dashboard_get), and return that tile's entry: the tile itself and what it produced — the same entry analytics_dashboard_run gives that tile for the same body. Takes the same `from`, `to`, `filters` and `compare` ("previous_period", "previous_week" or {from, to}; to compare yesterday with today, from and to today with compare {from: yesterday, to: yesterday}) as analytics_dashboard_run, checked against the whole board the same way (a filter key the board does not declare is a 400), and a funnel takes a filter on its first step only, exactly as there. Use it to fill a heavy board in progressively, one call per tile, keeping at most about 6 calls in flight per board (as many as analytics_dashboard_run runs at once). A text tile carries a null result; a tile that fails carries an error with an HTTP status rather than failing the call; a tile id the board does not have is a 404.
MCP tool: analytics_dashboard_tile-run
Parameters
| Name | In | Type | Required | Description |
|---|
from | body | string (date-time)
| — | Inclusive window start; overrides the stored window |
to | body | string (date-time)
| — | Inclusive window end; overrides the stored window |
filters | body | object
| — | Values for the dashboard's filters, by filter key: a filter without its dimension, e.g. {"country": {"op": "eq", "value": "DE"}}. A key the dashboard does not declare is a 400 |
compare | body | string | objectone of: previous_period, previous_week
| — | Also read a second range; each insight tile then carries `compare`, whose `window` is the exact range read. "previous_period": as many days as have elapsed of this window, ending the day before it starts. "previous_week": the same days one week earlier. {from, to}: any range of up to 366 whole UTC days (YYYY-MM-DD, inclusive), clipped to today; it may overlap this window |
idOrSlug | path | string
| • | The dashboard's slug, or its uuid |
tileId | path | string (uuid)
| • | The tile's id, as analytics_dashboard_get returns it |
Response
Returns object.
| Field | Type | Always | Description |
|---|
tile | object
| • | — |
result | object | null
| • | — |
POST/analytics/funnels/run
Run an ordered conversion funnel over the analytics semantic layer for the current product. Supply filters on a step to narrow its rows; omit filters to apply no step filters. Each step names a semantic entity and is counted in resolved persons from the identity graph, so a person is counted once per step no matter how many rows or identifiers they produced. Deterministic: entity, dimension and metric names are resolved against the semantic catalog, never generated. A step whose entity has a `propertiesColumn` in the catalog also accepts filters on `props.<key>`, compared as a string. Step filters support legacy {dimension, eq} or op eq, neq, in, not_in, starts_with, is_null, is_not_null; in and not_in take 1–100 values of one type; starts_with takes a string and works on text dimensions. neq and not_in retain NULL rows. Give each step a label (1–60 characters, e.g. Дошли до /pricing); every returned step carries label — the declared one, or its entity and filters when it has none. Prefer this over analytics_query_run whenever the question is a conversion between ordered steps.
MCP tool: analytics_funnel_run
Parameters
| Name | In | Type | Required | Description |
|---|
steps | body | object[]
| • | Ordered funnel steps, 2 to 5 |
from | body | string
| • | Inclusive first day (UTC) |
to | body | string
| • | Inclusive last day (UTC) |
window_days | body | integer
| — | Maximum days between two CONSECUTIVE steps for the later one to count |
grain | body | stringone of: day, week, month
| — | When set, also return per-step persons bucketed by step-entry time |
person_filter | body | stringone of: exclude_identified
| — | `exclude_identified` drops the rows of identified people: a person who has an email or a phone in the analytics person graph. The link is the row's identifier, not time, so a visitor's earlier anonymous rows are dropped too once that visitor is identified. Rows with no identifier (cookieless events) are never excluded, because there is nothing to resolve them by. The person graph is rebuilt on a schedule (hourly by default), so a newly identified person is excluded only after the next rebuild. Omit the field to count everyone. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
meta | object
| • | — |
steps | object[]
| • | — |
series | object[]
| — | — |
GET/analytics/insights
List the saved insights of the current product, newest first. Narrow by an exact tag or by a case-insensitive substring of the name or slug. Returns definitions, not results.
MCP tool: analytics_insight_list
Parameters
| Name | In | Type | Required | Description |
|---|
tag | query | string
| — | Exact tag the insight must carry |
q | query | string
| — | Case-insensitive substring of the name or slug |
Response
Returns object.
| Field | Type | Always | Description |
|---|
insights | object[]
| • | — |
POST/analytics/insights
Save a named insight for the current product: one to eight series over the analytics semantic layer, an optional formula combining them by key, a time window and how to display it. Use display bold_number for headline KPIs with dashboard compare, pivot for rows × metrics, line for daily dynamics, and steps for funnels. Give every series a human label when there are several. Filters neq and not_in keep NULL rows. Money stays raw; set the series format {kind: money, scale, currency} so readers see its scale and currency. Each series may have a trimmed display label (1–60 characters). Use series format for display, group_label for a grouped metric's label dimension, and formula_label with formula_format for a named formula. For several formulas use formulas [{key f1..f4, label 1–60, expr, format}] (up to 4) instead of formula — never both (400); each is evaluated exactly as formula is and its number is total[key] (and a pivot cell keyed by it). A percent value is a fraction (0.128 means 12.8%). display facts draws the first series as the base bar (100%) and each next series as a bar with its value and % of the base — use it for independent facts about the same visitors (e.g. base = visited from ads, then reached /pricing, clicked CTA, signed up), unlike steps which are sequential; it is for kind number only (400 otherwise) and runs like any number insight. Chart displays (display-only; refused with 400 at display, stacked or combo when the series cannot be drawn, saying what fits): pie for shares of a whole — one metric series with group_by, or 2–8 ungrouped metric series of one unit (format kind, and currency and scale for money), no percent, no formula; area for volume over time, kind trend only; stacked: true (bar or area only) stacks 2+ series or one grouped series of one unit, no percent, no formula; bar_horizontal for a ranking — one grouped metric series, or 2–8 ungrouped series of one unit (a funnel under kind number counts as its last step), no formula; combo for two scales over time (e.g. cost as bars, signups as a line) — kind trend, 2–4 ungrouped metric series, combo {line: [keys]} names the line series or formulas (default the last series), at least one series stays a bar; all bars must share one unit and all lines one unit (format kind, currency and scale for money; no format is a number), else 400 — move the odd series to the line or split the insight. A number insight may combine a funnel (its value is its last step over the window, ungrouped) with metrics in a formula, e.g. cost per signup = cost / funnel to signup_completed. A single metric series in a trend or number insight may use group_by with per-series top_n (1–20, default 8); grouped insights cannot have a formula. display steps shows a funnel as its steps (persons and conversion per step) and is the default for kind funnel; every other kind defaults to line, and steps is refused on them. Give every funnel step a label (1–60 characters): it is the step's column title and steps label; without one it reads as its entity and filters. A funnel series may set hidden_steps [0-based step indexes] — display-only, e.g. hide the entry step that another funnel already shows: hidden steps still count and every number, formula and total is unchanged; at least one step must stay visible. display pivot is a table of rows per group value across series: every series (a metric, or a funnel by a dimension of its first step) may carry its own group_by; rows are joined by the group value as an exact string. A grouped funnel counts a person in EVERY group they entered by, each from their first entry with that value, so a funnel row equals the same funnel with its first step filtered to that value; rows can overlap and its total is the distinct people (the ungrouped funnel), not the sum of rows. Join keys are exact, so the values must match (events.utm_campaign must equal the Google Ads campaign_id); formula runs per row and over the totals, and a formula or sort names a funnel series by its LAST step only (E means E.<last>); sort {by: series key | formula | f1..f4, dir} and top_n (1–50, default 20) order and cap the rows, the rest fold into other; rows_label (1–40) titles the row-header column. Worked example, the ads campaign table: display pivot, rows_label Кампания, series A label Расход = googleads_campaign_daily cost by campaign_id (format money), B label Клики = clicks by campaign_id, E label Путь = funnel by utm_campaign with steps [{label Уникальные, events page_view}, {label Дошли до /pricing, path starts_with /pricing}, {label Регистрации, signup_completed}], formulas [{key f1, label Цена клика, expr A / B, format money}, {key f2, label Цена регистрации, expr A / E, format money}], sort {by A, dir desc}. The slug is the address the insight is read by and cannot be changed later. A save is refused (400, naming the field to fix) for several series without a label, for display pivot without group_by on any series, and for a series whose request does not suit the kind (series.N.request: trend needs metric requests, number metric or funnel requests, funnel needs funnel requests, retention needs retention requests; only display pivot mixes metric and funnel series) — the same insight would fail to run. Otherwise it returns the stored insight plus warnings[] {code, message, path}: advice that does not block the save — series that differ only in one filter value of a dimension other than event_name, with no group_by yet and not combined by one formula (use group_by or pivot), a formula over money without a money formula_format (or a money formulas entry without a money format), a funnel step without a label (it would show as its entity and filters), a money-looking metric without a unit or series format, a window that starts after today (it has no days yet), a daily table longer than 31 rows (use line). A window.to after today is fine: a run clips it to today, so from a fixed date to a later date is the way to say since <date>. Stores the definition only; it does not run it. Series requests are validated with the same schemas analytics_metric_query and analytics_funnel_run accept, so a stored insight always runs.
MCP tool: analytics_insight_create
Parameters
| Name | In | Type | Required | Description |
|---|
slug | body | string
| • | — |
name | body | string
| • | — |
description | body | string
| — | — |
kind | body | stringone of: trend, funnel, number, retention
| • | — |
series | body | object[]
| • | — |
formula | body | string | null
| — | — |
formula_label | body | string
| — | — |
formula_format | body | object
| — | Display format for a raw value. kind percent means a fraction: 0.128 displays as 12.8%. Money stays raw at the declared scale; readers convert it for display with this format. |
formulas | body | object[]
| — | Up to four named formulas over the series keys, each its own column or line; use instead of formula |
rows_label | body | string
| — | Pivot only: the title of the row-header column, 1 to 40 characters (e.g. Кампания) |
window | body | object
| • | — |
grain | body | stringone of: day, week, month
| — | — |
display | body | stringone of: line, bar, bold_number, table, steps, pivot, facts, pie, area, bar_horizontal, combo
| — | How to show the insight. Defaults to steps for a funnel insight, line otherwise. steps is for a funnel only; facts is for kind number only; pivot is a table of rows per group value across series; pie, area, bar_horizontal and combo are refused when the series cannot be drawn that way |
stacked | body | boolean
| — | Display area or bar only: stack the series (or the groups of one grouped series); all in one unit. Defaults to false |
combo | body | object
| — | Display combo only: which series draw as the line |
sort | body | object
| — | Pivot only: which column orders the rows, and the direction |
top_n | body | integer
| — | Pivot only: rows kept, 1 to 50 (default 20); the rest are folded into other |
tags | body | string[]
| — | — |
target | body | object | null
| — | — |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | — |
kind | stringone of: trend, funnel, number, retention
| • | — |
name | string
| • | — |
slug | string
| • | — |
sort | object
| — | Pivot only: which column orders the rows, and the direction |
tags | string[]
| — | — |
combo | object
| — | Display combo only: which series draw as the line |
grain | stringone of: day, week, month
| — | — |
top_n | integer
| — | Pivot only: rows kept, 1 to 50 (default 20); the rest are folded into other |
series | object[]
| • | — |
target | object | null
| — | — |
window | object
| • | — |
display | stringone of: line, bar, bold_number, table, steps, pivot, facts, pie, area, bar_horizontal, combo
| — | — |
formula | string | null
| — | — |
stacked | boolean
| — | Display area or bar only: stack the series (or the groups of one grouped series); all in one unit. Defaults to false |
formulas | object[]
| — | Up to four named formulas over the series keys, each its own column or line; use instead of formula |
warnings | object[]
| • | — |
created_at | string (date-time)
| • | — |
created_by | string | null
| • | — |
product_id | string (uuid)
| • | — |
rows_label | string
| — | Pivot only: the title of the row-header column, 1 to 40 characters (e.g. Кампания) |
updated_at | string (date-time)
| • | — |
description | string
| — | — |
formula_label | string
| — | — |
formula_format | object
| — | Display format for a raw value. kind percent means a fraction: 0.128 displays as 12.8%. Money stays raw at the declared scale; readers convert it for display with this format. |
DELETE/analytics/insights/:id
Permanently delete one saved insight of the current product. Dashboards referring to it will no longer resolve it.
MCP tool: analytics_insight_delete
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The insight's uuid; slugs are not accepted here |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | — |
deleted | booleanone of: true
| • | — |
PATCH/analytics/insights/:id
Change fields of a saved insight of the current product. Only the fields supplied are changed; the rest keep their stored values. The slug cannot be changed — create a new insight instead. Returns the stored insight plus warnings[] for the row as it now stands. An update is refused only for what it changes: missing labels when it replaces series, pivot without group_by when it changes display or series, a chart display that cannot be drawn when it changes kind, display, series, stacked, combo or a formula; the same findings on an older insight come back as warnings. Switching display drops a stored stacked or combo the new display does not use (the response shows them gone). A series replaced here carries its own hidden_steps (display-only; omit it to show every step).
MCP tool: analytics_insight_update
Parameters
| Name | In | Type | Required | Description |
|---|
name | body | string
| — | — |
description | body | string
| — | — |
kind | body | stringone of: trend, funnel, number, retention
| — | — |
series | body | object[]
| — | — |
formula | body | string | null
| — | — |
formula_label | body | string | null
| — | — |
formula_format | body | object | null
| — | Display format for a raw value. kind percent means a fraction: 0.128 displays as 12.8%. Money stays raw at the declared scale; readers convert it for display with this format. |
formulas | body | object[] | null
| — | Up to four named formulas over the series keys, each its own column or line; use instead of formula |
rows_label | body | string | null
| — | — |
window | body | object
| — | — |
grain | body | stringone of: day, week, month
| — | — |
display | body | stringone of: line, bar, bold_number, table, steps, pivot, facts, pie, area, bar_horizontal, combo
| — | — |
stacked | body | boolean | null
| — | Display area or bar only: stack the series (or the groups of one grouped series); all in one unit. Defaults to false |
combo | body | object | null
| — | — |
sort | body | object | null
| — | — |
top_n | body | integer | null
| — | — |
tags | body | string[]
| — | — |
target | body | object | null
| — | — |
id | path | string (uuid)
| • | The insight's uuid; slugs are not accepted here |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | — |
kind | stringone of: trend, funnel, number, retention
| • | — |
name | string
| • | — |
slug | string
| • | — |
sort | object
| — | Pivot only: which column orders the rows, and the direction |
tags | string[]
| — | — |
combo | object
| — | Display combo only: which series draw as the line |
grain | stringone of: day, week, month
| — | — |
top_n | integer
| — | Pivot only: rows kept, 1 to 50 (default 20); the rest are folded into other |
series | object[]
| • | — |
target | object | null
| — | — |
window | object
| • | — |
display | stringone of: line, bar, bold_number, table, steps, pivot, facts, pie, area, bar_horizontal, combo
| — | — |
formula | string | null
| — | — |
stacked | boolean
| — | Display area or bar only: stack the series (or the groups of one grouped series); all in one unit. Defaults to false |
formulas | object[]
| — | Up to four named formulas over the series keys, each its own column or line; use instead of formula |
warnings | object[]
| • | — |
created_at | string (date-time)
| • | — |
created_by | string | null
| • | — |
product_id | string (uuid)
| • | — |
rows_label | string
| — | Pivot only: the title of the row-header column, 1 to 40 characters (e.g. Кампания) |
updated_at | string (date-time)
| • | — |
description | string
| — | — |
formula_label | string
| — | — |
formula_format | object
| — | Display format for a raw value. kind percent means a fraction: 0.128 displays as 12.8%. Money stays raw at the declared scale; readers convert it for display with this format. |
GET/analytics/insights/:idOrSlug
Read one saved insight of the current product by its slug or its id. Returns the definition, not results.
MCP tool: analytics_insight_get
Parameters
| Name | In | Type | Required | Description |
|---|
idOrSlug | path | string
| • | The insight's slug, or its uuid |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | — |
kind | stringone of: trend, funnel, number, retention
| • | — |
name | string
| • | — |
slug | string
| • | — |
sort | object
| — | Pivot only: which column orders the rows, and the direction |
tags | string[]
| — | — |
combo | object
| — | Display combo only: which series draw as the line |
grain | stringone of: day, week, month
| — | — |
top_n | integer
| — | Pivot only: rows kept, 1 to 50 (default 20); the rest are folded into other |
series | object[]
| • | — |
target | object | null
| — | — |
window | object
| • | — |
display | stringone of: line, bar, bold_number, table, steps, pivot, facts, pie, area, bar_horizontal, combo
| — | — |
formula | string | null
| — | — |
stacked | boolean
| — | Display area or bar only: stack the series (or the groups of one grouped series); all in one unit. Defaults to false |
formulas | object[]
| — | Up to four named formulas over the series keys, each its own column or line; use instead of formula |
created_at | string (date-time)
| • | — |
created_by | string | null
| • | — |
product_id | string (uuid)
| • | — |
rows_label | string
| — | Pivot only: the title of the row-header column, 1 to 40 characters (e.g. Кампания) |
updated_at | string (date-time)
| • | — |
description | string
| — | — |
formula_label | string
| — | — |
formula_format | object
| — | Display format for a raw value. kind percent means a fraction: 0.128 displays as 12.8%. Money stays raw at the declared scale; readers convert it for display with this format. |
POST/analytics/insights/:idOrSlug/run
Run a saved insight of the current product and return its numbers: every series executed over the resolved window and aligned onto the same buckets, the formula evaluated bucket by bucket, a total per series, optional series_labels keyed by series key, and whether the target was met. Grouped metric insights return ranked groups (top_n plus other for count/sum), and meta with group_by and group counts. Money stays raw at its scale; series_format tells the app how to convert and display it. When grouped by currency, each value group carries its own format and currency; mixed-currency headlines, formula totals, and the other group are null with a reason in meta. A funnel insight also returns steps: per series key, every step over the whole window with label, persons, conversion_from_first and conversion_from_prev — a step nobody reached is 0, never missing; its series and total remain the last step per bucket. A pivot insight returns pivot: rows {key, label, cells} per group value merged across series by exact string key (a cell is null where that series has no row), other (the rows past top_n), total and total_reasons, sort and meta {rows_total, rows_dropped, top_n}; a funnel series is one cell per step keyed <key>.<stepIndex> with series_steps labels, and step_pct on each row, on other and as total_step_pct gives each step cell over that row's first step (1 for step 0, null when step 0 is 0), so a cell reads 41 · 13.6 %; rows_label is the row-header title when the insight declares one. An insight with formulas returns formulas (per key, bucket by bucket; absent for a pivot, whose numbers are the cells f1..f4), formula_labels and formula_formats keyed by formula key in definition order, and each formula's total under total[key]; compare carries formulas and total[key] the same way. Totals are honest: count and sum are summed over all rows; count_distinct, avg and ratio are read ungrouped over the window; a grouped funnel's rows overlap (a person is in every group they entered by), so its step totals are the ungrouped funnel — distinct people — and its other is read on its own: the distinct people who entered by any group past top_n. Where a total cannot be read it is null with the reason in total_reasons (other follows the same rule), never a wrong number. A pivot has no buckets: series is empty and formula null. Missing count/sum buckets are zero. The window comes from the stored definition — relative windows and the current cycle are resolved at call time — and `from` and `to` override it for this call only, changing nothing stored. Use this rather than rebuilding the insight out of analytics_metric_query calls: the alignment and the formula are what make the numbers comparable.
MCP tool: analytics_insight_run
Parameters
| Name | In | Type | Required | Description |
|---|
from | body | string (date-time)
| — | Inclusive window start; overrides the stored window |
to | body | string (date-time)
| — | Inclusive window end; overrides the stored window |
idOrSlug | path | string
| • | The insight's slug, or its uuid |
Response
Returns object.
| Field | Type | Always | Description |
|---|
meta | object
| — | — |
extra | object
| — | — |
pivot | object
| — | — |
steps | object
| — | — |
total | object
| • | — |
groups | object[]
| — | — |
series | object
| • | — |
window | object
| • | — |
compare | object
| — | — |
formula | object[] | null
| • | — |
insight | object | null
| • | — |
formulas | object
| — | — |
formula_label | string
| — | — |
series_format | object
| • | — |
series_labels | object
| — | — |
target_status | string | nullone of: met, missed
| • | — |
formula_labels | object
| — | — |
filters_skipped | object[]
| — | — |
formula_formats | object
| — | — |
POST/analytics/metrics/query
Compute declared metrics from the analytics semantic layer for the current product, bucketed by time. Supply group_by to split each time bucket by one dimension; omit it to aggregate without dimension grouping. Deterministic: entity, metric and dimension names are resolved against the semantic catalog, never generated. Prefer this over analytics_query_run whenever the question is a metric over time — no LLM. Where the catalog gives an entity a `propertiesColumn`, `group_by` and a filter dimension also accept `props.<key>` to read one raw event property; property values always compare as strings. Filters support legacy {dimension, eq} or op eq, neq, in, not_in, starts_with, is_null, is_not_null; in and not_in take 1–100 values of one type; starts_with takes a string and works on text dimensions. neq and not_in retain NULL rows.
MCP tool: analytics_metric_query
Parameters
| Name | In | Type | Required | Description |
|---|
entity | body | string
| • | Semantic view name from the catalog |
metrics | body | string[]
| • | Declared metric names to compute. A metric of type `count_distinct` must be the only one: request it separately |
grain | body | stringone of: day, week, month
| • | Time bucket size |
from | body | string
| • | Inclusive first day (UTC) |
to | body | string
| • | Inclusive last day (UTC) |
group_by | body | string
| — | One declared dimension, or `props.<key>`, to group by |
filters | body | object[]
| — | Filters on declared dimensions or `props.<key>`: legacy eq, or op eq, neq, in, not_in (1–100 values of one type), starts_with (string value on text dimensions), is_null, is_not_null. neq and not_in retain NULL rows. |
person_filter | body | stringone of: exclude_identified
| — | `exclude_identified` drops the rows of identified people: a person who has an email or a phone in the analytics person graph. The link is the row's identifier, not time, so a visitor's earlier anonymous rows are dropped too once that visitor is identified. Rows with no identifier (cookieless events) are never excluded, because there is nothing to resolve them by. The person graph is rebuilt on a schedule (hourly by default), so a newly identified person is excluded only after the next rebuild. Omit the field to count everyone. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
meta | object
| • | — |
series | object[]
| • | — |
POST/analytics/persons/timeline
Return one resolved person's profile and their chronological activity across every identity-bearing semantic entity of the current product. Address the person either by person_id or by any single identifier (email, phone, users_id, anonymous_id), which is normalised exactly as the identity graph normalised it. Deterministic: no LLM, and only catalog-declared columns are returned. Prefer this over analytics_query_run whenever the question is about one specific person.
MCP tool: analytics_person_timeline
Parameters
| Name | In | Type | Required | Description |
|---|
person_id | body | string
| — | Graph person id, when already known |
identifier | body | object
| — | Any single identifier belonging to the person |
from | body | string
| — | Inclusive first day (UTC); unbounded when omitted |
to | body | string
| — | Inclusive last day (UTC); unbounded when omitted |
limit | body | integer
| — | Maximum events to return, newest first |
Response
Returns object.
| Field | Type | Always | Description |
|---|
meta | object
| • | — |
events | object[]
| • | — |
person | object
| • | — |
truncated | boolean
| • | — |
POST/analytics/query
Answer a natural-language analytics question for the current product. Fallback surface: it routes the question to analytics_metric_query or analytics_funnel_run when it fits one of them and returns that tool's exact request and response in `structured`; it generates SQL only when neither fits. Call analytics_metric_query, analytics_funnel_run or analytics_person_timeline directly when you already know the shape of the question — those are deterministic and cheaper.
MCP tool: analytics_query_run
Parameters
| Name | In | Type | Required | Description |
|---|
q | body | string
| • | Natural-language analytics question |
Response
Returns object.
| Field | Type | Always | Description |
|---|
sql | string
| • | — |
rows | object[]
| • | — |
answer | string
| • | — |
truncated | boolean
| • | — |
structured | object
| — | — |
explanation | string
| • | — |
POST/analytics/retention/run
Measure day-N retention over the analytics semantic layer for the current product: of the subjects whose FIRST matching cohort event falls in a bucket, how many produced a return event on day N (within `return_window_days` after it). The cohort key is `subject` — an event property (`props.<key>`, e.g. the customer product id) or `identity` for the entity's declared identity columns — and each subject is counted exactly once. Deterministic: entity and dimension names are resolved against the semantic catalog, never generated. A bucket whose day-N horizon has not elapsed yet is still returned, with `retained` and `rate` NULL rather than 0 — null means "not measurable yet", 0 means "measured, nobody came back", and a caller must not confuse them. `total` therefore covers the measurable buckets ONLY, so a half-elapsed cohort never drags the headline rate down; `meta.unmeasurable_buckets` says how many were left out.
MCP tool: analytics_retention_run
Parameters
| Name | In | Type | Required | Description |
|---|
entity | body | string
| • | Semantic view name from the catalog |
subject | body | string
| • | Cohort key: "identity" or `props.<key>` |
cohort | body | object
| — | The starting event; empty filters mean any row of the entity |
return | body | object
| — | The coming-back event; empty filters mean any row of the entity |
day | body | integer
| • | N: a subject is retained if it has a return event at least N days after its cohort event |
return_window_days | body | integer
| — | Length of the window that opens on day N; a return event must fall inside it |
from | body | string
| • | Inclusive first day of the cohort range (UTC) |
to | body | string
| • | Inclusive last day of the cohort range (UTC) |
grain | body | stringone of: day, week, month
| • | Cohort bucket size, applied to each subject's FIRST cohort event |
Response
Returns object.
| Field | Type | Always | Description |
|---|
meta | object
| • | — |
total | object
| • | — |
buckets | object[]
| • | — |
Was this page helpful?Thanks for your feedback!