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

# CDP audiences: build segments, computed traits, and activate

> Turn clean CDP profiles and events into targeted audiences — build segments from attributes and behavior, derive computed traits, roll up account scores, and push audiences to ad networks.

# CDP Audiences

This guide walks the full path from raw customer data to an audience you can spend money against: what profiles the CDP stores, how to build a segment from attributes and behavior, how to stamp derived traits onto contacts, how B2B accounts get scored, and how to push the result to an ad network. It closes the loop opened by the [end-to-end campaign guide](/guides/campaign-end-to-end) — the audience you target a campaign with is built here.

## 1. What the CDP stores

The CDP maintains two things per contact and per account:

* **Profiles and identity.** Every contact carries resolved identifiers (email, phone, device ids), built-in attributes (name, company, lifecycle stage), your [custom fields](/guides/custom-fields), and derived data — tags, scores, and computed traits. Contacts in the same `company` roll up into an account for B2B scoring.
* **Events.** Behavioral activity arrives through the tracking surface (`POST /api/v1/cdp/track`) and file ingest (`POST /api/v1/cdp/file-ingest` for CSV batches): page views, link clicks, purchases, product events. Segments filter over this behavioral history with operators like `performed_event` and `event_count`, so the cleanliness of your event feed determines the quality of every audience downstream.

If your contacts or events are thin, no segment below will fix that — ingest first, then segment. The [event schemas API](/api-reference/endpoints/cdp) (`/api/v1/cdp/event-schemas`) lets you govern which event names and properties are valid, so a renamed event does not silently empty a segment.

## 2. Build a segment

A segment is a saved audience definition: a filter over contact attributes, custom fields, segment membership, and behavioral events. Create one with `POST /api/v1/contacts/segments`:

```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": "High-value recent clickers",
    "description": "Enterprise customers who clicked a campaign link in the last 30 days",
    "filters": {
      "and": [
        { "field": "lifecycle_stage", "op": "equals", "value": "customer" },
        { "field": "custom_attribute.plan_tier", "op": "equals", "value": "enterprise" },
        { "field": "event.Link Clicked", "op": "performed_event", "value": { "within": "30d" } }
      ]
    },
    "auto_refresh": true,
    "auto_refresh_interval_minutes": 1440
  }'
```

Filter-building rules worth knowing before you ship one:

* Conditions combine with `and` / `or` groups, nested up to three levels. A condition is `{ "field", "op", "value" }` — the operator vocabulary includes comparisons (`equals`, `gt`, `in`, `contains`), time-window membership (`in_last`), and behavioral verbs (`performed_event`, `did_not_perform_event`, `event_count`, `performed_sequence` for ordered journeys — "did A then B within 72h").
* Events are referenced with the `event.<event name>` field prefix — pair it with a behavioral operator. Custom fields use `custom_attribute.<key>`.
* Segments can reference other segments (`{ "field": "segment_id", "op": "in_segment", "value": "<segment_id>" }`) so a base "active customers" segment is defined once and reused everywhere.
* `auto_refresh` keeps membership materialized on a schedule (12 hours up to monthly). A campaign reads the materialized membership at send time, so your filter edits do not need a send-time re-evaluation.

Size the filter before saving it: `POST /api/v1/contacts/segments/preview` evaluates a filter against live data and returns the match count without persisting anything, and `POST /api/v1/contacts/segments/estimate` projects size for planning. If you would rather describe the audience in words, `POST /api/v1/contacts/segments/from-prompt` converts a sentence ("enterprise customers who clicked but did not convert this month") into a validated filter you can review and save.

For the full endpoint list — membership, exports, snapshots, overlap — see the [Segments API reference](/api-reference/segments).

## 3. Computed traits

Segments filter over facts that already exist. A **computed trait** manufactures a new fact: you author a deterministic predicate, and contacts whose data matches get the rule's `output_tag` stamped onto their profile. That tag then behaves like any other attribute — segmentable, personalizable, exportable.

