Identity resolution

How scattered events across devices and sessions collapse into one person.

A visitor browses on their phone, converts on a laptop a week later, and gives you an email at checkout. Those are three anonymous IDs and one human. Identity resolution is the step that works that out — and it’s what lets a conversion carry the click ID from a visit that happened days earlier.

Identifiers

Every event is broken down into the identifiers it carries. Fifteen kinds participate:

anonymous_id, user_id, email, phone, device_id, fbclid, gclid, ttclid, li_fat_id, msclkid, crm_contact_id, epik, rdt_cid, sc_click_id, twclid

Email and phone are normalized before matching, using the same rules as PII hashing — so Ada@Example.com and ada@example.com are one identifier, not two.

Matching

The extracted identifiers are looked up against your identity graph, and the number of people they hit decides what happens:

MatchesOutcome
0A new person is created, owning these identifiers.
1The event links to that person; any new identifiers are attached.
NThe people are merged into the oldest one.

Merging into the oldest is deliberate. The oldest record has the longest history, so keeping it preserves the earliest touchpoints — which is exactly what first-touch and time-decay attribution depend on. The losing records’ identifiers are relinked and their rollups absorbed, so nothing is discarded.

Merges happen when a single event bridges two previously separate identifier sets — the classic case being a logged-in checkout on a device that had only ever been anonymous.

What identify() does

identify() attaches known traits to the current person:

1identify({
2 user_id: 'usr_1041',
3 email: 'ada@example.com',
4 first_name: 'Ada',
5});

Two things follow. A user_id marks the person identified, and the traits become available for hashing and delivery on subsequent events — improving match quality at every destination.

identify() only emits an event when something actually changed. Calling it on every page render is harmless — repeated identifies with identical data are skipped rather than billed as volume.

Click backfill

Ad platforms match conversions best when the conversion carries the click ID from the ad that produced it. But the click happens on a landing page, and the conversion often happens days later on a page with no query string at all.

So every click ID seen on a landing page is stored against the person with a per-platform expiry. When a conversion event arrives carrying no click IDs, unexpired clicks for that person are backfilled — most recent wins per platform.

Backfill is skipped entirely if the conversion already carries any click ID. The click the visitor actually arrived on is always more truthful than one reconstructed from history.

Expiry windows

Each platform’s window matches its own attribution policy — sending a click ID older than the platform accepts just produces an unmatched conversion.

PlatformClick IDsExpiry
Googlegclid, gbraid, wbraid90 days
Microsoftmsclkid90 days
LinkedInli_fat_id90 days
Pinterestepik30 days
Redditrdt_cid30 days
TikTokttclid28 days
Snapchatsc_click_id28 days
Xtwclid14 days
Metafbclid7 days

Meta’s seven days is the tightest window by a wide margin. If your sales cycle runs longer than a week, a Meta-attributed conversion frequently arrives after its fbclid has expired — collect email and phone so there’s still something to match on.

Backfill only triggers on conversion events. See the taxonomy for which names qualify.

gbraid and wbraid

Both are captured and stored, and both count for attribution — but neither is ever used to stitch identity.

They’re Google’s privacy-aggregated iOS click IDs, and aggregation is the point: a single value can represent many users. Treating one as an identifier would merge unrelated people into a single record, which is a far worse failure than a missed match. gclid has no such problem and is used normally.

Person segments

Resolved people are labelled with segments you can filter and report on:

  • identified — a user_id has been seen for this person.
  • paying — the person has a purchase in their history.
  • anonymous_high_intent — not yet identified, but showing intent-tier activity. See the taxonomy.
  • consent_denied — the person’s consent state blocks delivery. See Consent & privacy.