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

# How CDP segments recompute: real-time evaluation, re-enrichment, and staleness

> The segment lifecycle after an event lands: real-time membership re-evaluation on the event stream, scheduled re-enrichment of profile traits, streaming delivery to destinations, and the staleness guarantees each downstream surface actually holds.

# How CDP segments recompute

The [CDP event model](/concepts/cdp-event-model) defines the event stream
and the [CDP segments guide](/guides/cdp-segments) shows how to build an
audience on it. This page is the frame between them: **after an event
lands, when does a segment actually move, and when does that move reach
the surfaces downstream?** The answer differs per surface — a dashboard
membership badge, a journey trigger, an ad-audience sync, and a streaming
destination each converge on their own cadence — so this page defines the
lifecycle and the staleness guarantee for each. Read it once and every
"is my segment current?" question reduces to a lookup in the table at the
end.

## Section 1 — Event-time vs request-time attribution

A segment decision is never made on the request that ingested the event.
When `POST /api/v1/cdp/track` accepts an event, the request does exactly
three things: validate the payload against your tracking plan, append the
row to the tenant's `cdp_events` table, and publish the event onto a
per-tenant pub/sub channel. The 200 tells you the ingest truth is durable
— nothing more. Privacy governance (suppression, erasure cascades) runs on
the same pipeline, covered in
[Consent and suppression model](/concepts/consent-and-suppression-model).

Everything downstream — identity binding, segment membership, computed
traits, journey enrollment, destination delivery — runs **after** the
request, on workers that subscribe to that channel or sweep on their own
cadence. Two consequences operators should internalize:

* **Batches, not the request, move segments.** An event backdated by the
  client still triggers a re-evaluate pass as soon as it lands; the
  decision rides on the server-stamped arrival time, never on the
  forgeable client `timestamp`.
* **Failures defer, they never block ingest.** A degraded worker skips
  an event and catches up on the batch tick; the ingest path itself keeps
  a clean 200 either way. Real-time is the fast path, never the only path.

This is the same async posture the
[Async processing model](/concepts/async-processing-model) page defines
platform-wide — the CDP pipeline is one instance of it.

## Section 2 — The real-time re-evaluation pass

Within roughly a second of an accepted `track` or `identify` event, Orbit
re-evaluates **the one contact the event binds to** against every
auto-refresh segment in the tenant — and touches nothing else:

1. The pub/sub fan-out delivers the event to a subscriber that first
   filters it: events with no resolved contact id skip (an anonymous,
   pre-identify event cannot move membership; the first event after
   identify catches up), and `page` / `screen` / `group` / `alias` events
   skip (they do not change the segmentable contact row in a way the
   membership engine keys on — the batch tick converges them).
2. The contact is re-evaluated against the tenant's auto-refresh segment
   definitions — the identical filter compiler the scheduled batch refresh
   uses, so real-time and batch never disagree on whether a contact
   matches.
3. The fresh match set is diffed against the contact's current membership
   rows, and only the **crossings** are applied: a segment the contact
   newly matches is entered, a segment it no longer matches is exited.

Each confirmed crossing then fans out through the same downstream
surfaces a scheduled batch crossing fires:

| Crossing output                                      | Downstream surface                                               |
| ---------------------------------------------------- | ---------------------------------------------------------------- |
| Membership row written                               | Dashboard membership badge + live segment count                  |
| `contact.entered_segment` / `contact.exited_segment` | Outbound webhooks — CRM mirrors                                  |
| `segment.entered` / `segment.exited`                 | First-class CDP events — destinations + journey `event` triggers |
| Journey dispatch                                     | Enrollment into journeys triggered by segment entry or exit      |
| Audience size snapshot                               | The segment's audience-over-time history                         |

A guard prevents recursion: the `segment.entered` / `segment.exited`
events this pass itself emits are skipped at the filter, so a crossing
never re-triggers the pass that produced it.

## Section 3 — Scheduled re-enrichment of profile traits

Segments filter on two ingredient classes, and they drift on different
cadences:

* **Behavioral facts** (performed events, event counts) — re-evaluated
  per contact, near-instantly, by the pass above.
* **Profile traits** (the enriched attributes an operator's segment
  filter references — industry, country, lifecycle stage) — refreshed on
  a **daily** sweep.

