Every attribution vendor eventually meets the same temptation: the easiest way to connect a visit to a purchase is to know who the visitor is. Once you know that, everything becomes simple. Join on the person. Follow them across devices, across sites, across time. Attribution stops being an inference problem and becomes a lookup.
That path is well-trodden and it ends somewhere specific. It ends with a company that started out measuring marketing and now maintains a profile of a person who never agreed to it. The tooling that gets you there is the same either way — the difference is entirely in what you choose to store, and in whether you drew the line before or after you needed it.
This is where that line goes, and why it holds.
The join you cannot avoid
Start with what is genuinely necessary, because it is easy to over-correct into a system that cannot do its job.
An AI-referred visitor arrives anonymously. They have no account, they have not logged in, and the only thing you know about them is an opaque identifier in a first-party cookie. They browse, they leave, and — days or weeks later — they come back, sign in, and buy something.
The discovery touch belongs to the anonymous identifier. The purchase belongs to an account. If those two are never connected, the AI touch is orphaned from the revenue it produced, and the one thing this entire category exists to demonstrate cannot be demonstrated.
So a join is required. The question is not whether to connect the anonymous identity to the authenticated one. It is what you store in order to do it, and what that stored thing then enables.
The question is never whether to make the join. It is what the stored join makes possible afterwards.
Where attribution ends and profiling begins
The boundary is not the join itself. It is four properties of how the join is stored.
The same mechanism, two very different systems
| Property | Attribution join | Identity graph |
|---|---|---|
| Scope | One site. The mapping means nothing elsewhere | Cross-site. That is the entire point |
| Content | Two opaque identifiers, nothing else | Attributes: email, device, location, behaviour |
| Enrichment | None. Nothing is appended over time | Continuous. The profile accretes |
| Purpose | Join sessions to conversions | Targeting, segmentation, resale |
| Deletion | Trivial — one row per visitor per site | Hard — the graph is the value |
Read down the last column. Every property that makes an identity graph valuable is a property that makes it dangerous, and none of them is required for attribution.
The four constraints follow directly:
Per-site scope. The mapping is meaningful only within one customer's site. The same person visiting two different sites produces two unrelated mappings with no key connecting them. This is the constraint that does the most work — it makes cross-site identity resolution not merely prohibited but structurally impossible, which is a much stronger guarantee than a policy.
Hashes only. What is stored is an opaque identifier derived from the application's account identifier. Not an email address, not a hashed email address that could be matched against another dataset, not anything that means something outside the system.
No profile attributes. The record holds the mapping and nothing else. No name, no location, no device list, no behavioural summary. There is nowhere for a profile to accumulate because no field exists to hold one.
Attribution-only use. The mapping is read for exactly one purpose: joining sessions to conversions. Not for segmentation, not for lookalike audiences, not for enrichment, and never sold.
Write the constraint down before you need it
The pressure to relax these arrives later, from a real customer with a reasonable-sounding request, at a moment when revenue depends on saying yes. A constraint documented in advance — with the reasoning, not just the rule — is the only thing that survives that conversation. A constraint held informally does not.
IP addresses: the one to get right first
IP addresses are the most consequential piece of data in an ingestion pipeline, and the most casually handled.
An IP address is personal data under GDPR. It is also genuinely useful — for geography, for rate limiting, for bot detection. The temptation to keep it is constant and the reasons are always good.
The correct handling is to hash it at the moment of ingestion, with a per-site salt, and never write the raw value anywhere. Not to the analytics store, not to a log line, not to a debug trace, not to disk at any point.
The details matter more than they look:
Hash at the edge, not downstream. If the raw IP reaches your analytics store before being hashed, it has already been persisted, and a hash applied afterwards protects nothing. The transformation belongs at the earliest point the request is handled.
Salt per site, not globally. A single global salt means the same visitor produces the same hash across every customer's site — which quietly reconstructs a cross-site identifier out of data you were careful not to store. Per-site salting closes that.
Watch the logs. This is where raw IPs actually leak. Not from the data pipeline, which somebody designed carefully, but from an error log that dumps a request object, an access log that was on by default, or a third-party error reporter capturing request context. The pipeline gets reviewed; the logs do not.
Where the raw IP is allowed to exist
The window is deliberately narrow: from arrival to hashing, in memory, and nowhere else.
Request arrives
The raw address exists in memory at the edge. This is the only place it should ever exist.
Hash with per-site salt
A one-way transform, salted per site so hashes cannot be correlated across customers.
Discard the original
The raw value is never written to storage, logs, traces or error reports.
Downstream sees only the hash
Every subsequent stage works with the hashed value. Nothing downstream can recover the original.
Step three is marked as the failure point because it is the one that actually breaks in production. Everyone implements the hash. The raw value escapes through an observability path nobody was thinking about.
What you give up, and what you don't
Being honest about the trade-off, because there is one.
You give up cross-site measurement. You cannot tell a customer that a visitor who saw them on one site later converted on another. Some vendors sell exactly that. You cannot, and you should say so plainly rather than implying a capability you have deliberately declined.
You give up deterministic cross-device tracking. If someone discovers you on their phone and buys on their laptop without logging in on both, that connection is lost. You can model it in aggregate; you cannot follow the individual.
You give up enrichment. No appending firmographics, no inferring seniority, no third-party data joins.
What you keep is more than it sounds:
Attribution still works. The anonymous-to-authenticated join within a site covers the great majority of cases, because the great majority of conversions involve authentication on a device the person already used to browse.
Deletion is genuinely trivial. A GDPR erasure request means deleting rows keyed by visitor, within one site. There is no graph to untangle and no downstream system that absorbed a copy. This is not merely compliance theatre — a system where deletion is easy is one where deletion actually happens.
Data residency is achievable. Per-site scope means per-site placement. Keeping EU data in the EU is a routing decision rather than an architectural rewrite.
The breach surface is small. The worst case for a leaked mapping table is a set of opaque identifiers that mean nothing outside the system that generated them. Compare that with the worst case for an identity graph.
The failure mode is gradual
Nobody decides to build an identity graph. What happens is a sequence of individually reasonable additions: store the email hash to make debugging easier, keep the device type for a segmentation feature, retain location because a customer asked for a report. Each is defensible. The eleventh one arrives and you are running a profile business. This is why the constraint has to be on the schema and the stored fields, not on intentions.
Privacy as an engineering property
The framing that makes this stick is not compliance. Compliance is a floor, it is jurisdictional, and building to it produces systems that are technically defensible and still creepy.
The better framing is that retained data is a liability with a carrying cost. Every field you keep is something to secure, something to delete on request, something to explain in a DPA, something to migrate, something that can leak, and something that constrains where the system can run.
Under that framing the four constraints stop being ethical restraint and start being ordinary engineering discipline. Smaller schema, fewer joins, simpler deletion, easier residency, smaller blast radius. The privacy-respecting architecture is also the cheaper one to operate — not always, but here, and it is worth noticing when the incentives happen to align.
Where they do not align, the constraint has to hold anyway. That is what makes it a constraint rather than a preference.
Frequently asked
Yes, within one site. Attribution requires joining an anonymous pre-login session to a post-login conversion, which needs a mapping between the two identifiers. That mapping can be scoped to a single site, contain only opaque identifiers with no profile attributes, and be used exclusively for attribution. What it cannot then do is follow a person across sites — which is the capability that turns attribution into profiling.
Four things: scope, content, enrichment and purpose. An attribution join is per-site, holds only opaque identifiers, never accumulates attributes, and is read only to connect sessions to conversions. An identity graph is cross-site by design, holds identifying attributes, grows over time, and is used for targeting or resale. The mechanism is similar; the stored data and what it enables are not.
Hash them at the moment of ingestion using a per-site salt, and never write the raw value to storage, logs, traces or error reporting. Hashing downstream is too late — the raw value has already been persisted. Salting per site rather than globally prevents the same visitor producing identical hashes across different customers, which would rebuild a cross-site identifier out of data you deliberately avoided storing.
Cross-site measurement, deterministic cross-device tracking for users who do not authenticate on both devices, and third-party data enrichment. What you keep is attribution that works for the large majority of conversions, genuinely simple GDPR deletion, achievable data residency, and a much smaller breach surface.
Compliance is the floor, and building to the floor produces systems that are legally defensible and still uncomfortable to explain. The stronger framing is that retained data is a liability with a carrying cost — everything stored must be secured, deleted on request, documented, migrated, and can leak. Under that framing the constraints are ordinary engineering discipline that happens to also satisfy the regulation.
Sources & further reading
- 01Regulation (EU) 2016/679 (GDPR), Article 4 — Definitions — EUR-Lex
- 02What is personal data? — Information Commissioner's Office (ICO)
- 03Judgment in Case C-582/14 (Breyer) — dynamic IP addresses as personal data — Court of Justice of the European Union