Skip to content

Analytics API

REST endpoints for the Analytics area of Lessly.

The Analytics area publishes 18 REST endpoints. Paths are relative to https://api.lessly.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.

MCP tool: analytics_catalog_get

Parameters

No parameters.

Response

Returns object.

FieldTypeAlwaysDescription
cycleobject | null
entitiesobject[]
product_idstring
generated_atstring
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

NameInTypeRequiredDescription
tagquerystringExact tag the dashboard must carry
qquerystringCase-insensitive substring of the name or slug

Response

Returns object.

FieldTypeAlwaysDescription
dashboardsobject[]
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. 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

NameInTypeRequiredDescription
slugbodystring
namebodystring
tilesbodyobject[]
tagsbodystring[]

Response

Returns object.

FieldTypeAlwaysDescription
idstring (uuid)
namestring
slugstring
tagsstring[]
tilesobject[]
created_atstring (date-time)
created_bystring | null
product_idstring (uuid)
updated_atstring (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

NameInTypeRequiredDescription
idpathstring (uuid)The dashboard's uuid; slugs are not accepted here

Response

Returns object.

FieldTypeAlwaysDescription
idstring (uuid)
deletedbooleanone 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. The slug cannot be changed — create a new dashboard instead.

MCP tool: analytics_dashboard_update

Parameters

NameInTypeRequiredDescription
namebodystring
tilesbodyobject[]
tagsbodystring[]
idpathstring (uuid)The dashboard's uuid; slugs are not accepted here

Response

Returns object.

FieldTypeAlwaysDescription
idstring (uuid)
namestring
slugstring
tagsstring[]
tilesobject[]
created_atstring (date-time)
created_bystring | null
product_idstring (uuid)
updated_atstring (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

NameInTypeRequiredDescription
idOrSlugpathstringThe dashboard's slug, or its uuid

Response

Returns object.

FieldTypeAlwaysDescription
idstring (uuid)
namestring
slugstring
tagsstring[]
tilesobject[]
created_atstring (date-time)
created_bystring | null
product_idstring (uuid)
updated_atstring (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. 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.

MCP tool: analytics_dashboard_run

Parameters

NameInTypeRequiredDescription
frombodystring (date-time)Inclusive window start; overrides the stored window
tobodystring (date-time)Inclusive window end; overrides the stored window
idOrSlugpathstringThe dashboard's slug, or its uuid

Response

Returns object.

FieldTypeAlwaysDescription
tilesobject[]
dashboardobject
POST/analytics/funnels/run

Run an ordered conversion funnel over the analytics semantic layer for the current product. Each step names a semantic entity (optionally narrowed by declared-dimension filters) 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 may also filter on `props.<key>`, compared as a string. Prefer this over analytics_query_run whenever the question is a conversion between ordered steps.

MCP tool: analytics_funnel_run

Parameters

NameInTypeRequiredDescription
stepsbodyobject[]Ordered funnel steps, 2 to 5
frombodystringInclusive first day (UTC)
tobodystringInclusive last day (UTC)
window_daysbodyintegerMaximum days between two CONSECUTIVE steps for the later one to count
grainbodystringone of: day, week, monthWhen set, also return per-step persons bucketed by step-entry time

Response

Returns object.

FieldTypeAlwaysDescription
metaobject
stepsobject[]
seriesobject[]
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

NameInTypeRequiredDescription
tagquerystringExact tag the insight must carry
qquerystringCase-insensitive substring of the name or slug

Response

Returns object.

FieldTypeAlwaysDescription
insightsobject[]
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. The slug is the address the insight is read by and cannot be changed later. 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

NameInTypeRequiredDescription
slugbodystring
namebodystring
descriptionbodystring
kindbodystringone of: trend, funnel, number, retention
seriesbodyobject[]
formulabodystring | null
windowbodyobject
grainbodystringone of: day, week, month
displaybodystringone of: line, bar, bold_number, table
tagsbodystring[]
targetbodyobject | null

Response

Returns object.

FieldTypeAlwaysDescription
idstring (uuid)
kindstringone of: trend, funnel, number, retention
namestring
slugstring
tagsstring[]
grainstringone of: day, week, month
seriesobject[]
targetobject | null
windowobject
displaystringone of: line, bar, bold_number, table
formulastring | null
created_atstring (date-time)
created_bystring | null
product_idstring (uuid)
updated_atstring (date-time)
descriptionstring
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

NameInTypeRequiredDescription
idpathstring (uuid)The insight's uuid; slugs are not accepted here

Response

Returns object.

FieldTypeAlwaysDescription
idstring (uuid)
deletedbooleanone 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.

MCP tool: analytics_insight_update

Parameters

NameInTypeRequiredDescription
namebodystring
descriptionbodystring
kindbodystringone of: trend, funnel, number, retention
seriesbodyobject[]
formulabodystring | null
windowbodyobject
grainbodystringone of: day, week, month
displaybodystringone of: line, bar, bold_number, table
tagsbodystring[]
targetbodyobject | null
idpathstring (uuid)The insight's uuid; slugs are not accepted here

Response

Returns object.

FieldTypeAlwaysDescription
idstring (uuid)
kindstringone of: trend, funnel, number, retention
namestring
slugstring
tagsstring[]
grainstringone of: day, week, month
seriesobject[]
targetobject | null
windowobject
displaystringone of: line, bar, bold_number, table
formulastring | null
created_atstring (date-time)
created_bystring | null
product_idstring (uuid)
updated_atstring (date-time)
descriptionstring
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

NameInTypeRequiredDescription
idOrSlugpathstringThe insight's slug, or its uuid

Response

Returns object.

FieldTypeAlwaysDescription
idstring (uuid)
kindstringone of: trend, funnel, number, retention
namestring
slugstring
tagsstring[]
grainstringone of: day, week, month
seriesobject[]
targetobject | null
windowobject
displaystringone of: line, bar, bold_number, table
formulastring | null
created_atstring (date-time)
created_bystring | null
product_idstring (uuid)
updated_atstring (date-time)
descriptionstring
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, and whether the target was met. 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

NameInTypeRequiredDescription
frombodystring (date-time)Inclusive window start; overrides the stored window
tobodystring (date-time)Inclusive window end; overrides the stored window
idOrSlugpathstringThe insight's slug, or its uuid

Response

Returns object.

FieldTypeAlwaysDescription
extraobject
totalobject
seriesobject
windowobject
formulaobject[] | null
insightobject | null
target_statusstring | nullone of: met, missed
POST/analytics/metrics/query

Compute declared metrics from the analytics semantic layer for the current product, bucketed by time and optionally grouped by one declared dimension. 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, no SQL generation. 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.

MCP tool: analytics_metric_query

Parameters

NameInTypeRequiredDescription
entitybodystringSemantic view name from the catalog
metricsbodystring[]Declared metric names to compute
grainbodystringone of: day, week, monthTime bucket size
frombodystringInclusive first day (UTC)
tobodystringInclusive last day (UTC)
group_bybodystringOne declared dimension, or `props.<key>`, to group by
filtersbodyobject[]Equality filters on declared dimensions, or on `props.<key>` event properties

Response

Returns object.

FieldTypeAlwaysDescription
metaobject
seriesobject[]
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

NameInTypeRequiredDescription
person_idbodystringGraph person id, when already known
identifierbodyobjectAny single identifier belonging to the person
frombodystringInclusive first day (UTC); unbounded when omitted
tobodystringInclusive last day (UTC); unbounded when omitted
limitbodyintegerMaximum events to return, newest first

Response

Returns object.

FieldTypeAlwaysDescription
metaobject
eventsobject[]
personobject
truncatedboolean
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

NameInTypeRequiredDescription
qbodystringNatural-language analytics question

Response

Returns object.

FieldTypeAlwaysDescription
sqlstring
rowsobject[]
answerstring
truncatedboolean
structuredobject
explanationstring
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

NameInTypeRequiredDescription
entitybodystringSemantic view name from the catalog
subjectbodystringCohort key: "identity" or `props.<key>`
cohortbodyobjectThe starting event; empty filters mean any row of the entity
returnbodyobjectThe coming-back event; empty filters mean any row of the entity
daybodyintegerN: a subject is retained if it has a return event at least N days after its cohort event
return_window_daysbodyintegerLength of the window that opens on day N; a return event must fall inside it
frombodystringInclusive first day of the cohort range (UTC)
tobodystringInclusive last day of the cohort range (UTC)
grainbodystringone of: day, week, monthCohort bucket size, applied to each subject's FIRST cohort event

Response

Returns object.

FieldTypeAlwaysDescription
metaobject
totalobject
bucketsobject[]
Was this page helpful?
Esc

Start typing to search the docs.

navigateselect