Skip to main content

First CDP activation, end to end

The CDP has four phases — ingest, identity resolution, segmentation, activation — and each has a dedicated guide that goes deep on its own surface. What none of them do alone is walk you through one continuous pass: send an event, watch it stitch to a person, build an audience over that history, and push the audience to a destination you can spend against. This tutorial does that one pass, start to finish, in one sitting. Do this walkthrough first. Once the loop is live once, the phase guides become your deepening references:

Why a single end-to-end pass matters

Each phase guide assumes the others exist. Read in isolation they leave you stitching the loop yourself: the source guide tells you how to accept events but not what to do once they land; the segments guide assumes resolved profiles and clean events; the activation guide assumes a segment worth publishing. New CDP workspaces fail in the gaps between guides — events ingested but never stitched, segments built over unresolved duplicates, activations publishing thin membership nobody diagnosed upstream. Run this one pass against a test source and a test destination before you turn on real traffic. When something misbehaves later, you re-enter the phase guide for that phase — but you re-enter it knowing which phase is broken, because you have seen the whole loop healthy once.

1. Prerequisites

Have all three of these before the first event:
  1. A published tracking plan. Declare the events your app will send and the property shape per event, in soft enforcement mode, on the tracking plan surface. Soft mode logs violations without rejecting events, so a new producer can never stall on a mismatch while you iterate. Tighten to strict later.
  2. One connected source. An ingest secret minted under Integrations → CDP → Source (inbound) and deployed to your app — the source and destinations guide walks minting and rotation. Your app posts signed events to https://api.orbit.devotel.io/cdp/v1/<ingest_id>/track.
  3. One destination with credentials. An ad-network audience reached over an OAuth connection, or an HTTPS webhook sink you control. Wire the credentials before you need them — the activation phase only maps a segment onto a destination that already authenticates.
Also have an API key with an owner, admin, or developer role. Every mutation in this walkthrough (rules, segments, activations) is gated to those roles; reads accept more.

2. Ingest a source

Pick a source kind on Integrations → CDP — Segment, RudderStack, a generic webhook, or your own SDK posting directly. Mint the ingest secret from the Source (inbound) tab, deploy it to the app, and sign every request per the ingest-signing guide: the three headers X-Orbit-CDP-Timestamp, X-Orbit-CDP-Nonce, and X-Orbit-CDP-Signature over <timestamp>.<nonce>.<raw body>, keyed by the secret plaintext. Fire one track event from the app. Then confirm it landed — never assume:
  1. Open Integrations → CDP → Event debugger.
  2. Filter Type = track and match your event name.
  3. Expand the row and read the exact properties and context the gateway stored.
If the row is missing, the failure is at the signature layer (a 401 the app swallowed) or the tracking plan is rejecting the payload — the event debugger and DLQ guide walks both, including how to read a spilled or lost payload body. Do not proceed until one real event is visible here.

3. Resolve identity

Events arrive carrying whatever userId your SDK sent. Whether that stitches onto a contact — and which contact — is decided by the identity rules, not by the event itself. The default resolves userId against external IDs only; an email- or phone-shaped userId does not stitch until you enable that match shape.
  1. Open Integrations → CDP → Identity resolution and review the match shapes: External ID (seeded, priority 100), plus opt-in Phone, Email, and Anonymous ID.
  2. If your events carry email or phone as the identity, simulate first on Audience → Identity resolution — the dashboard refuses to save a change to those shapes until a simulation has run in your session, because an over-broad shape stitches wrong people together irreversibly. Read the estimated merge count; if it over-merges, narrow the shape or raise its priority.
  3. Save the rule set back on the CDP page, then set the survivorship policy (which value wins a merge per field) in the same builder.
  4. Confirm resolution landed: open Audience → Identity resolution and check that your test event’s identity folded onto one contact, not a review-queue duplicate. The data catalog and identity rules guide covers priorities, the simulation gate, and the policy-versus-review-queue split.
Skip nothing here: every segment below reads resolved contacts, so unresolved duplicates survive into every audience count you build.

