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

# Build conversion funnels and retention grids over CDP events

> Operator guide to the Funnels & Retention surface: define an ordered funnel from collected CDP events, read step-to-step drop-off, and run cohort-retention grids — plus API access and the role gate.

# Funnels & Retention

The **Insights → Funnels & Retention** page answers one question: **how many subjects move through a sequence of product events you define, and where do they stop?** Open **Insights → Funnels & Retention** in the Orbit console, type an ordered list of event names as funnel steps, pick a conversion window, and run — the page counts how many subjects in your collected CDP event stream reached each step in order. The same page also runs cohort-retention grids over any cohort event / return event pair.

This guide covers what you can build without code, how to read a funnel and a retention grid, who can open the surface, a worked example, and the API endpoints behind the page.

## What you can build without code

Two reports, both computed on demand over the CDP event stream your SDKs and integrations already collect:

* **An ordered conversion funnel.** Name 2–8 events as steps in the order they should happen — `Signed Up` → `Activated` → `Purchased`. Orbit counts how many subjects reached step 1, then step 2, then step 3, and reports each step-to-step conversion rate and drop-off count. A **conversion window** (1–90 days) bounds how long a subject may take between entering the funnel and completing the remaining steps.
* **An N-day / N-week cohort retention grid.** Pick a cohort event and a return event; Orbit groups subjects into daily or weekly cohorts by their **first** fire of the cohort event, then measures what share returns to fire the return event in each later period — out to 4–16 periods on the page.

Events must already be collected in your CDP. If a step name does not match anything your tracking plan emits, the step reaches nobody — check the event registry under **CDP → Tracking plan** before assuming a flow is broken.

## Build your first funnel

1. Open **Insights → Funnels & Retention**.
2. Under **Conversion funnel**, fill the step rows top to bottom in the order subjects should take them. Add or remove rows with **Add step** / the remove button — the funnel needs at least two steps; the page accepts up to eight.
3. Pick a **Conversion window** — the maximum time between a subject's step 1 fire and their final step fire. A 7-day window catches same-week onboarding; a 30-day window suits longer activation cycles.
4. Run the funnel. The result lists **Entered** (subjects who fired step 1 in the window), **Converted** (subjects who reached the final step), and the overall conversion rate alongside per-step rows.
5. Read the step rows: each step shows its reached count with a progress bar, plus the **step conversion** (from the immediately previous step) and the **dropped off** count. The largest drop-off between two adjacent steps is where the flow loses people.

Pick the step-to-step rate, not the overall rate, to locate the break: an overall rate of 18% can hide a healthy first step and a broken second step, or the reverse.

### Three surfaces, three different questions

The funnel builder is easy to confuse with two neighbours. They answer different questions:

* **Funnels & Retention** (this page) — the funnel **you defined**. You name the events and the order; the page measures conversion through your definition. Use it to test a hypothesis: "subjects who activate in week one should reach `Purchased`."
* **Journey Paths** (`/insights/journey-paths`) — the paths customers **actually walked**, reconstructed from their traffic. No step definition; the surface discovers the common sequences and where they stop. Use it when you do not yet know what the flow should be. See [Read Journey Paths](/guides/journey-paths-reading).
* **Message-delivery funnels** in **Insights → Analytics** — the carrier pipeline for a single send: sent → delivered, with carrier drop reasons. That is a delivery drill-down, not a user-journey conversion measure.

## Cohort retention on the same page

The second builder on the page runs the same cohort-retention engine documented in [Cohort retention](/guides/insights-cohort-retention) — use that guide for the full reading (right-censoring, pooled curve, per-cohort heatmap). The short version here:

1. Enter a **Cohort event** — the first-fire milestone that places a subject in a cohort (`Signed Up`).
2. Enter a **Return event** — the predicate that counts as "came back" (`Order Completed`, `Logged In`, or the same event as the cohort event for raw active retention).
3. Pick the **Bucket** (Weekly or Daily) and the number of **Periods** (4–16).
4. Run. Each row of the grid is one entry cohort (the day or week subjects first fired the cohort event) with its size, and each column is a period offset (W0, W1, …). W0 is always 100% — the cohort's own entry fire. Deeper teal means higher retention; hover any cell for the `retained / cohort size` fraction.

Read a column (W1 across all cohorts) to ask *is retention improving across weeks?* and a row (one cohort W0 → W12) to ask *how does a single cohort decay?* The same vocabulary as the cohort guide applies: the **cohort event** is the entry predicate, the **return event** is the return predicate, and both must be non-repeating meaningful fires in your tracking plan — see [Choose the cohort event and the return predicate](/guides/insights-cohort-retention) in the cohort guide.

Keeping a stable event pair across runs matters here too: change the return predicate and comparing periods becomes meaningless.

## Who can open the page

