> ## 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.

# Lists vs segments: the two membership models

> The Audience pillar ships two membership constructs — a static list (a stored set of contact ids, membership changes only by add/remove) and a dynamic segment (a stored predicate, membership is recomputed on the event stream). This page sets them side by side: what membership means in each, when each recomputes, which construct a given use case should pick, the footguns of confusing them, and how a segment materialized into a list freezes the audience.

# Lists vs segments: the two membership models

The [Audience hub](/audience/overview) surfaces two ways to define who you can reach, and they look similar on the grid — both are targetable by campaigns, both export to CSV, both show a member count. They differ underneath: a **list** is a stored set of contact ids; a **segment** is a stored predicate. Membership in a list is an explicit row someone added. Membership in a segment is a derived result the platform recomputes as contact data moves. Pick the wrong one and you either re-send to a stale roster or freeze an audience you expected to keep updating.

The two guides cover each in isolation — [Static contact lists](/guides/contact-lists-static-targeting) for the list model, [Build CDP segments](/guides/cdp-segments) for the segment model — and the [segment recompute model](/concepts/cdp-segment-recompute-model) defines when a segment's membership actually moves. This page is the frame between them: the one vocabulary that names the difference, the decision table that picks the right construct per use case, and the footguns that bite when the two are confused.

## 1. The two membership models

Phrase both in one vocabulary and the difference collapses to a single question — is membership **stored** or **derived**?

* **A list is a stored set of contact ids.** Membership is an explicit row: a contact is on the list because someone (or an API call) added them, and they leave only when someone removes them. The list holds the membership; the membership does not change unless the list itself is edited. There is no recompute, no filter, no "current state" to drift — the list **is** the current state.
* **A segment is a stored predicate.** Membership is a derived result: a contact is in the segment because, at the last recompute tick, running the segment's filter against that contact's profile returned `true`. The segment holds the **rule**, not the roster. The roster is materialized by running the rule, and it is re-run on the cadence the [segment recompute model](/concepts/cdp-segment-recompute-model) defines — a near-real-time per-contact pass on the event stream plus a scheduled batch backstop, with enriched profile traits refreshed daily.

The one-line restatement: **a list answers "who did I add?", a segment answers "who matches right now?"** A list never goes stale on its own; a segment is designed to go stale and then refresh.

## 2. What each ships with

The two constructs come with different machinery, because they answer different questions.

### The list model

* **Manual add, manual remove.** Members arrive by search-and-pick in the dashboard, by direct identifier (phone, email, or contact id) over the [Contacts API](/api-reference/endpoints/contacts), by bulk add (`POST /api/v1/contacts/lists/{id}/members/bulk`, up to 1,000 ids per call), or by CSV import that attaches contacts to a list in one pass. Removal is a row-level action; the contact itself is untouched — only the grouping is gone.
* **Stable membership.** Once added, a contact stays until removed. No background process touches the roster. This is the point and the discipline: a list built for a regulatory notice does not silently drop a contact who later unsubscribed — eligibility (consent, opt-outs, quiet hours) is still enforced at send time by the messaging pipeline, but **membership** is frozen until you touch it. Re-curate before reuse.
* **No recompute.** There is nothing to recompute. The member count is a count of rows, not a count of predicate matches. The list's freshness is exactly the freshness of the last add/remove you performed.
* **Ideal for fixed cohorts.** Event invitees, a VIP group a success manager hand-picked, a suppression roster legal approved, a one-off re-engagement wave, a regulatory-notice recipient set that must not drift. See the [static lists guide](/guides/contact-lists-static-targeting) for the full workflow.

### The segment model

* **A predicate over contact data.** The segment's filter references profile fields, computed traits, scores, and behavioral events — "contacts whose `lifecycle_stage` is `active` and `total_orders` ≥ 5", or "contacts in the top churn-risk tier." The filter is the source of truth; the materialized membership is a read over it.
* **Recomputed on a cadence.** Membership is re-evaluated near-real-time per contact (the event-stream pass) and reconciled by a scheduled batch sweep; enriched profile traits the filter references are refreshed on a daily sweep. See the [segment recompute model](/concepts/cdp-segment-recompute-model) for the per-surface staleness table — the dashboard badge converges in \~1s, ad-audience syncs land in hours.
* **Reflects fresh state automatically.** A contact that newly qualifies is entered; one that no longer qualifies is exited. A growing segment keeps working in a recurring campaign without anyone editing the campaign — this is the property a list does not have.
* **Ideal for standing rules.** "Everyone who bought in the last 30 days", "churn risk above 60%", "active customers" — audiences that should keep themselves current. See the [segments guide](/guides/cdp-segments) for building them, and the [scoring pipeline](/concepts/cdp-scoring-pipeline) for the scores a segment filter can read.

