Tracking & Attribution

How Does Visitor Identity Resolution Work in Attribution?

Visitor identity resolution joins one person's sessions into a single record using shared identifiers — and it decides whether your attribution is real.

Quick answer

Visitor identity resolution is the process of deciding that several browsing sessions belong to the same person and joining them into one record. It works by matching identifiers those sessions share — a first-party cookie id, a normalised email or phone number, an ad click id, a payment customer id — and ranking each match by how much it actually proves. Deterministic identifiers the person supplied are safe to join on; inferred ones like IP address need tight limits. Without resolution, one buyer looks like five strangers and the sale gets credited to whichever session was last.

Tell us what's broken. We'll fix your tracking — free.

Describe the tracking/attribution problem you're stuck on and we'll map it to a fix: server-side conversions to Meta, Google, TikTok and Pinterest, plus first-party tracking that survives Safari. No code required.

Visitor identity resolution is the process of deciding that several browsing sessions belong to the same person, and joining them into one record. It works by matching identifiers those sessions share — a first-party cookie id, a normalised email address or phone number, an ad click id, a payment customer id — and weighting each match by how much it actually proves.

Without it, one buyer looks like five strangers, and the sale gets credited to whichever session happened to be last.

This is the layer underneath every attribution report you read. If it is wrong, the report is wrong in a way that no choice of attribution model can fix.


Why does one customer show up as five different visitors?

Because a “visitor” in most tools is a cookie, not a person, and cookies do not last. Every time one is cleared, blocked, capped or never set, the same human comes back as somebody new. A single purchase can end up spread across four or five visitor records that nothing ever connects.

The usual causes, roughly in order of how much damage they do:

  • Device switching. The ad gets clicked on a phone at lunch; the form gets filled on a laptop that evening. Two devices, two storage buckets, no shared identifier unless something deliberately creates one. This is the ordinary path for considered purchases, which is why tracking leads across devices is a separate problem from tracking sessions.
  • Browser storage limits. Safari’s Intelligent Tracking Prevention (ITP) shortens the life of cookies written by JavaScript, so a visitor returning after a gap arrives as a stranger even though nothing was deleted by hand.
  • Ad blockers and privacy extensions. A blocked script never writes an id at all. Those sessions are not missing — they are present, anonymous, and counted as new every time.
  • Iframe embeds. When your form lives inside a Typeform, Jotform or GoHighLevel frame, the frame’s URL carries none of the campaign parameters from the parent page. The lead is real and the source is blank.
  • Private windows and second browser profiles. A fresh storage bucket every time, by design.
  • Long consideration windows. A trial that converts six weeks later has outlived the identifiers that would have explained where it came from.

Each of these is survivable on its own. Together they mean a meaningful share of your conversions land with no visible source, which is why ad conversions show up as Direct far more often than any media buyer’s instinct says they should.


What identifiers can you actually join people on?

Two kinds, and the difference between them is the whole discipline. Deterministic identifiers are ones the person supplied or a platform stamped: an email address, a phone number, an ad click id, a logged-in user id, a payment customer id. Probabilistic ones are inferred from context: an IP address plus a user agent, a device fingerprint, timing. Deterministic joins are safe to act on. Probabilistic joins need limits, because their failure mode is merging two different people into one customer.

Dark dashboard diagram of the identity cluster resolver: six identifier tiers listed on the left — visitor id, email, phone, IP plus user agent, device fingerprint, click id — each tagged deterministic or inferred, converging into one resolved person record on the right showing sessions, devices and journey span

Deterministic joins: the person handed you the key

A deterministic join is one you could defend in a meeting. The same normalised email appears on two sessions, so those sessions are the same person. The same works for a phone number in E.164 format, the same ad click id seen twice, or the same payment customer id on two orders.

Normalisation is where these quietly fail. Ava.Chen@gmail.com, ava.chen@gmail.com and avachen+forms@gmail.com are one mailbox and three different strings. 0412 345 678 and +61412345678 are one phone and two different strings. If you store what was typed instead of a canonical form, your deterministic joins silently stop firing and you will believe you have more customers than you do.

Probabilistic joins: useful, and worth a leash

IP address plus user agent is the workhorse here, and it is genuinely useful — the same device on the same network within a short window is usually the same person. It is also how a shared office network or a mobile carrier’s shared address turns twelve people into one.

Three rules keep this honest. Keep the time window narrow. Cap how many records a single weak signal may merge. And never let a probabilistic signal overrule a deterministic one — if the emails disagree, the emails win.

The expensive mistake is treating every signal as equal. A join on a hashed email and a join on “same office IP” are not the same claim, and a system that stores them the same way will eventually tell you that your entire Melbourne office is one very enthusiastic customer.


What does identity resolution change in your reports?

