Cohort retention
Cohort retention answers one question: of the people who first did something in a given period, what share keeps doing it in every period after? Open Insights → Cohort Retention in the Orbit console, pick a cohort event and a return event, and the page draws three views over your collected CDP events — a pooled retention curve, a per-cohort heatmap, and headline KPIs — computed to the day or the week. This guide covers what the surface measures, how Orbit computes cohorts, how to pick the two events, how to read the curve and the heatmap, and how to get the numbers in front of stakeholders every week.What a cohort retention curve tells you
A retention read splits one blended “are we retaining?” number into per-cohort detail. A rising average can hide a broken flow: if this week’s new signups retain much worse than last week’s, but older cohorts still pad the average, the blended number says nothing is wrong. Think of weekly churn spikes after onboarding-flow changes, or deliberate at-risk messaging cohorts — cohorts catch the break; the blended number does not. Three choices shape any retention read:- Daily vs weekly cohorts. A daily bucket catches same-day onboarding drop-off and day-one/day-two decay, but day-of-week effects (weekend vs weekday volume) make individual daily cohorts noisy. A weekly bucket smooths that noise and is the default for onboarding and engagement reads. Run both against the same events when you need to know whether a drop is a day-one problem or a week-two problem.
- Bounded vs unbounded windows. The curve is bounded: it ends at period N (4–16 period buckets in the console) because the curve past a low-activity point rarely changes the decision. Keep full history from the first cohort forward rather than a trailing 30-day default, so the oldest cohorts stay observable across all N periods.
- The heatmap is the per-cohort ground truth. The curve averages; the heatmap disagrees row by row. When the curve moves, the heatmap tells you which cohorts moved it.
How Orbit computes cohorts from your events
Report requests go toPOST /api/v1/cdp/analytics/cohort-retention, and the computation runs over the CDP event stream your SDKs and integrations already collect.
MIN(received_at) per subject), buckets that instant into day or week periods, then counts what fraction of each cohort fires the return event in each subsequent period bucket. A returning subject counts once per period — later repeats in the same bucket do not re-count them.
A subject is whatever identity your events carry — contacts resolved to a contact ID, otherwise an anonymous ID or a user ID, in that precedence order. Subjects whose events carry no identity at all never join the cohort. See identity resolution for how identities merge across events.
Period 0 is the entry bucket and is always 100% — the cohort’s own fire of the cohort event lands it there. Every period after that measures the share that returned.
Two guards keep the surface honest:
- Window defaults are period-aware. With
periods: 12on weekly buckets, the default entry window covers at least 12 weeks (and never less than 30 days) so the curve is actually observable out to its final period. Explicitsince/untilin the request body override the default and bound when subjects may enter cohorts. - Right-censoring is explicit, not hidden. A cohort counts toward the pooled curve only once enough calendar time has elapsed for that period, so the youngest cohorts cannot drag the tail into a false-zero dip. Where a cohort is too recent, the curve simply stops; the censor note under the builder says exactly this.
Choose the cohort event and the return predicate
The whole reading depends on this pair:- Cohort event is the first-fire marker that puts a subject in a cohort. Good cohort events are non-repeating life-cycle milestones:
Signed Up,Activated,Plan Subscribed. A repeating event as the cohort marker puts a subject into whichever period they happened to fire it in, which dissolves the cohort definition — pick the milestone, and the period placement stays meaningful. - Return event is the predicate whose presence in a later bucket counts as “returned.” Match the predicate to the question: habit (
Logged In,Session Started), value (Order Completed,Report Exported), raw active retention (the same event as the cohort marker), or money (Invoice Paid). The return event decides what “retention” means, so the event name is the whole definition — set your tracking plan to emit it once per meaningful visit, not per click.
Logged In to Page Viewed”), and cohorts with success predicates that only the top decile ever fires (those cohorts flatline near zero).
Steps to land the pair cleanly:
- Open Insights → Cohort Retention in the console (or call the endpoint directly).
- Type the cohort event name exactly as it appears in your tracking plan — the event registry on CDP → Tracking plan lists every collected event and its shape.
- Type the return event the same way. A typo-ed event name does not error — it returns zero-subject cohorts, so check the Subjects KPI against your expected traffic before trusting the curve.
- Keep the pair fixed per report. When someone proposes a better predicate, either add it as a second view or re-run the original on the same window so the numbers stay comparable.
Read the heatmap and the area curve
The page draws the same data three ways. Heatmap. One row per cohort bucket, one column per period offset, cells shaded single-teal on a white surface — the deeper the teal, the higher the rate (white ≙ low, deep teal ≙ high), with rate percentages inside. Hover any cell to readretained / cohort_size. Read it two ways:
- Down a column (e.g. W1 across rows) answers is retention improving across cohorts? This is the diagnostic question.
- Across a row (a single cohort W0 → W12) answers how does one cohort decay? This is the shape question.
Worked example — email vs SMS onboarding over 12 weekly periods
Compare two onboarding cohorts: email driven (email_onboarded) vs SMS driven (sms_onboarded), return predicate activated (the subject completed any onboarding-critical step).
Run A — SMS onboarding. Set cohort sms_onboarded, return activated, weekly buckets, 12 periods, since = 12 weeks ago. Example output (one row from the heatmap, pooled W1 highlighted):
W1 stabilizes around 30%, W4 around 17% — the SMS flow carries ~17% into week four. The pooled curve flattens after W2, so whatever decay remains is front-loaded into the first two periods.
Run B — email onboarding. Same return predicate, same window, cohort
email_onboarded:
The email flow sits 5–7 points below SMS at W1 and carries that gap through W12. Read together, the two rows say SMS onboarding produces structurally better retention for the same return predicate, at this volume, over this window — a direct channel-allocation signal.
A churn regression looks different. Suppose a product change to the activation flow ships July 6. Down the W1 column, June cohorts hold at ~30% week over week, then the July 6 row prints 26%–27% and stays there for every newer cohort. The pooled curve shifts down after the release date; the W0→W6 tail flattens rather than dipping, which says the break is at entry, not in later decay. The merged curve would look like a 4-point dip; the per-cohort view says which cohorts brought the dip and whether older cohorts stayed healthy — which is the difference between “we have a churn wave” and “we broke the new flow in the second week of July.”
Ship the report on a schedule
The cohort-retention endpoint is the scheduled surface; the console plays the ad-hoc explorer. Schedule the API call from wherever you run scheduled jobs (an internal script, a CI cron, or a lightweight automation runner) — the same body on every run — and deliver it to stakeholders:- Keep the pair stable. Change the return predicate and the digest number shifts with no real movement underneath — version the event picks alongside the report.
- Fix the window. A rolling window means the oldest cohort falls out of scope week over week and the “latest” period is never what it claimed to be. Pass explicit
since/until(or pin them in the report script) so the digest compares like with like. - Owner/admin/developer keys with
contacts:readcan run the endpoint. The scheduled report itself is a plain HTTP call — no special role needed.
Troubleshooting
See also
- Insight dashboards reading — where this page sits among the Insights surfaces
- CDP tracking plan — keep event names honest before you pick the cohort pair
- CDP identity resolution — what counts as a subject in a cohort
- Scheduled reports guide — the generic cadence/send-now/reference surface for any scheduled report