> ## Documentation Index
> Fetch the complete documentation index at: https://docs.userkit.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Retention cohorts

> Weekly cohorts and the share of each one that came back — with the definitions that decide what a cohort is, what retained means, and what an empty matrix is telling you.

Retention is a ratio between two definitions. Group people by *when* and count
how many of them came back *when else* — change either half and the same
product retains 40% or 80%. This page is the halves.

The panel draws it under **Analytics → Retention**, behind `analytics:read`.

## What a cohort is

**A UTC week, Monday-start.** A contact belongs to the week of its first
`$auth.identified` event — the fact UserKit publishes the moment somebody stops
being anonymous. When there is no such event, the week the contact was created
is used instead: a contact born over `POST /v1/contacts` or a
[CSV import](/en/guides/import-contacts) fires no auth fact, and dropping those
would silently exclude every tenant who migrated their users in.

**Anonymous visitors have no cohort at all.** A visitor gets a contact row and a
session, so its events carry a `contact_id` — which is exactly why "has a
contact" cannot be what a cohort counts. Identified only, the same exclusion
that keeps visitors off your
[active-contact meter](/en/concepts/contacts).

## What retained means

The contact produced **at least one authenticated event** in that week: a
sign-in, or anything it did while signed in. Not a page view from a logged-out
browser, and not an email open.

Week 0 is the cohort week itself, so its retention is 1 by construction — a
cohort is 100% of itself. Offsets run densely up to the current week, so a week
nobody came back in is a zero on the chart rather than a hole in it.

## Reading it

```bash theme={null}
curl "https://api.userkit.dev/v1/organization/analytics/retention?environment=live" \
  -H "Authorization: Bearer uk_st_…" \
  -H "X-Organization-Id: …"
```

```json theme={null}
{
  "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",
  "from": "2026-06-01T00:00:00Z",
  "to": "2026-08-03T00:00:00Z"
}
```

`contacts` is the cohort's size and the denominator of every `retention` below
it. `retention` is a fraction between 0 and 1, not a percentage — a percentage
is one multiplication away and a ratio does not lose precision on the way to a
chart.

A cohort three weeks old has three entries in `weeks`. The fourth is not a
zero; it has not happened yet, and drawing one would report a churn that no
week has had the chance to contradict.

## Eight weeks, recomputed

A nightly job recomputes **the last eight cohort weeks** whole, from your raw
events, and upserts the result. Running it twice lands the same numbers.

Rows older than that window **freeze** where the last run left them. That freeze
is the point: your raw event history can age out without the chart moving
underneath it. It also means a correction reaches a cohort only while it is
still inside the window.

The default range covers those eight weeks. `from` and `to` narrow it, and they
are compared against the cohort's **Monday** — a cohort is charted whole or not
at all, because half a curve read as the whole one is not a rougher retention
number, it is a wrong one. Your plan's analytics retention window applies here
the same way it applies to the [event counts](/en/guides/analytics#the-explorer).

## What an empty answer means

`status` is the field that keeps an empty `cohorts` from lying, and it exists
because the two ways to get one call for opposite actions:

| `status`  | What it means                                                              | What to do                                            |
| --------- | -------------------------------------------------------------------------- | ----------------------------------------------------- |
| `ready`   | The matrix is what the last run wrote                                      | Read it                                               |
| `pending` | This environment has contacts that could be cohorted, and no rows for them | Wait for the nightly job, or check that it is running |
| `empty`   | No cohort the window holds has anybody in it                               | Nothing is broken. There is nothing to draw yet       |

It is `ready` whenever `cohorts` is non-empty. The distinction between the other
two is asked of your contacts rather than guessed at from the emptiness:
reporting "no data" to somebody whose job never ran is exactly the reassurance
that stops them looking.

## Whose midnight

Weeks are UTC at storage and your organization's timezone at display, the same
rule the [event counts](/en/guides/analytics#whose-midnight) and the
[revenue metrics](/en/guides/revenue-metrics#whose-midnight) follow. Deciding it
once is what lets a retention curve and an event chart be laid over each other.