Four numbers move, and not all of them in the direction you were hoping for. Lead counts fall, because duplicates collapse. The Direct bucket shrinks, because sessions that had no source of their own inherit one. The measured sales cycle gets longer, because the real first touch is now further back. And first-touch and last-touch stop agreeing with each other.

That last one is the point. When both models return the same answer, one of them is not doing any work. The gap between “what started this” and “what closed this” is what tells you whether your top-of-funnel spend is doing anything.

Expect the deduplicated lead count to be the uncomfortable conversation. A CRM that counts lead rows and a system that counts unique contacts will disagree, and the smaller number is the correct one.


How do you attribute a session that has no source at all?

You look at the person’s other sessions. A conversion session arriving with no campaign parameters and no referrer is not evidence of Direct traffic — it is a missing value, and missing values can be filled in from neighbours.

The mechanism is inheritance. If the same person has another session close in time that carried a click id or campaign parameters, the conversion belongs to that click. The classic pattern: someone clicks an ad, lands, wanders off, comes back an hour later by typing the domain, and buys. The buying session looks Direct. The click that paid for it happened ninety minutes earlier on the same device.

Inheritance has to be time-boxed and evidence-ranked, or it becomes a machine for inventing attribution. A useful version inherits strong signals across a short window from sessions proven to be the same device, allows for a phone switching from wifi to mobile data mid-journey, and falls back to a referrer-only match only when nothing better exists.

The underlying principle is worth stating plainly: Direct should mean “we do not know”, not “a channel called Direct”. Anything that treats an absence of data as a traffic source is reporting on its own blind spot.


What can you fix without buying anything?

More than vendors admit. Five fixes, each with the limit that comes with it:

  1. Set your first-party cookie from the server. A Set-Cookie header from your own domain is not subject to the lifetime cap browsers apply to cookies written by JavaScript. Highest-value change on the list, and it does nothing for cross-device.
  2. Normalise email and phone before you store them. Lowercase and trim email, strip Gmail dots and + suffixes before hashing, convert phones to E.164. Cheap, permanent, and only helps once you have the contact details.
  3. Capture the click id server-side from the landing URL. fbclid, gclid, gbraid, wbraid, msclkid and ttclid arrive in the URL, where no blocker can remove them. Requires you to have a server-side touch.
  4. Pass identifiers explicitly across domains. If your landing page and checkout sit on different domains, the id has to travel in the URL — which breaks the moment a redirect or an email client rewrites the link.
  5. Deduplicate in your CRM on normalised email and phone. Worth doing regardless. Your CRM only ever sees people who submitted something.

The ceiling is the same for all five: they start working at submit time. Sessions where nobody submitted anything stay anonymous forever, and on a typical form those are the majority. Someone who typed an email and then abandoned is a partial lead — an identified person — but only if something captured the field before the page closed. If it did not, there is no identifier to resolve, and no amount of graph-building creates one.


How does PartialLeads resolve visitor identity?

PartialLeads maintains a per-tenant identity graph and resolves each person’s sessions with a cluster resolver that unions across six tiers: the durable visitor id, email, phone, IP plus user agent within a bounded window, a device fingerprint, and a shared click id seen on more than one session. Each tier carries its own confidence, and the strong tiers outrank the weak ones rather than averaging with them.

The visitor id itself is set by the server as a first-party cookie, with local storage as a backup, rather than written by the script in the browser — which is why Safari’s cap on script-written cookies does not quietly delete your returning visitors. The device-fingerprint tier is bounded by both a time window and a row cap, because an unbounded fingerprint tier is how false merges happen.

On top of the cluster sits visit-sibling inheritance, which heals attribution inside a single visit in three tiers: a strong inherit from a sibling session on the same client, IP and user agent within a short window; a “cellular handoff” variant that allows the IP to change when the user agent and Meta click cookie still match, covering a phone that swapped wifi for mobile data; and a weak referrer-only fallback when nothing stronger exists.

When a purchase arrives from Shopify, WooCommerce, Stripe, GoHighLevel or a webhook, it is matched back to a session on a ranked ladder — echoed visitor id first, then email, then phone, then IP — and both a first-touch and a last-touch row are written, snapshotting the campaign parameters, click ids, referrer and landing page as they were. Purchases that match nothing still land, and stay recoverable when identity arrives later.

Resolution also feeds dispatch. The identifiers on the cluster are what get hashed and sent to the ad platform through the Conversions API, and the completeness of that set is what the platform scores as event match quality. With email and phone as required form fields, PartialLeads consistently delivers EMQ 9+ — observed across customer accounts. Email-only forms cap structurally lower, because you cannot transmit a match parameter you never collected.

Dark dashboard mockup of the Leads list with the Journey column: each row shows a chain of channel badges ending in a green conversion square, with Partial and Completed status badges and a revenue column

