Skip to main content

Propensity segments

Propensity segments are saved segments whose membership comes from a predictive model’s ranking rather than from filter rules. Instead of “contacts where lifecycle_stage equals customer,” the segment is “the top of the rankings the churn model or value model computed.” You score a population with a trained model, the scores rank, and the top of that ranking becomes the saved audience. The same surface backs the dashboard pages under Audience → Propensity segments and Audience → Churn risk. The API and the UI drive the same workflows.

1. How propensity segments differ from static segments

A static segment (a rule-based segment or a once-materialized list) says “membership was decided at the moment we saved.” A propensity segment says “membership was decided by a model’s ranking.” The difference matters for both what the segment contains and how it stays current:
  • Origin of membership. A rule-based segment’s members match filter predicates. A propensity segment’s members are the contacts the trained model ranked highest — the top-N of the scoring population.
  • What the segment answers. A rule-based segment answers “who matches this condition?” A propensity segment answers “who is most likely to churn?” or “who is predicted to be worth the most?” — questions no field predicate alone can answer.
  • Staleness profile. A rule-based segment with auto_refresh = true re-evaluates its filter on a schedule; its membership drifts only when the underlying attributes change. A propensity segment’s snapshot drifts when the model’s scores drift — re-training or re-scoring the model can reorder the ranking even if no contact attribute changed.
The saved segment itself is always a static snapshot (auto_refresh = false). Renewal is driven from the model side — the activation schedule or a manual re-activate re-materializes the current ranking, which is the propensity surface’s equivalent of “recompute this segment.”

2. Prerequisites

Before you create a propensity segment, the model that produces the score must be trained and its scoring population must be non-empty:
  • A trained predictive model. Train churn_propensity or lifetime_value first via POST /api/v1/cdp/predictive-models/:model/train. An untrained model replies 422 with status: "insufficient_data" on both /score and /activate, and the dashboard’s propensity surfaces show the same “not enough history” inline alert — the predictive models guide covers what counts as enough history for each model.
  • A non-empty scoring population. Each model scores a fixed server-side predicate — still-active contacts for churn, contacts without a recorded value for CLV. If the predicate matches no one, the score and activate calls return materialized: false without persisting anything.
The bound input the model relies on is the trained fit itself (the learned coefficients), not a contact field you bind at request time. The features it reads — tenure, recency, message and event counts — are pulled from each contact’s profile at score time, so a contact with no profile activity is simply low-ranking rather than excluded.

3. Create a propensity segment from a model output

Activation is one call. Pass the server-owned model key; the server re-scores the population and materializes the top-N as a new static segment:
The body is minimal because the ranking logic lives server-side:
  • limit (optional, 1–500) — how many contacts from the top of the ranking to save. Omit it to save the whole ranked population.
  • name (optional) — the saved segment’s name. A readable name matters here because the segment will surface in the segments list and the campaign / journey audience pickers under this exact label.
The response returns materialized: true, the created segment, member_count, and activation_ready: true. The segment is immediately visible in GET /api/v1/contacts/segments and targetable by campaigns. Use the lifetime_value key the same way to save a high-value cohort:
The dashboard’s Audience → Propensity segments page calls this same endpoint when you click “Save top N as a segment” — the UI and the API materialize identically.

4. Read membership and the score histogram

Before you save the top-N, preview the ranking. A bare score call (no contact_id) returns the top-N plus population_scored, so you can size the audience before materializing it:
The response ranks the population descending and echoes count (rows returned) against population_scored (total ranked) — that second number is the denominator the top-N limit is a slice of. Each returned row carries the model’s score (score for a classifier, predicted_value_cents for the value model) per contact, which you read as the ranking histogram: the distribution of scores across the returned top-N tells you whether the top tier is meaningfully separated from the tail or whether the ranking is compressed. Once a segment is materialized, read its membership the same way as any other segment — GET /api/v1/contacts/segments/:id/members returns the saved contact list, and the segment carries the prediction that produced it as a static label rather than a live score.

5. Use the segment in a campaign or activation

The saved propensity segment is a normal segment in the segments list — it appears in the campaign and journey audience pickers, can be exported through audience export, and can be synced through reverse ETL to a warehouse. Target it exactly as you would a rule-based segment:
Scoring decided who is in the segment; targeting it decides nothing about how a message is sent. The same propensity scores also feed the per-contact scores endpoint (GET /api/v1/contacts/:id/scores) that gate automations read when they condition on a contact’s risk or value band.

6. Refresh cadence

A materialized propensity segment is a frozen snapshot until someone re-materializes it. Renewal happens on the model side, not the segment side:
  • Scheduled re-activation. The per-model activation schedule (PUT /api/v1/me/predictive-activation-schedule) re-scores the model on an hourly / daily / weekly cadence and re-saves the top-N to a stable segment name. This is the propensity-surface equivalent of auto_refresh on a rule-based segment. The predictive models guide covers the schedule contract.
  • Manual re-activation. POST /activate again with a new name. The new snapshot sits alongside the old one in the segments list — you see “At-risk — top 100” and “At-risk — top 100 (2026-08-31)” as separate named segments, and you can diff their membership.
  • The underlying scores also refresh on their own cadence. The nightly scorer rewrites the persisted per-contact score fields (propensity_score, ltv_estimate_cents) every tick. A segment materialized today reflects tonight’s ranking only when you re-activate; the scores the nightly job writes do not retroactively edit the frozen segment’s membership.
Check the model’s drift monitor (GET /api/v1/cdp/predictive-models/:model/drift) before trusting a segment materialized against an old fit — when the drift report shows the input distribution has moved, refresh the fit before you refresh the segment.

7. Failure modes

  • Model not trained. A 422 with status: "insufficient_data" on score or activate means the fit could not be computed — fewer than 50 labelled rows, or a classifier with only one outcome class. Train the model once history lands, then retry.
  • Empty scoring population. An activate call that returns materialized: false (HTTP 200) means the model’s scoring predicate matched no one. The segment was not created; nothing to delete. Check that the predicate’s base (still-active contacts, or contacts with no recorded value) actually has members — a fresh workspace often has none.
  • Empty histogram / low population_scored. A score call that returns count: 0 or a small population_scored against your expected base is the same predicate-empty condition at the preview stage. The top-N cannot exceed the ranked population, so a top-100 on a population-scored 12 saves 12 contacts.
  • Stale fit behind the scores. Long gaps between train and activate mean the ranking the segment was built on is an old fit. The drift endpoint surfaces this; re-train, then re-activate.
  • Segment exists but is empty in a picker. A segment saved earlier can look fine by name and still send to no one if the snapshot is stale relative to where the scores have drifted — re-activate with a current fit rather than editing the segment by hand.

Dashboard twin

The same endpoints back two dashboard pages under Audience:
  • Propensity segments — the ranking preview, the top-N bound, the tier badges (high / growth / emerging on value, high / medium / low on churn), and the one-click activation button.
  • Churn risk — a dedicated home for the churn model’s at-risk ranking with the same save-as-segment flow.
Nothing the UI does is hidden from the API; the pages are a rendering of the same contracts.

See also