LW IT Solutions
« Blog Overview /Digital Marketing / Conversions API Hashing: Normalising Customer Parameters Before...
Read this article in other languages:

Conversions API Hashing: Normalising Customer Parameters Before SHA-256 and Which Fields Stay Unhashed

Conversions API Hashing: Normalising Customer Parameters Before SHA-256 and Which Fields Stay Unhashed
Contents
  1. Why a Small Difference Is a Total Difference
  2. The Form Each Parameter Has to Take
  3. The Parameters That Must Not Be Hashed
  4. What This Looks Like From Outside
  5. Where the Normalisation Belongs
  6. Questions and answers
  7. Sources

A Conversions API event that carries a hashed email address looks the same whether the hashing was done right or wrong. The payload validates, the API answers with a success, the event counter goes up, and the match quality figure stays where it was – because the address, correctly hashed into a string that corresponds to nothing, was compared against a customer base and found in none of it.

That is the failure mode worth understanding before any of the plumbing: hashing does not fail loudly, it fails by producing something that is technically flawless and semantically empty.

The same email address hashed twice: once trimmed and lowercased, once as it came from the form, producing two completely different SHA-256 values of which only one matches the stored record
Two real SHA-256 values of the same address. Three characters of whitespace and a few capitals separate them.

Why a Small Difference Is a Total Difference

A hash function is built so that a minimal change to the input produces an unrecognisably different output. That property is what makes hashing useful, and it is exactly what makes a normalisation mistake fatal: there is no such thing as almost matching. Two hashes are identical or they have nothing to do with each other, and no amount of fuzzy matching downstream can recover the difference.

So the entire question of whether an event finds a person is decided before the hash function is ever called, in the few lines that decide what exactly goes into it.

The Form Each Parameter Has to Take

Parameter Required form What goes wrong
em trimmed, lowercase a capital from the form, a trailing space from a paste
ph digits only, with country code the national trunk zero in +49 (0)170 survives
fn, ln lowercase, no punctuation or spaces a hyphen in a double-barrelled surname
ct lowercase, no spaces Frankfurt am Main keeps its spaces
st, country two-letter lowercase code the country is written out
ge exactly f or m anything else, hashed as if it were a value
db YYYYMMDD a locale date format, silently reordered

The phone number deserves a note of its own, because it is the one case that cannot be normalised mechanically. Stripping non-digits from +49 (0)170 1234567 leaves 4901701234567, and the zero after the country code is a national dialling prefix that has no place in an international number. No rule can decide that reliably for every country, which is why the honest handling is to strip, then flag the suspicious pattern for a human rather than guess.

The Parameters That Must Not Be Hashed

The opposite mistake is rarer and just as final. client_ip_address, client_user_agent, fbp and fbc are sent in plain text. They are not personal identifiers in the sense the hashed fields are; they are values Meta matches against its own records, and a hashed one is a string that corresponds to nothing on the other side.

This happens most often when a hashing helper is applied to the whole user-data object at once, which is a reasonable-looking piece of code and wrong in exactly four fields. A helper that hashes by field name rather than by object is the version that survives a schema change.

What This Looks Like From Outside

# the same address, three ways
"  Max.Mustermann@Example.COM "   → 524d6368ceb086b2b1d8fed14ee75313…
"  max.mustermann@example.com "   → de4c5edc5348280eedb5b029d7851764…
"max.mustermann@example.com"      → dd432348e6c3373c5f646913fa94ae79…

# only the third is ever compared against anything

None of these produces an error. The event count in the events manager is identical in all three cases, the delivery is confirmed in all three cases, and the only figure that moves is the match quality – downwards, slowly, in a way that is easy to attribute to seasonality or to a consent change.

Which is why the useful check is not the API response but a comparison against a known value. Take one address from the customer base, hash it the way the shop hashes it, hash it the way the specification prescribes, and compare the two strings. They match or the pipeline has a normalisation bug, and the answer takes a minute.

Where the Normalisation Belongs

The last decision is architectural. Normalisation can happen in the browser, in the server container, or in the backend that has the customer record – and only the last of those has the original value in the form the customer base stores it in.

Doing it in the server-side container is the common middle ground and works, provided the container receives the raw value and not something a form has already mangled. Doing it in the browser is the one arrangement to avoid: it puts the plain address into a request that leaves the visitor’s machine, which is precisely what the hashing was there to prevent.

Questions and answers

Does hashing make customer data anonymous under data protection law?

No. A hash that Meta can match against its own customer base is pseudonymised, not anonymous, and under the GDPR pseudonymised data remain personal data; linking the value to a person is in fact the whole purpose here. Hashing keeps the address from being transmitted in plain text, but it replaces neither the lawful basis nor consent.

What happens if a value is hashed twice on its way to Meta?

The same as with a normalisation mistake: the payload validates, the API reports success, and no person is found. A hash of a hash is again a string of 64 hexadecimal characters, so it looks like a correct one and still corresponds to no record on the other side.

The case arises where two stages each behave sensibly on their own. The shop system delivers the address to the server container already hashed, and a tag there hashes every user-data field once more. Neither stage is wrong in isolation; only the chain is.

The usual safeguard is a check before hashing: if a value already consists of exactly 64 characters from 0–9 and a–f, it is passed through instead of being hashed again. An email address never matches this pattern because it contains an @, and neither does a phone number, because E.164 allows at most 15 digits. The comparison against a known value that the article describes catches the rest, provided it is applied at the end of the chain, to the payload that actually goes to Meta, and not to an intermediate stage.

How should the postcode and external_id, which the table leaves out, be handled?

The postcode (zp) is hashed and, before that, written in lower case without spaces or hyphens: the Polish postcode 00-950 becomes 00950, the Dutch 1012 AB becomes 1012ab. For the United States, Meta asks for the first five digits only.

external_id is the shop’s own identifier, such as the customer number. Meta recommends hashing it but does not require it. What matters is consistency: if the identifier is also sent through the pixel, it has to arrive in the same form on both paths, hashed or not, because otherwise Meta sees two different identifiers.

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Get in Touch

Briefly describe your project or inquiry for a tailored response. This site is protected by reCAPTCHA.

Write a comment

Practical experience and questions about the implementation are welcome here.

The email address is not published. Required fields are marked with an asterisk.

Articles & categories

CCTV

Follow this category by RSS

Cloud & AI

All 11 articles in this category Follow this category by RSS

Data Privacy

All 18 articles in this category Follow this category by RSS

Digital Analytics

All 58 articles in this category Follow this category by RSS

Digital Marketing

All 37 articles in this category Follow this category by RSS

IT & Networks

All 17 articles in this category Follow this category by RSS

Music Production

All 16 articles in this category Follow this category by RSS

Raspberry PI

Follow this category by RSS

SaaS & Internet Earning

Articles coming soon

Smart Home

All 18 articles in this category Follow this category by RSS

Web Development

All 11 articles in this category Follow this category by RSS

WordPress Plugins & Tricks

All 13 articles in this category Follow this category by RSS