4. Build the segment on resolved events

With events landing and identity stitching, build the audience. A segment is a saved filter over contact attributes, custom fields, and behavioral events; create with POST /api/v1/contacts/segments. Two worked examples — a plain filter and a SQL trait feeding it.

A worked behavioral filter

Three things to notice. Events are referenced with the event.<name> prefix and pair with a behavioral operator — performed_event, did_not_perform_event, event_count, or performed_sequence for ordered journeys. The within window is relative, so “last 14 days” needs no timestamp math. And auto_refresh materializes membership on a cadence — an activation in the next step reads that materialized membership, so set it. Size the filter before you save it. POST /api/v1/contacts/segments/preview evaluates the same filter against live data and returns a match count without persisting; a count of zero or “the whole tenant” both mean the filter is wrong, not the data.

A concrete SQL trait feeding the segment

When the fact you need already exists as warehouse SQL — a finance-owned LTV view, a dbt model — adopt it as a SQL trait instead of rebuilding it in the predicate DSL. One read-only SELECT returning (user_id, trait_value), run on your schedule, stamps each value into the contact’s traits map:
Once the trait materializes, it is segmentable like any attribute — { "field": "traits.ltv_tier", "op": "equals", "value": "high" } combines freely with the behavioral conditions above. The full engine-choice tree (DSL predicate, SQL, AI) is on the SQL-traits page; the complete filter vocabulary is the segments guide.

5. Activate to a destination

Activation publishes the segment somewhere it earns spend — an ad network’s audience or your webhook sink — on a cadence you control. Walk it in two calls, per the activation pipeline guide. Wire the destination once. For an ad network (Google shown; Meta, TikTok, LinkedIn, and the rest of the catalog behave the same):
For your own HTTPS sink instead, PATCH /api/v1/cdp/audience/webhook with target_url and a signing_secret; deliveries arrive signed X-Orbit-Signature: t=<unix-sec>,v1=<hmac-sha256>, the same scheme your existing Orbit webhook verifier already checks. Either way, identifiers are SHA-256-hashed before dispatch to ad networks — cleartext PII never leaves the tenant. Run the first activation explicitly rather than waiting for the schedule:
Dry-run it first with POST /api/v1/cdp/audience/preflight/google whenever the verdict is uncertain — same gate, nothing dispatched. The pre-activation gate blocks undersized audiences, low identifier coverage, and consent or suppression violations; the failing check ids come back in the response. If this org has maker-checker on, the run queues for a second person’s approval — submit, have an owner approve, re-submit with the returned approval id.

6. Readback and troubleshooting

Confirm the run landed, then learn the lag budget so you know healthy from stuck. Confirm. GET /api/v1/cdp/audience/activations lists run history; GET /api/v1/cdp/audience/activations/:id drills one run with requested / matched / skipped counts and per-member rejection reasons (never PII). Status ok with matched near requested is healthy; partial names its bucket; skipped means nothing matched or no connection resolved at dispatch. For a warehouse destination instead of an ad network, read the same verdicts on the sync run log — per-run attempted / delivered / rejected / failed and error classes (auth, validation, rate_limit, …) that decide the retry loop. The lag budget. Each phase has its own expected delay; a complaint only becomes a bug when one phase exceeds its budget: Reading the table top-down converts “the audience is wrong” into “phase N is over budget” in one pass. Then re-enter that phase’s dedicated guide — source and destinations, identity rules, segments, activation, or reverse ETL — with the failure already localized. The four failure shapes you will actually meet. A 409 CONNECTION_NOT_CONFIGURED on activate means the destination is not fully wired (missing audience_id or connection id). A 422 AUDIENCE_PREFLIGHT_BLOCKED names its failing gate check — resize the segment, fix identifier coverage, or override deliberately. Zero matched members means rows arrived with no strong identifier — fix the mapping at ingest, not the activation. A partial run whose drilldown shows validation buckets with “missing or empty upsert identifier” means the upsert key is unmapped — fix the field mapping, then rerun. All four are covered per-phase in the guides linked above.

See also