The re-enrichment sweep re-derives the canonical first-party enrichment
patch (a work email's domain, a normalized full name, a company field)
for contacts whose enrichment provenance is overdue, rewrites those
attributes with a fresh provenance timestamp, and back-fills attributes
that became derivable only after the first enrichment ran. When a
refreshed trait changes whether a contact matches a segment filter, the
correction lands on the next evaluation pass — real-time on the
contact's next event, batch otherwise. Third-party append vendors never
run on this sweep; a third-party refresh is per-tenant and rides its own
credential-backed path when the operator enables one, so disabling a
vendor never breaks the cadence for the rest of the tenant.

## Section 4 — Streaming delivery: always-on destinations and the deliberate config-skip

**Streaming delivery to external buses does not depend on a segment at
all.** A tenant's configured Kafka topic or Kinesis stream receives every
tracked event — the full CDP event stream, segment membership changes
included — within tens of milliseconds of ingest, by subscribing to the
same pub/sub fan-out the membership pass consumes. There is no `cdp_events`
polling and no segment-resolution gate between ingest and the bus.

The config resolution is deliberately fail-soft: per-tenant destination
config is cached for 30 seconds, and a **transient** failure to read it
(a pool reconnect, a failover blip) is classified as transient and the
event is skipped rather than retried hot or surfaced as an operator
alert. The next event for that tenant re-resolves the config fresh — no
bad state persists, nothing to be cleared. Expect **single-event gaps**
on the destination during a transient store blip; a sustained outage is a
different shape (consecutive failures surface on the destination's
run-status, and a per-tenant dead-letter queue bounds loss against a
genuinely misconfigured bus).

Two deliberate design points to plan against:

* If you mirror audiences into your own downstream, do it off the stream,
  not off the membership table — `segment.entered` / `segment.exited` are
  first-class events on it, and the membership rows the dashboard reads
  are a read-model over the same crossings.
* Destination config edits (enable, credential swap) propagate to the
  delivery path within the 30-second cache window — expect a sub-minute
  warm-up, not a request-time effect.

## Section 5 — The segment staleness model, per surface

Segments are **real-time-ish, not instantaneous**. Every auto-refresh
segment has two recomputers that agree by construction — the per-contact
real-time pass on the event stream (the fast path) and the scheduled
batch refresh sweeping the tenant's auto-refresh set (the backstop) —
but each downstream surface holds its own staleness guarantee:

| Surface                                                          | Converges when                                             | Worst-case staleness                                                   |
| ---------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------- |
| Dashboard membership badge + segment list                        | Per-contact, on the contact's next `track`/`identify`      | \~1s after the event lands; the batch tick reconciles any missed event |
| `segment.entered` / `segment.exited` webhooks + journey triggers | Same crossing that wrote the badge                         | \~1s; exactly-once per crossing, even under a worker race              |
| Enriched profile traits (operator-referenced attributes)         | Daily re-enrichment sweep                                  | Up to \~24h after the source field changed                             |
| Your Kafka / Kinesis streaming destination                       | Every tracked event, incl. membership crossings            | Tens of milliseconds; single-event gaps on a transient config blip     |
| Ad audiences (Google / Meta / TikTok sync)                       | The destination's own run cadence after your segment moves | Provider-dependent — plan in hours, never seconds                      |
| Warehouse / object-storage exports                               | The destination's own run cadence                          | Per the destination's schedule — plan in hours                         |

`page` / `screen` / `group` / `alias` events and anonymous (pre-identify)
events carry no request-time membership effect: the real-time pass skips
them by design, and the scheduled tick converges them.

## Section 6 — What this means for reads

Three practical rules operators should code and brief against:

1. **Never read-your-own-write.** A `track` 200 acknowledges the event;
   it does not carry any membership decision. Two immediate reads — one
   to ingest, one to `GET /api/v1/cdp/segments` — are not causally linked:
   the second can return the pre-event membership. Poll the segment list
   or the membership badge; don't assert on the response of the first.
2. **Plan downstream in hours, not seconds.** A contact qualifying for a
   segment is not the same as an ad platform's custom audience containing
   that contact — the sync destination runs on its own cadence after the
   segment moves. If a campaign must act on a membership change now,
   trigger off the `segment.entered` webhook or a journey, not off an ad
   audience.
3. **`page` / `screen` events do not refresh segments at the fast-path
   cadence.** If your segment filters must react to them, the segments
   still converge — on the scheduled batch cadence, not per-event.

## Section 7 — Relationship to the event model and accounts

The pieces this page draws on are defined elsewhere:

* [CDP event model](/concepts/cdp-event-model) — the append-only stream
  the membership pass reads, the server-stamped arrival ordering it
  trusts, and the identity binding that makes a `contact_id` resolvable.
* [B2B accounts](/concepts/cdp-accounts) — the read-side projection the
  same event stream powers; group/account membership has the same
  read-after-ingest posture as segment membership.
* [CDP segments guide](/guides/cdp-segments) — how to build the segments
  whose lifecycle this page describes.
* [Async processing model](/concepts/async-processing-model) — the
  platform-wide rule (request acknowledges, workers converge) the CDP
  pipeline is one instance of.

## Cross-references

* [CDP event model](/concepts/cdp-event-model) — the stream this lifecycle recomputes over.
* [CDP segments](/guides/cdp-segments) — building the audiences.
* [Async processing model](/concepts/async-processing-model) — the platform async posture.
* [Consent and suppression model](/concepts/consent-and-suppression-model) — privacy governance on the same pipeline.
* [Operator observability map](/concepts/operator-observability-map) — where worker and destination health surfaces for operators.
* [Tenant isolation](/concepts/tenant-isolation) — each tenant's stream and workers are isolated per tenant schema.
