A valid request URL is required to generate request examples{
"cohorts": [
{
"cohort_week": "2026-07-20",
"contacts": 2,
"weeks": [
{
"week_offset": 0,
"retained": 2,
"retention": 1
},
{
"week_offset": 1,
"retained": 1,
"retention": 0.5
},
{
"week_offset": 2,
"retained": 0,
"retention": 0
}
]
}
],
"status": "ready",
"range": {
"from": "2026-06-01T00:00:00Z",
"to": "2026-08-03T00:00:00Z",
"retention_days": 365,
"clamped": false
},
"from": "2026-06-01T00:00:00Z",
"to": "2026-08-03T00:00:00Z"
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}Weekly retention cohorts
Requires analytics:read. The retention matrix a nightly rollup recomputes: for each cohort week, how many contacts it holds and how many of them came back in each week after it.
A cohort is a UTC week, Monday-start. A contact belongs to the week of its first $auth.identified event, falling back to the week it was created when there is no such event — a contact born over POST /v1/contacts or a CSV import fires no auth fact. Anonymous visitors have no cohort at all, the same exclusion that keeps them off the active-contact meter.
Retained in week N means the contact produced at least one authenticated event — a sign-in, or anything it did while signed in — inside cohort_week + N weeks. Week 0 is the cohort week itself, so retention there is 1 by construction. Offsets are dense up to the current week: a cohort nobody returned to charts as zero rather than as a gap.
Eight cohort weeks are recomputed on every run, and rows outside that window freeze where the last run left them. That freeze is the point of a rollup — the raw event partitions can be dropped without the chart moving.
status says what an EMPTY cohorts means, because the two causes call for opposite actions: pending is contacts this environment could have cohorted and no rows yet (the rollup has not run, or has not caught up), empty is no cohort the window holds having anybody in it. It is ready whenever the matrix has anything in it.
A valid request URL is required to generate request examples{
"cohorts": [
{
"cohort_week": "2026-07-20",
"contacts": 2,
"weeks": [
{
"week_offset": 0,
"retained": 2,
"retention": 1
},
{
"week_offset": 1,
"retained": 1,
"retention": 0.5
},
{
"week_offset": 2,
"retained": 0,
"retention": 0
}
]
}
],
"status": "ready",
"range": {
"from": "2026-06-01T00:00:00Z",
"to": "2026-08-03T00:00:00Z",
"retention_days": 365,
"clamped": false
},
"from": "2026-06-01T00:00:00Z",
"to": "2026-08-03T00:00:00Z"
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}Autorizações
A staff session token, uk_st_…. Minted by sign-up, sign-in or the two-factor exchange. Only a 401 means it is spent; not_a_member (403) is about the organization named in X-Organization-Id and leaves the token good for the others.
Cabeçalhos
The organization the caller is acting on — the org_… code that appears in the panel URL. It identifies; the membership JOIN is what authorizes, so a forged code reads nothing: the answer is not_a_member (403), which does not mean the session is over. Absent, the session's default organization answers, or — if that membership was revoked while the session was open — any other one the caller still holds.
Parâmetros de consulta
Which environment to act in. A view parameter, valid only on the staff surface — a machine credential never chooses its environment, it is resolved from the key.
live, test Oldest cohort week to include, compared against the week's Monday. Default: 9 weeks ago, which is what covers the 8 cohort weeks the rollup keeps.
Newest cohort week to include. Default: now.
Resposta
The matrix, oldest cohort first, each cohort's weeks in offset order.
Show child attributes
Show child attributes
What an empty cohorts means. ready whenever it is not empty.
ready, pending, empty The window the read actually answered over, and what the plan did to it.
Every analytics read carries one. analytics_retention is a plan LIMIT and never a gate: asking for a year on a plan that keeps a month answers 200 over the month, with clamped true and from moved forward to the horizon — there is no 402 anywhere in this module.
Unlike the staff audit window, which only filters a read, this horizon is also enforced in storage: a nightly sweep deletes analytics past each organization's retention. So data beyond from is not merely hidden, and an upgrade recovers the horizon rather than the history.
Show child attributes
Show child attributes