Create a rule with `POST /api/v1/cdp/computed-traits`:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/cdp/computed-traits \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "High-value active customer",
    "description": "Predicted LTV above 500 USD, seen in the last week, opted in to SMS",
    "output_tag": "high-value-active",
    "predicate": {
      "all_of": [
        { "kind": "numeric", "trait": "predicted_ltv", "op": "gt", "value": 500 },
        { "kind": "date", "trait": "last_seen_at", "op": "within_days", "value": 7 },
        { "kind": "boolean", "trait": "sms_optin", "op": "eq", "value": true }
      ]
    },
    "enabled": true
  }'
```

How the predicate language works:

* **Facts.** Predicates read a fact bag per contact. Platform-supplied facts include `predicted_ltv`, `churn_risk_score`, `lifecycle_stage`, `engagement_score`, `last_seen_at`, `created_at`, `total_revenue`, `email_optin`, `sms_optin` — plus any custom-field key you pass; the vocabulary is open.
* **Leaves.** A leaf compares one fact: `numeric` (`eq`, `gt`, `between`, …), `string` (`contains`, `in`, …), `boolean`, `date` (`within_days`, `older_than_days` — relative windows so "last 7 days" needs no timestamp math), and `exists` / `not_exists` for presence checks.
* **Combinators.** Leaves nest under `all_of`, `any_of`, and `not`, up to a bounded depth so a rule stays auditable.
* **Missing facts are safe.** A missing or wrong-typed fact evaluates a leaf to false (except `not_exists`) — a rule never stamps a tag off absent data.

Two operational notes. First, assignments are maintained by a scheduled evaluator that sweeps enabled rules and syncs tags onto contacts; a rule you save tonight is reflected on the next sweep. Second, `POST /api/v1/cdp/computed-traits/:id/evaluate` runs a rule against a fact bag you pass inline without writing anything — use it from a rule-builder UI to answer "would this contact match?" before enabling.

## 4. Account scoring

Per-contact scores (churn risk, intent, propensity, LTV) already exist on the profile. For B2B motions you need the same math at the **account** level — one number per company. The scoring surface computes that on the fly, no ingestion or storage required:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/cdp/accounts/scores?sort=churn_risk&min_contacts=2" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

Every contact with a `company` value is grouped into an account, and per-contact scores roll up into:

* `account_churn_risk` — value-weighted churn (a high-LTV contact's churn moves the account number more than a trial user's). Accounts with no attributed value report no churn number rather than masquerading as a top risk.
* `expansion_propensity` and `intent_score` — upsell and intent signals averaged across the account's members.
* `total_value_cents` — the account's aggregate member LTV.
* `firmographic_band` and `health_segment` — size bucket (`solo` … `enterprise`) and the ABM triage label (`at_risk`, `expansion`, `engaged`, `stable`) you can target campaigns on.

`GET /api/v1/cdp/accounts/score?account=<name>` drills into one account with the per-contact member breakdown — which individual contacts are driving the account's risk, and what stage each is in.

## 5. Retention segments on churn risk

Per-contact scores are segmentable numeric fields, not just a sortable list. To build a self-updating at-risk audience, put `churn_risk` in a segment filter:

```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": "At risk — churn above 60%",
    "filters": {
      "and": [
        { "field": "churn_risk", "op": "gt", "value": 0.6 }
      ]
    },
    "auto_refresh": true
  }'
```

Three rules to keep retention audiences honest:

* **Unscored contacts never match.** A contact with no score yet reads as no score, so numeric rules fail-closed — a win-back flow cannot fire on a guess. Give newly imported contacts a scoring cycle (the daily re-scoring pass) before relying on a score filter.
* **Preview before you activate.** `POST /api/v1/contacts/segments/preview` returns the match count against live scores, so a threshold of 0.6 versus 0.8 is a two-call comparison, not a guess.
* **Auto-refresh keeps it live.** Scores move daily; a `churn_risk > 0.6` segment with `auto_refresh` re-evaluates after each re-scoring pass. Contacts crossing the boundary enroll in segment-triggered flows ([campaigns](/guides/campaign-end-to-end) — win-back enroll-on-entry, suppress-on-exit).

Prefer a snapshot over a threshold? Score the trained churn-propensity model's population and send the top of the list straight to a segment:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/cdp/predictive-models/churn_propensity/activate \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Churn watchlist", "limit": 500 }'
```

