Identity resolution
Duplicate contacts corrupt everything downstream of them: a message history split across two records reads as two customers, a segment counts one person twice, churn and LTV scores split, and a contact opted out on one record can still be reachable through its twin. Identity resolution collapses those duplicates into one golden record so every channel, campaign, and downstream analytics read the same profile. This guide covers how Orbit matches identities, how you feed identifiers in, how to review and apply merges in the dashboard and via the API, how to control which values survive a merge, and how consent and suppression follow the merged record.1. Why identity resolution matters
A person reaches you through different identifiers at different times — a web form (email), an inbound SMS reply (phone), a CRM import (external id), an opt-out list entry. Until those are stitched together your data lies:- Omnichannel history is fragmented. The conversation timeline, customer 360, and search across message history show one thread per contact record, so duplicates split one person’s story.
- Segments and scores miscount. CDP segments filter live contact rows; duplicates inflate counts and can split a person’s lifecycle stage, computed traits, or churn/LTV scores across two records.
- Suppression and consent leak. A person opted out on one record can still be mailed via its duplicate (see section 6).
2. How Orbit matches — deterministic vs probabilistic
Orbit ships two matching surfaces, layered on the same identifier sets: Deterministic rules are the always-on layer.PUT /api/v1/cdp/identity-rules authors a rule per identifier type (external_id, email, phone, anonymous_id) with a priority, and a contact lookup matches on the strongest enabled rule at the time it runs — so tracked events, APIs, and imports resolve to the right contact as soon as an identifier arrives.
GET /api/v1/cdp/identity/merge-candidates runs a bounded scan over your contacts and returns ranked pairs — the same person entered twice with a fuzzy similarity match on name, phone-format respellings, or shared non-unique channel ids (WhatsApp / Viber). Each candidate carries a confidence (0–100) and a band:
- High confidence (
auto_merge) — the candidate is prepared to merge automatically (for example two records whose normalized phone and email corroborate each other). - Review band (
review) — the deterministic rules could not make the call; a steward confirms each one before it merges. A phone-only link where the members carry different emails (the classic shared-handset / reassigned-number ambiguity) lands here, not in an automatic merge.
min_confidence (0–100) on the query string to bound the scan to the band you actually want to see, and include_auto_merge=false to look only at the review queue.
3. Submit identities and map identifiers
Identity resolution can only think over the identifiers your contacts actually carry. Two paths feed them in: At contact creation or import. Pass a phone, email, and your ownexternal_id on POST /api/v1/contacts (or on campaign imports and webhook-driven upserts). Prefer your own system’s stable key in external_id over the phone/email alone — the deterministic rules above match on external_id first when a rule for it is enabled.
POST /cdp/v1/:ingest_id/identify|track|alias|batch). An identify upserts the profile and stamps userId, anonymousId, and traits; an alias merges two identities explicitly (the same deterministic merge the identifier rules above rely on). See the CDP ingest reference for the full envelope, and the data model for the id and envelope conventions every endpoint returns.
4. Resolve and review merges
The dashboard surface is Audience → Identity resolution. It is the merge console, the ranked match list, the rule simulator, and the survivorship policy editor on one page. By API you interact with the same queue:- Dismiss it. Mark the candidate as not-a-match and it leaves the queue (the pair can be re-filed later if the data changes).
- Merge it. With an accept verdict the secondary record collapses into the primary (see section 5).
POST /api/v1/cdp/identity-resolution/simulate returns a what-if preview (which pairs a proposed rule would merge, a per-field survivorship preview, and an over-merge warning when a proposed rule would collapse clearly-different people) without writing anything.
5. Merge contacts safely
A merge collapses one or more secondary records into a primary survivor, redirects channel membership, tags, custom-field values, conversation history, and CDP events onto the survivor, then marks the secondary as merged-away so ids can no longer be accidentally re-contacted as distinct people. The response returns amerge_id and a 30-minute undo window; POST /api/v1/contacts/unmerge/:mergeId reverts the fold-in while the window is open, and GET /api/v1/contacts/merge-history lists every merge for audit.
Which values win on the survivor is governed by your survivorship policy — a tenant-wide rule set authored on the Identity resolution page (or GET/PUT/DELETE /api/v1/cdp/survivorship-policy) that applies to every merge, console or API. Rules pick a strategy per governed field (the merge copies these scalar contact columns: phone, email, WhatsApp id, Viber id, first/last/display name, company, country code, timezone, language, external id):
prefer_target/prefer_source— always keep the surviving (or folded-in) record’s value.most_recently_updated/most_recently_created— take the value from the record that has the newer timestamps.prefer_non_null— fill in an empty survivor field from the folded-in record and leave populated values alone.prefer_source_system— prefer the value from whichever record’ssourcecolumn matches a named system (crm,salesforce,import), so provenance, not just recency, can decide golden fields.
fieldStrategies or per-attribute attribute.<key> preferences for custom fields, and a custom field survives a merge only when a rule or a pin says what to do with it. Second, run the simulator (section 4) against the full policy before enabling it so an over-merge warning catches a rule that would collapse two different people.
6. Suppression, opt-outs, and consent after a merge
Consolidating one person into one record only helps if the merged record carries the person’s real consent state. When the survivor and the merged record carry opposite states (one opted out of SMS, the other opted in), check the opt-out lists and the consent verdicts before you trust the merged record for sending. On the API,GET /api/v1/cdp/consent/:contactId reads a contact’s per-channel consent state and POST /api/v1/cdp/consent/check answers “may I contact this person on this channel, for this purpose?” — run one once against the survivor if the two records could be in dispute. When a contact revokes consent, the revocation propagates to downstream stores so a segment refreshed after the merge does not re-include someone who opted out on any of the records that were folded in. The same records surface in the public consent form if you collect consent at intake.
7. Troubleshooting common cases
Over-merging — two unrelated people got collapsed. Two records with an identical phone but different emails are a shared-handset / reassigned-number ambiguity (section 2) and belong in the review band, not the high-confidence band. If one slipped through, the fix is the 30-minuteunmerge on the same page (section 5), then re-review the pair with a dismiss verdict on the queue. To prevent a rule from producing that shape, run POST /api/v1/cdp/identity-resolution/simulate before enabling it — it names the overlapping identifiers and flags when a proposed rule would collapse clearly different people.
Cross-channel duplicates keep reappearing. A re-imported duplicate whose email and phone are both already attached to the live record is forced to leave those columns null and can only repeat soft fields (name, company) or a shared non-unique channel id. Run the review scan (GET /api/v1/cdp/identity/merge-candidates) after every bulk import — if you imported with a stable external_id, the deterministic rules (section 2) catch the pair deterministically instead of re-queueing a fuzzy match.
Conflicting identities on the survivor. If the merged record suddenly has the wrong email or phone, the wrong survivorship strategy chose the losing side — check GET /api/v1/cdp/survivorship-policy for the rules that govern that field, then re-merge with a per-request fieldStrategies pin that overrides the tenant policy (per-request pins always win over the policy).
Two stewards race on the same candidate. A verdict on a pending review row is terminal — a second steward’s accept or reject returns a 409. If a merge is rejected because one of the contacts was already merged or removed, the API returns a “stale candidate” error and the console drops the stale pair from your queue — refresh and the corrected pair list re-appears.