## 3. Decision table — which construct for which use case

Walk the table before you build. If the audience is a **decision** a human made (a hand-picked cohort, an approved roster), it is a list. If the audience is a **rule** that should keep itself current, it is a segment.

| Use case | Construct | Why |
| - | - | - |
| One-off send to a hand-picked cohort (event invitees, a win-back wave of 400 lapsed contacts) | **List** | A human decided the roster; it should not change between build and send. A segment would re-evaluate and silently drop or add members mid-flight. |
| Recurring campaign to "everyone who bought in the last 30 days" | **Segment** | The qualifying set moves every day as new purchases land and the 30-day window rolls forward. A segment recomputes; a list would freeze the cohort at the moment you built it and drift out of date. |
| Suppression list (do-not-send roster legal or ops approved) | **List** | Suppression is a decision, not a query — the contact is suppressed because someone put them on the list, and they stay until someone removes them. A segment that re-evaluates could re-admit a contact mid-suppression. |
| Personalization audience ("show loyal-customers variant to active buyers") | **Segment** | Personalization keys off current state — the contact's live lifecycle stage — so the audience should reflect fresh data, not a snapshot taken once. |
| Paid-media sync to Meta / Google / TikTok custom audiences | **Segment** | Ad-network activation syncs a segment's current membership on the destination's run cadence; a list would push a frozen snapshot that stops matching the live population. See [ad-audience incrementality](/concepts/cdp-ad-audience-incrementality) and the [Activation](/audience/overview) tile. |
| A recipient set whose membership must not drift (regulatory notice, legal send) | **List** | The send's recipients are fixed at approval time and must not change between approval and delivery — exactly the list property of stable, frozen membership. |

The rule behind the table: **if the answer to "who is in this audience?" changes between when you build it and when you send, and you want it to — use a segment. If it must not change — use a list.**

## 4. How they interact

The two constructs are not walled off from each other. Three interactions matter, and the third is the one that bites.

1. **A segment can be materialized into a list (snapshotting).** Export a segment's current membership to CSV (`GET /api/v1/contacts/segments/:id/export.csv`), then import that CSV into a list — or, where the segments surface offers it, snapshot the current membership into a static list directly. The result is a **list** that holds the segment's membership **at the moment you materialized it**. The snapshot does not inherit the segment's recompute; it is a list from that point on, with all the list properties — stable, frozen, no recompute.
2. **A list can be one input to a segment predicate.** A segment filter can reference list membership — "in segment `active_buyers` **and not** in list `suppression_roster`" combines a derived audience with a stored one. The segment recomputes; the list's membership is read as-is at recompute time. This is how a standing rule and a hand-curated suppression set compose.
3. **A send can target either directly.** Over the campaigns API, target a list with `audience_type: "list"` and the list id, or target a segment with its id. At send time the difference is when membership is frozen: a **list** resolves to the members it holds when the audience resolves (a fixed roster — contacts added after the send resolves are not swept in); a **segment** can re-evaluate its filter at send time, so a growing segment keeps working without anyone touching the campaign. See [Campaign end-to-end](/guides/campaign-end-to-end) for the send-time resolution.

### The snapshot-then-diverge footgun

The footgun lives in interaction #1. **Materializing a segment to a list freezes membership.** If you built the audience as a segment because it should keep updating, and then you materialize it to a list for a one-time convenience (a CSV for a downstream tool, a static copy for an export), the list does not track the segment — it holds the snapshot. A contact who qualifies tomorrow is not in the list; a contact who exits tomorrow is still in the list.

The fix is a usage rule, not a setting: **if you expect the audience to keep updating, stay on the segment.** Materialize to a list only when the one-time copy is the point — a regulatory notice whose recipients must not drift, a legal send that needs a frozen, auditable roster, a one-off export. For a recurring campaign, target the segment directly; for a send whose recipients must be frozen, materialize once and use the list. The worked example below walks both lifecycles.

## 5. Scale and cost notes

The two constructs scale differently, and the cost model is worth naming before you pick on size alone.

* **A list is O(rows stored).** Member count is a row count; cost is the storage of the membership rows and the add/remove traffic you generate. A list of 1M contacts is 1M rows that do nothing until you touch them. There is no recompute spend, no per-send fan-out beyond resolving the roster.
* **A segment's cost follows the predicate complexity and the recompute cadence.** A filter over a single profile field is cheap; a filter over behavioral event counts, computed traits, and scores costs the evaluation of each at every recompute tick. The real-time per-contact pass is bounded to the one contact an event binds to, but the scheduled batch sweep re-evaluates the tenant's full auto-refresh set — a complex predicate across a large population is the expensive shape. See the [segment recompute model](/concepts/cdp-segment-recompute-model) for the cadence that drives the sweep.
* **Very large segments fan out per-send.** A send that targets a segment re-evaluates the filter at send time, and a journey that triggers on segment entry or exit enrolls every contact that crosses — the [journey enrollment fan-out](/concepts/journey-enrollment-fan-out) page defines the per-journey entry rate limit that bounds that blast radius. A list send resolves a fixed roster and does not re-evaluate; the fan-out difference is the send-time re-evaluation, not the membership size alone.