The response carries the created segment and member count — a static snapshot of the ranking you just previewed, ready for campaigns, ad-network sync, or export. If the model cannot be fit yet the response is a `422` with the fit report (missing outcomes, minimum sample sizes parsed back to you); let resolved lifecycle outcomes accumulate and retry.

`GET /api/v1/contacts/scores` lists every contact's churn risk, intent, propensity, lifetime-value estimate, and health label with cursor pagination — sort descending on `churn_risk` and filter by `segment_label` to power an at-risk review list.

## 6. Activate an audience

Building an audience is half the job; activation pushes it somewhere it earns spend. The CDP's activation surface maps a segment to an ad-network audience and syncs membership:

```bash theme={null}
# 1. Wire a platform: which segment feeds which ad-network audience
curl -X PATCH https://api.orbit.devotel.io/api/v1/cdp/audience/config/meta \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "connection_id": "<nango-connection-id>",
    "destination_audience_id": "<ad-network-audience-id>",
    "segment_id": "seg_high_value_clickers"
  }'

# 2. Run an activation: add (or suppress) members in the ad audience
curl -X POST https://api.orbit.devotel.io/api/v1/cdp/audience/activate/meta \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "segment_id": "seg_high_value_clickers",
    "operation": "add"
  }'
```

Supported platforms: Meta, Google, TikTok, LinkedIn, Snapchat, Pinterest, Reddit, The Trade Desk, and Criteo. Two things to know about what leaves your tenant:

* **Hashed, not cleartext.** Identifiers (email, phone, name, address columns) are normalized and SHA-256-hashed before dispatch — the ad networks match hashed columns against their own user graphs, so cleartext PII never leaves the platform, is never logged, and is never persisted on the destination config. The one exception is the mobile advertising id (IDFA/GAID), which the ad networks match raw by contract.
* **Use it for suppression too.** `operation: "remove"` pushes a segment as an exclusion — suppress active customers from acquisition spend, or suppress recent purchasers from a promo audience.

`GET /api/v1/cdp/audience/activations` returns the run history (counts only) so you can confirm what synced, and `GET /api/v1/cdp/audience/config` shows every platform mapping with its last-run status. Audiences also leave the CDP without ad networks: a saved segment is directly targetable by [campaigns](/guides/campaign-end-to-end) and flows, exportable to CSV (`GET /api/v1/contacts/segments/:id/export.csv`), and syncable to a warehouse via [reverse ETL](/api-reference/endpoints/cdp).

## 7. Identity resolution and consent

Two disciplines sit underneath everything above; audiences built on duplicated persons or unconsented contacts are wasted spend and a compliance risk.

**Identity resolution** merges duplicate contact records that represent one person — the same human arriving via web form, mobile app, and a CSV import, each with a different identifier. `GET /api/v1/cdp/identity/merge-candidates` lists proposed merges for review. Merging matters to this guide because segments and scores are only as accurate as the identity layer: merge first, then your churn scores and segment counts stop double-counting.

**Consent** gates whether you may use a profile at all. `GET /api/v1/cdp/consent/:contactId` reads a contact's current consent state per channel, and `POST /api/v1/cdp/consent/check` answers "may I contact this person on this channel for this purpose?" — the same verdict the marketing-send pipeline enforces before dispatch. When a contact revokes consent, the revocation propagates across downstream stores so a segment refreshed tomorrow does not re-include someone who opted out today. Keep audience definitions consent-aware where the channel requires it (`sms_optin`, `email_optin` are facts you can predicate on, as the trait example above shows), and rely on the send-time gate as the backstop.

## See also

* [Campaign end-to-end](/guides/campaign-end-to-end) — targeting this audience with a send
* [Ad-hoc audience CSV export](/guides/audience-export-adhoc) — pull a one-off CSV from an unsaved filter without creating a segment
* [Custom fields](/guides/custom-fields) — model the attributes segments and traits filter over
* [Segments API reference](/api-reference/segments) — full endpoint list for segments, exports, and overlap
* [CDP API reference](/api-reference/endpoints/cdp) — computed traits, scoring, activation, identity, and consent endpoints