The funnel and cohort computations read the PII-bearing CDP event stream, so the surface is gated the same way as other CDP read surfaces: **owner, admin, and developer** console roles with the **`contacts:read`** scope. Operators without that role see an access-denied page rather than the builders. Funnel results and retention grids are aggregate counts (reached, retained, rates) — they do not expose individual subjects, but the underlying stream does carry identities, which is why the scope is required.

## Worked example — signed up → activated → purchased

A commerce tenant collects `Signed Up`, `Activated` (completed onboarding), and `Purchased` in their CDP. Build steps in that order with a 30-day window and run.

**Result header:** Entered 5,000 · Converted 900 · Overall 18%.

| Step | Event     | Reached | Step conversion | Dropped off |
| ---- | --------- | ------: | --------------: | ----------: |
| 1    | Signed Up |   5,000 |               — |           — |
| 2    | Activated |   2,100 |           42.0% |       2,900 |
| 3    | Purchased |     900 |           42.9% |       1,200 |

The read: the funnel loses 2,900 subjects between sign-up and activation but only 1,200 between activation and purchase — the onboarding step converts less (42.0%) than the closing step (42.9%), so the first fix to test is onboarding, not checkout. At **Entered 5,000 → Activated 2,100 → Purchased 900**, the overall 18.0% decomposes into a 2,900-subject gap at activation and a 1,200-subject gap at purchase; the window note under the result (`30d`, with the exact since/until dates) tells you which calendar range those counts cover.

Flip to the retention grid on the same page with cohort event `Signed Up`, return event `Purchased`, weekly buckets, 8 periods: each row is the set of subjects who first signed up that week, and W1 / W4 / W8 answer "of this cohort, what share purchased at all in weeks 1, 4, 8 after entry" — the funnel's 18% overall rate viewed as a recurrence question over time.

## API access

Everything on the page is a compute-on-demand POST against your API key — there is no GET for a saved report, so the console is the explorer and the API is the scheduled surface. Both endpoints live under **`/api/v1/cdp/analytics`** and require `owner`, `admin`, or `developer` keys with the `contacts:read` scope.

**Run a funnel:**

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/cdp/analytics/funnel \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "steps": ["Signed Up", "Activated", "Purchased"],
    "window_days": 30
  }'
```

Request fields: `steps` (2–8 event names, 1–200 characters each), `window_days` (1–365, default 30), and optional `since` / `until` ISO datetimes to bound which subjects may enter. The response carries `entered`, `converted`, `overall_conversion_rate`, and a `steps` array with per-step `reached`, `dropped_off`, `step_conversion_rate` (previous step), and `overall_conversion_rate` (from entry) — plus the resolved `window_days` / `since` / `until`.

**Run a retention grid:**

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/cdp/analytics/cohort-retention \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "cohort_event": "Signed Up", "return_event": "Purchased", "period": "week", "periods": 12 }'
```

Request fields: `cohort_event` and `return_event` (1–200 characters each), `period` (`day` or `week`, default `week`), `periods` (1–90, default 8), and optional `since` / `until` bounding cohort entry. Each cohort row carries `cohort_period`, `cohort_size`, and `retained` / `retention_rate` vectors of length `periods + 1` where index 0 is the entry bucket.

A weekly job that re-runs the same body against both endpoints and posts the diff to stakeholders turns this from an ad-hoc console visit into a monitored funnel — the same pattern as [Cohort retention → Ship the report on a schedule](/guides/insights-cohort-retention).

## Troubleshooting

| Symptom                                          | Why / fix                                                                                                                                                    |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Funnel enters 0 subjects                         | Step 1's event name does not match anything collected — a mistyped name returns an empty funnel, not an error. Check the name against the CDP tracking plan. |
| A mid-funnel step reaches 0                      | Same cause, one step down: later steps inherit the entry window but an unknown event name reaches nobody.                                                    |
| 403 when running either report                   | The key needs an owner/admin/developer role with `contacts:read`.                                                                                            |
| 422 response body names `steps` or `window_days` | `steps` is 2–8 entries of 1–200 characters; `window_days` is 1–365. The response body names the failing field.                                               |
| Retention grid is empty                          | No subjects first-fired the cohort event in the bound window — widen `since`/`until` or verify the cohort event name.                                        |
| Numbers differ from yesterday's run              | Both reports are computed at request time with no caching; new events landing changes yesterday's window. Pin `since` / `until` for comparable runs.         |

## See also

* [Cohort retention: read the curve and the heatmap](/guides/insights-cohort-retention) — the full retention-reading guide; this page runs the same engine
* [Read Journey Paths](/guides/journey-paths-reading) — the observed-paths surface this funnel builder contrasts with
* [Insight dashboards reading](/guides/insights-dashboards) — where Funnels & Retention sits among Insights surfaces
* [CDP tracking plan](/guides/cdp-tracking-plan) — keep event names honest before building steps from them
* [CDP identity resolution](/concepts/cdp-identity-resolution) — what counts as a subject entering a funnel