You can see the resolution working without opening anything. The Leads list renders each person’s touches left to right in a Journey column — a badge per session, a grey dot where the source is genuinely unknown, a green square for the conversion — with visual weight encoding signal strength and the ring treatment encoding how that session was stitched into the cluster. The Attribution report then shows first-touch, last-touch and a resolved model side by side, and itemises recovered attribution by mechanism.

What breaks The mechanism Where you see it in the dashboard
One person counted as five visitors Six-tier cluster resolver with per-tier confidence Leads list, unique contacts vs lead rows
Safari forgets the visitor after a short window Server-set first-party visitor cookie, not script-written Returning visitors stay stitched on the Journey
Ad clicked on a phone, purchase made on a desktop Email, phone and payment-customer joins across devices Journey timeline, Attribution report
Conversion session arrives with no source Visit-sibling inheritance, three tiers, time-boxed Recovered attribution, reclassified sessions
Purchase cannot be tied back to a click Ranked purchase-to-session match ladder Purchases ledger, matched vs unmatched
Click id lost to a blocked or malformed cookie _fbc reconstructed from the fbclid in the URL CAPI activity log payloads
Weeks between the first click and the sale First touch stored on the cluster, not the session Time to purchase, first vs last touch
The ad platform cannot match your conversion Full hashed identifier set sent server-side Event match quality in Events Manager
Two form fills, two different email addresses Phone and visitor-id joins merge the cluster Leads list, unique contacts

Two honest constraints. Identity resolution can only work with identifiers that were collected — it recovers the people who identified themselves somewhere, not the ones who never did. And matching on the platform side is not ours to control: PartialLeads controls what gets dispatched and how complete it is, while whether Meta or Google can resolve that person to an account depends on their graph, consent, and opt-out status. Any vendor claiming otherwise is describing the half of the problem they do not own.

Tell us what's broken. We'll fix your tracking — free.

Describe the tracking/attribution problem you're stuck on and we'll map it to a fix: server-side conversions to Meta, Google, TikTok and Pinterest, plus first-party tracking that survives Safari. No code required.

Sources

  1. Meta for Developers — Conversions API, customer information parameters: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters
  2. Meta for Developers — Conversions API, server event parameters: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/server-event
  3. Meta for Developers — Deduplicate Pixel and Conversions API events: https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events

Frequently asked questions

QWhat is the difference between an identity graph and identity resolution?
The graph is the data structure; resolution is the act of querying it. An identity graph stores the links between identifiers — this email belongs with this visitor id, which belongs with this phone number. Resolution is what happens when a new session arrives and the system walks those links to decide which existing person it belongs to, if any.
QIs deterministic matching always better than probabilistic?
For acting on, yes. For coverage, no — probabilistic signals are often the only thing available, because most sessions never produce an email or a phone number. The right design uses both and keeps them separable, so a weak join can be overruled by a strong one and you can always tell which kind of evidence a given merge rests on.
QDoes identity resolution work if the visitor never fills in a form?
Partially. A returning visitor on the same browser resolves on the first-party cookie alone, and sessions within one visit can be stitched by device signals. What cannot happen is resolving that anonymous person to a name, an email or a customer record, because none of those identifiers exist yet. Resolution connects sessions; it does not invent identity.
QWill identity resolution make my numbers go up?
Usually the opposite, at first. Deduplicating people reduces your lead count and lengthens your measured sales cycle. What goes up is attributed revenue and the share of conversions with a known source, because purchases that previously read as Direct get connected back to the click that caused them.
QHow is this different from what Google Analytics already does?
Analytics tools resolve identity mostly within one browser, and report on sessions and users rather than named people. They are not designed to join a purchase in your payment processor to an ad click from two weeks earlier on a different device. That join needs identifiers from outside the browser — the email on the order, the phone on the lead, the customer id on the payment.
QCan identity resolution merge two people by mistake?
Yes, and any vendor who says otherwise is not being careful. Shared IP addresses, family devices and office networks all produce false merges if weak signals are trusted too far. The defences are narrow time windows, caps on how many records one weak signal may merge, and a strict rule that deterministic evidence overrides inferred evidence.
QDoes any of this work without third-party cookies?
Yes — none of it depends on them. Every identifier described here is first-party or supplied by the person: your own cookie set from your own server, an email or phone typed into your own form, a click id that arrived in your own landing page URL, a customer id from your own payment processor. Third-party cookie deprecation does not touch this layer.
QWhat should I check first if my attribution looks wrong?
The Direct bucket. If a large share of conversions report as Direct, the problem is almost never that people are typing your domain — it is that sessions are not being connected. Check whether your visitor cookie is set server-side, whether click ids are captured from the landing URL, and whether email and phone are normalised before storage.

Find the qualified leads your forms are currently throwing away.

Install PartialLeads on one landing page, send traffic, and compare what your CRM captured against what PartialLeads recovered and qualified.