The short version: a list's cost is storage and edit traffic; a segment's cost is predicate evaluation × recompute frequency × population, plus the send-time fan-out when a send or journey re-evaluates the filter. A very large, frequently-recomputed segment is the shape to budget — a very large, hand-curated list is just a large list.

## 6. Worked example — a "last-90-day buyers" audience

Two sends need the same logical audience — contacts who bought in the last 90 days — but they answer different questions, so they take different constructs.

### Build it as a segment (for a weekly recurring campaign)

The campaign runs every Monday and must reach **whoever matches this week** — new buyers enter as their purchases land, and buyers whose last purchase ages past 90 days exit. Build it once as a segment:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/contacts/segments \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Last-90-day buyers",
    "auto_refresh": true,
    "filter": {
      "all": [
        { "field": "last_order_at", "op": "gte", "value": "now-90d" },
        { "field": "total_orders", "op": "gte", "value": 1 }
      ]
    }
  }'
```

The segment recomputes on the cadence the [recompute model](/concepts/cdp-segment-recompute-model) defines — the event-stream pass moves a contact the moment a new purchase lands, the batch sweep reconciles, and the weekly campaign re-evaluates the filter at each send. **You build it once and it stays fresh for the life of the campaign.** Target the segment directly with the campaign; do not materialize it, or you freeze the very freshness you built the segment for.

### Materialize a one-time list copy (for a legal-notice send)

A legal notice must go to the buyer population **as it stood at approval time**, and the recipient set must not drift between legal sign-off and delivery — a contact who buys tomorrow is not a recipient, and a contact who exits tomorrow still is. Materialize the segment to a list once:

```bash theme={null}
# 1. Export the segment's current membership to CSV.
curl "https://api.orbit.devotel.io/api/v1/contacts/segments/seg_9f2a7c1e4b/export.csv" \
  -H "X-API-Key: dv_live_sk_your_key_here" -o last-90-day-buyers-legal-snapshot.csv

# 2. Import that CSV into a static list (see the import guide for the wizard).
#    The list now holds the snapshot — frozen, auditable, no recompute.
```

The list is a **static snapshot** of the segment at the moment you exported it. It does not track the segment; a contact who qualifies tomorrow is not in the list, and that is the point — the legal send's recipients are fixed at approval and do not move. Target the list with `audience_type: "list"` for the notice send.

### Contrast the two lifecycles

| | Segment (weekly campaign) | List (legal-notice snapshot) |
| - | - | - |
| Source of truth | The filter definition | The frozen rows |
| Recomputes | Yes — event-stream pass + batch sweep, daily trait refresh | No — static from the moment of materialization |
| A new buyer landing tomorrow | Entered automatically | Not in the list |
| A buyer exiting the 90-day window tomorrow | Exited automatically | Still in the list |
| Send-time behavior | Re-evaluates the filter | Resolves the fixed roster |
| Use when | The audience should keep updating | The audience must not change |

The same logical audience, two constructs, two lifecycles — and the footgun is exactly the difference between the two rows: **materialize the segment to a list and the left column becomes the right column.** Stay on the segment for the weekly campaign; materialize once for the legal notice.

## Cross-references

* [Segment recompute model](/concepts/cdp-segment-recompute-model) — when a segment's membership actually moves, and the per-surface staleness table.
* [CDP streaming destinations](/concepts/cdp-streaming-destinations) — streaming the membership-crossing events that drive a segment to a Kafka topic or Kinesis stream.
* [Journey enrollment fan-out](/concepts/journey-enrollment-fan-out) — the entry rate limit that bounds the blast radius when a journey triggers on segment entry or exit.
* [Contact scoring pipeline](/concepts/cdp-scoring-pipeline) — the daily scores a segment filter can read, and the on-demand pass for freshly imported contacts.
* [Ad-audience incrementality](/concepts/cdp-ad-audience-incrementality) — the holdout and lift measurement for a segment activated to a paid-media audience.
* [Build CDP segments](/guides/cdp-segments) — the guide for building the segment whose lifecycle this page contrasts.
* [Static contact lists](/guides/contact-lists-static-targeting) — the guide for the list model this page contrasts.
* [Audience hub](/audience/overview) — the tile map where Lists and the segment surfaces sit side by side on the grid.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.