Identity resolution and merge semantics
Every segment membership, funnel, attribution model, and routing decision is only as correct as the contact row it reads. This page defines how an incoming identifier becomes one contact — which keys resolve, which merge direction wins, what each fold-in writes onto the survivor, and what updates downstream — so you can reason about the duplicate or split-contact shapes the operator surfaces show. The operational guide (queue, console, undo window) is Identity resolution; the shape of the upstream stream is The CDP event model.Section 1 — Identity keys and precedence
Four keys anchor a contact. Each is normalized before it enters the identity index, so formatting never decides a match:
Precedence is tenant-owned.
PUT /api/v1/cdp/identity-rules authors one
rule per identifier type (external_id, email, phone, anonymous_id)
with a numeric priority; a lookup matches on the strongest enabled rule
at the time it runs. An operator who imports from a CRM typically puts
external_id first so the CRM key, not a respelled phone, settles the
match. The rule set resolves lookups everywhere — ingest binding, imports,
inbound messaging, and the stitch API.
Anonymous traffic is a first-class key, not noise. Events carry one or both
of userId and anonymousId; they hold contact_id = NULL until the
identity pass binds them (see
The CDP event model, Section 2). When an
identify later pairs that anonymous_id with a strong phone or email,
the pre-identify session folds onto the resolved contact in the same
request.
Section 2 — Deterministic versus probabilistic resolution
Orbit runs two resolution layers on the same identifier sets, and the distinction decides what a duplicate scan can do unattended:- Deterministic. Rows whose normalized identifiers are equal on a
declared key group transitively into one profile — the
emailplusphonepair this-and-that corroborate each other, so equality, not similarity, does the match.POST /api/v1/cdp/identity-resolution/previewdry-runs the groups without writing;POST /api/v1/cdp/identity-resolution/runauto-merges each group whose confidence clears your threshold and leaves the rest as a review queue. - Probabilistic. The remainder — a fuzzy name, a phone-format
respelling, a shared non-unique channel id (WhatsApp or Viber) — ranks
into a review band instead of merging.
GET /api/v1/cdp/identity/merge-candidatesreturns ranked pairs with a confidence and a band;min_confidenceandinclude_auto_merge=falsebound the scan to the review queue. A phone-only link whose members carry different emails (the shared-handset or reassigned-number ambiguity) lands here by design — it is a steward’s call, never an automatic fold.
POST /api/v1/cdp/identity-resolution/simulate) previews which pairs the
new rule would collapse and raises an over-merge warning — run it before
enabling so a too-loose rule fails in preview, not on live contacts.
Section 3 — Merge direction and survivorship
The primary record survives; secondaries fold in and are marked merged-away so channels cannot re-contact them as distinct people. Field-level survivorship is decided by three sources, in a strict precedence:- Per-request pins (
fieldStrategies, plusattribute.<key>pins for custom fields) — set on the merge call itself, highest precedence. - Your tenant survivorship policy (
GET/PUT/DELETE/api/v1/cdp/survivorship-policy) — a rule per governed field over the scalar contact columns (phone, email, WhatsApp id, Viber id, name, company, country code, timezone, language, external id). Strategies areprefer_target/prefer_source,most_recently_updated/most_recently_created,prefer_non_null, andprefer_source_system(value from the record whose source is a named system —crm,salesforce,import). - The safe default — blanks and nulls never overwrite a populated value; a partial update cannot erase an established trait.
- Reversal is bounded, not vague. Every merge returns a
merge_id; the undo window stays open for 30 minutes, andPOST /api/v1/contacts/unmerge/:mergeIdrestores the split while it is open.GET /api/v1/contacts/merge-historyis the full audit list. - Every merge writes a durable audit row recording the verdict, the confidence, and the identifiers that corroborated the match — the GDPR art. 15/22 “how did this decision happen” answer. A merge you applied through the review queue carries the steward’s verdict on the same row.
Section 4 — Downstream effects
A merge redirects channel membership, tags, custom-field values, conversation history, and CDP events onto the survivor — so consumers that read the contact row re-derive from the unified profile on their own cadence:- Segments. The same fold-in that redirects events also redirects the contact’s segmentable fields; auto-refresh segments pick the change up on their next evaluation cadence, the scheduled refresh converges the rest. See How CDP segments recompute.
- Predictive attribute routing. Inbound voice routing matches on the resolved contact’s attributes (score and trait tags). After a merge, a caller’s routing verdict derives from the survivor’s profile — a routing rule that once split one person across “high value in one record, no score in the other” now reads one profile consistently.
- Conversation threading. Inbound messaging, voice, and video resolve by the bound phone or email; they read the survivor directly, so two channels that once opened parallel threads converge onto one timeline.
- Computed traits and scores. Churn, CLV, and engagement scores recompute over the unified event history instead of splitting across two partial records.
Section 5 — Where duplicates surface
Duplicates announce themselves on three read paths; check them when a count looks inflated or a caller history reads wrong:- The review queue —
GET /api/v1/cdp/identity/merge-candidatesor the dashboard Audience → Identity resolution page ranks open pairs; duplicates that never reach a merge sit here. - The contacts surface —
GET /api/v1/contacts(or the merge-history feed) shows the survivor and its folded-in secondaries; a contact with an unexpected twin usually cleared the review queue or came back through a re-import. - Number-ownership conflicts — when the same phone claims two
contact rows (common after a re-import that bypassed
external_id), inbound resolution and screen-pop read an ambiguous owner until a merge settles it.
unmerge catch the rare over-merge.
Section 6 — Tenant-owned decider
Every behavior above is a tenant-controlled surface; the platform enforces no canonical merge policy:- Rule priority and identity keys are set on
identity-rules. - The auto-merge threshold and the review band are set per tenant.
- Survivorship precedence (pins → policy → blank-safe default) is set per tenant and per request.
- Merges are reversible inside a 30-minute window and audited permanently, so an incorrect fold is recoverable, not silent.
Cross-references
- Identity resolution (guide) — the operational queue, console, and undo flow.
- The CDP event model — the identity-hint envelope upstream of resolution.
- How CDP segments recompute — the membership cadence a merge feeds.
- CDP API reference — the endpoint-level surface of every call named above.