SHA-256 of normalised personal data, per each platform's own rules.
Ad platforms match a server-side conversion to a user by comparing hashes, so
the hash has to be byte-identical to the one they computed from their own
copy of the same fact. That makes normalisation the whole job: " Ali@Ex.COM"
and "ali@ex.com" are the same person and must produce the same digest, and
a leading + on a phone number changes the answer.
The platforms disagree, and it matters
| phone | |
|---|---|
| Meta, Snapchat, Pinterest, Reddit | digits only — +20 100 123 4567 → 201001234567 |
| TikTok | E.164, the + kept — +201001234567 |
This is exactly why the raw value travels to the worker and is hashed at the edge of each client, rather than hashed once at enqueue: one shared digest cannot satisfy both, and hashing early would silently break TikTok matching in a way no error would ever report.
Raw values never leave the box
email, phone, names, city, state, zip, country and external ids are
hashed before egress. ip, user_agent and click ids are match keys the
platforms want in the clear, and pass through untouched.
Summary
Functions
Drop nil and empty values. Platforms reject empty match keys.
SHA-256 hex of an already-normalised value; nil passes through.
Lower-cased, trimmed, SHA-256 hex.
Reddit's email rule, which is nobody else's.
Does this already look like a SHA-256 digest?
Lower-cased, trimmed, non-empty — or nil. Normalisation without hashing.
Hash with fun, unless the value already is a digest.
Digits only, SHA-256 hex. Meta, Snapchat, Pinterest and Reddit.
E.164 with the leading + preserved, SHA-256 hex. TikTok only.
Lower-cased, trimmed, SHA-256 hex. Names, cities, countries, external ids.
Functions
Drop nil and empty values. Platforms reject empty match keys.
SHA-256 hex of an already-normalised value; nil passes through.
Lower-cased, trimmed, SHA-256 hex.
Reddit's email rule, which is nobody else's.
Lower-case, then strip dots from the local part and drop everything
after a +, then hash. Reddit's own published vector:
alice@example.com
Al.ice+Apple@Example.Comboth produce ff8d9819fc0e12bf…. Meta, Pinterest and TikTok do none of this
— they hash the address as written, lower-cased. Using the wrong rule
produces a valid request, a 200, and no matches, which is the failure this
library keeps having to design around.
Does this already look like a SHA-256 digest?
Some hosts store a hashed email and pass it straight through. Hashing a 64-character hex string a second time produces a digest that matches nothing, and nothing anywhere reports an error — the conversions simply stop attributing, which is the most expensive kind of silence in this library.
Lower-cased, trimmed, non-empty — or nil. Normalisation without hashing.
Hash with fun, unless the value already is a digest.
Digits only, SHA-256 hex. Meta, Snapchat, Pinterest and Reddit.
E.164 with the leading + preserved, SHA-256 hex. TikTok only.
Lower-cased, trimmed, SHA-256 hex. Names, cities, countries, external ids.