Skip to Content
DestinationsMatch Keys & Identifier Hashing

Match Keys & Identifier Hashing

Advertising and audience destinations do not match your customers on raw email addresses or phone numbers — they match on cryptographic digests of those identifiers. This page defines exactly how Zeotap normalizes, hashes, validates, and delivers match keys, and the contract you must follow if you hash identifiers yourself before they reach Zeotap.

Three routes to a hashed match key: raw columns hashed at delivery, pre-hashed columns validated and passed through, and hashing inside the sync — all converging on the destination as lowercase SHA-256 hex

Why Hashing

Two independent reasons force hashing, and both work in your favor:

  • Privacy. Hashing means raw PII never leaves your data pipeline in cleartext. The destination receives a one-way digest that it can match against its own hashed user base but cannot reverse into an email address or phone number.
  • Vendor requirement. Every major ad platform (Google Customer Match, Meta Custom Audiences, TikTok, LinkedIn, The Trade Desk, and the rest) requires identifiers to be hashed — most reject or silently discard unhashed values.

Matching only works when both sides hash the same bytes with the same algorithm. SHA-256("Alice@Test.com") and SHA-256("alice@test.com") are completely different digests, and the platform can only match one of them. That is why the normalization contract below matters more than the hashing itself.

The Normalization Contract

Zeotap applies these exact rules to every raw identifier before hashing. If you pre-hash values yourself, you must apply the same rules first — see Pre-hashing yourself.

IdentifierNormalizationThenExample
EmailTrim whitespace, lowercaseSHA-256, delivered as lowercase hex" Alice@Test.com "alice@test.comc160f8cc…
PhoneE.164: strip everything except digits, keep or add a leading +SHA-256, delivered as lowercase hex"(415) 555-1234"+4155551234; "+1 415-555-1234"+14155551234
Text PII (first/last name, city, state, zip, country — where a destination accepts them)Trim whitespace, lowercaseSHA-256, delivered as lowercase hex" Alice "alice93f8f291…

Two details of the phone rule deserve emphasis:

  • Only a leading + survives; every other non-digit character (spaces, dashes, parentheses, dots) is removed.
  • If the number does not start with + after stripping, one is prepended. This assumes your stored numbers already carry a country code. 4155551234 becomes +4155551234 — which is not the same person as +14155551234. Store phone numbers in full E.164 form (with country code) for reliable matching.

Three Ways to Deliver Hashed Values

You can get a correctly hashed match key to a destination through any of three routes. They can be freely combined — even on the same sync.

1. Map the raw column — Zeotap hashes at delivery

Map your plaintext email or phone column to the destination’s email / phone field. During delivery, Zeotap normalizes the value per the contract above and hashes it with SHA-256. For hash-matched destinations the raw value is never sent.

This is the recommended route: normalization and hashing are guaranteed to be correct, and you keep readable data in your warehouse.

2. Map a pre-hashed column — Zeotap validates and passes it through

If your warehouse only holds digests (a common compliance posture), map the digest column to the destination’s pre-hashed field:

Field keyMeaning
hashed_emailSHA-256 hex digest of the lowercase, trimmed email
hashed_phoneSHA-256 hex digest of the E.164 phone number (digits with leading +)

These canonical keys are available uniformly on every audience destination that matches on email or phone. Values are accepted in either case (uppercase or lowercase hex) and always delivered lowercase. Some destinations additionally accept other algorithms through explicit keys — see Per-Algorithm Keys below.

3. Hash inside the sync — transform or hash-on-sync

Two features hash values in your warehouse as part of the sync itself:

Either way, the identifier field receives a digest instead of a raw value. The delivery pipeline’s idempotence guard (below) recognizes the digest and passes it through unchanged — it is never hashed a second time.

Note: the two features normalize differently. The Hash PII checkbox applies the normalization contract for the column’s PII type before hashing (emails are lowercased and trimmed, phones formatted to E.164). The plain SHA-256 transform hashes the value exactly as it arrives — if your column can contain "Alice@Test.com", normalize first (in your model SQL) so the digest matches what the destination expects. For identifier columns, prefer the Hash PII checkbox or simply the raw-field route.

Precedence: Pre-Hashed Wins

When both the raw field and its pre-hashed counterpart are mapped (for example email and hashed_email), resolution for each row is:

  1. hashed_<field> wins. A valid SHA-256 hex digest on the pre-hashed field is lowercased and used as-is.
  2. Malformed pre-hashed values fall back to raw. If the pre-hashed value is not a 64-character hex digest, it is discarded (never uploaded — see below) and the raw column is used instead.
  3. The raw field is normalized and hashed — unless the value is itself already a SHA-256 hex digest, in which case it passes through (the idempotence guard).

Mapping both fields is a useful pattern when your digest column has gaps: rows with a valid digest use it, rows without one fall back to hashing the raw value.

Validation & Failure Semantics

A malformed digest is worse than a missing one: the destination accepts it, matches nobody, and your match rate silently degrades. Zeotap therefore never uploads a malformed pre-hashed value:

  • A value on hashed_email / hashed_phone that is not exactly 64 hexadecimal characters is dropped — it is neither uploaded nor re-hashed.
  • Resolution falls back to the raw column for that row, if one is mapped. If not, the row contributes no value for that identifier.
  • Every drop is counted per run. When any anomalies occur, the run log for the sync contains an identifier extraction anomalies warning with counts of invalid pre-hashed emails and phones, so match-rate loss is visible instead of silent. See Sync Runs for where to find run logs.

The same rule applies to the per-algorithm keys: a hashed_email_md5 value that is not 32-hex, a hashed_email_sha1 that is not 40-hex, or a hashed_email_sha512 that is not 128-hex is dropped and counted, never uploaded.

The idempotence guard

A value arriving on a raw email or phone field that is already a 64-character hex digest is passed through (lowercased) instead of being hashed again. This prevents double-hashing when:

  • hash-on-sync or a SHA-256 transform already hashed the value upstream, or
  • you mapped a pre-hashed column to the raw field.

The guard cannot misfire on genuine data: a real email address always contains @, and an E.164 phone number is at most 16 characters — neither can ever look like 64 hex characters. Guard pass-throughs are also counted in the run-log anomaly summary (as pre-hashed values seen on a raw field), so you can spot mappings that should be moved to the explicit hashed_* field.

Per-Algorithm Keys

hashed_email / hashed_phone are always SHA-256. A small number of destinations additionally accept other digest algorithms, exposed as explicit per-algorithm keys only where the vendor actually matches on them:

Field keyDigestAccepted byNotes
hashed_email_md5MD5 hex (32 chars)Pinterest Ads, Criteo, LiveIntentPinterest and Criteo also accept SHA-256 — prefer hashed_email when you have both.
hashed_email_sha1SHA-1 hex (40 chars)StackAdapt, LiveIntentSHA-1 is the only pre-hashed form StackAdapt matches — a SHA-256 digest will not match there.
hashed_email_sha512SHA-512 hex (128 chars)LinkedIn Ads (audiences)LinkedIn accepts SHA-256 (hashed_email) or SHA-512.

All per-algorithm digests are of the same normalized input (lowercase, trimmed email) — only the algorithm differs.

LiveIntent legacy behavior: LiveIntent’s hashed_email field historically accepted MD5- and SHA-1-length values as well. This still works, but it is deprecated — map MD5 digests to hashed_email_md5 and SHA-1 digests to hashed_email_sha1 so the algorithm is explicit and validation is exact.

The Trade Desk: Base64 Encoding

The Trade Desk’s API requires hashed identifiers as base64-encoded digest bytes, not hex. You do not need to handle this: map SHA-256 hex digests to hashed_email / hashed_phone exactly as for every other destination, and Zeotap transcodes hex to base64 internally at upload time. Never map base64 values — they fail the 64-hex validation and are dropped.

Pre-Hashing Yourself: The Contract You Must Follow

Warning: a digest of a wrongly-normalized value is undetectable. Zeotap validates the shape of a pre-hashed value (64 hex characters), but a well-formed SHA-256 digest of "Alice@Test.com" is indistinguishable from one of "alice@test.com". It will pass validation, upload cleanly — and match nothing. The only symptom is a low match rate at the destination.

If you populate hashed_email, hashed_phone, or the per-algorithm keys yourself, your pipeline must hash exactly this:

  • Email: lower(trim(email)), then hash, hex-encode. Example (SQL): SHA2(LOWER(TRIM(email)), 256).
  • Phone: the full E.164 string — digits only plus the leading +, country code included (e.g. +14155551234), then hash, hex-encode. Do not hash formatted numbers ((415) 555-1234) or numbers without a country code.
  • Hex encoding, not base64 — including for The Trade Desk (see above). Uppercase hex is accepted and lowercased on delivery.
  • No salt, no pepper, no truncation. Ad platforms match plain unsalted digests.

If you cannot guarantee this contract, map the raw column instead (route 1) and let Zeotap normalize and hash — it is contract-correct by construction.

Event (CAPI) Destinations

Server-side event destinations that carry user identifiers — Meta Conversions API, Google Enhanced Conversions, TikTok Events API, Snapchat CAPI, Pinterest CAPI, LinkedIn CAPI — follow the same rules: raw email / phone values are normalized and SHA-256 hashed, pre-hashed hashed_* values are validated and passed through, and malformed digests are dropped and counted, never sent. See each destination’s page for its full user-data field list.

Next Steps

Last updated on