LW IT Solutions logo LW IT Solutions logo 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

Management Summary

Meta uses customer details to help connect reported purchases with accounts for advertising measurement. Hashing replaces certain details, such as contact information, with calculated fingerprints before transmission. Consistent preparation is essential: different formatting of the same information can produce different fingerprints and prevent a match. This is why each type of information follows specific rules. The process changes the representation of personal data, but does not automatically make that data anonymous.

Contents
  1. Why the exact input matters
  2. Field rules instead of one function for everything
  3. Values that remain unhashed
  4. A reproducible test without real customer data
  5. Browser or server: the data flow matters
  6. Questions and answers
  7. Sources

SHA-256 calculates a fixed-length value from the input data. This value is called a hash. A successful Conversions API response does not establish that an email address was matched correctly. A plausible SHA-256 value can originate from incorrectly prepared data. Other identifiers may still support matching. API acceptance, data quality and actual matching therefore require separate checks.

Normalisation means converting an input into the form required for a particular field before hashing. For an email address, the sequence is removal of surrounding whitespace, conversion to lowercase and then SHA-256. Changing the resulting hash cannot repair an input error.

Two calculated SHA-256 values and comparison with a fictional expected value. This is not a Meta matching result.
Two spellings, two hashes. Local comparison with an expected value does not prove API acceptance or attribution.

Why the exact input matters

SHA-256 processes bytes. Spaces and letter case change the result. Three spellings of the same fictional address produce three different hashes in the example. Comparison with the expected hash requires the normalised spelling. This does not mean that Meta never compares other values, or that the entire event necessarily remains unmatched.

Field rules instead of one function for everything

The following overview is an integration guide. The current Meta field specification and the installed SDK version remain decisive. An SDK is a software library supplied to help applications use the API. It may already normalise and hash values, so preceding transformations must be compatible.

FieldPreparation and check
emRemoval of surrounding whitespace and lowercase conversion; no invented corrections to the address.
phInternational number with country calling code, containing only digits for hashing. Country-specific prefixes require correct interpretation first.
fn, lnLowercase and surrounding whitespace require attention. The inspected Python/Node.js helpers do not universally remove internal spaces and punctuation from these fields.
ctLowercase city name without spaces or punctuation according to the field specification; SDK behaviour needs representative tests.
country, stCountry as a two-letter ISO code such as de. Region handling is separate: a US state abbreviation is not a worldwide region rule.
ge, dbWhere available and permitted: f/m or a valid date in YYYYMMDD. Missing values must not be guessed.

Phone numbers can be processed automatically with country context and a maintained library such as libphonenumber. Removing every non-digit is insufficient: the German example +49 (0)170 1234567 would retain an unnecessary zero. However, deleting a zero after every country calling code is also wrong; a leading zero can belong to an Italian international number. Ambiguous input requires clarification. Format validation does not prove current reachability.

Values that remain unhashed

client_ip_address, client_user_agent, fbp and fbc are transmitted unhashed according to the respective specification. That does not make these values harmless or unrelated to personal data. The IP address must correspond to the event; a proxy address does not replace the browser address. Cookie and click identifiers must not be arbitrarily invented.

These are important examples, not a complete list of API fields. An explicit field mapping determines validation, normalisation, hashing or unchanged transmission. New fields need a deliberate decision rather than a blanket hashing loop over the entire object.

A reproducible test without real customer data

This Python example calculates complete hashes for a fictional address. The first two outputs start with 524d6368 and de4c5edc; the normalised version starts with dd432348.

Python
import hashlib

samples = [
    "  Max.Mustermann@Example.COM ",
    "  max.mustermann@example.com ",
    "max.mustermann@example.com",
]
for value in samples:
    print(repr(value), hashlib.sha256(value.encode("utf-8")).hexdigest())

expected = "dd432348e6c3373c5f646913fa94ae7947bc0086b42028aedec4371e3e3c2cdb"
normalised = samples[0].strip().lower()
assert hashlib.sha256(normalised.encode("utf-8")).hexdigest() == expected

The test demonstrates local calculation, not matching by Meta. Integration testing requires the same fictional test value to pass through every processing step, including the final data sent to Meta. Empty values, international phone numbers, postcodes and already hashed inputs also need coverage. Unprotected logs are unsuitable for raw customer data or real identifiers.

API errors and warnings remain relevant. A mismatching hash may pass unnoticed, but an SDK can reject malformed input before transmission. Event Match Quality is a diagnostic indicator, not proof that a specific event was linked to a specific person. A single hashing error does not imply a predictable change in that indicator or the event count.

Browser or server: the data flow matters

Normalisation and hashing can run in a browser or on a server. Meta also provides a client-side parameter builder. Browser hashing does not force transmission of the raw address: actual requests and other scripts determine whether raw data leave the device. A backend can also receive raw data, with corresponding protection requirements.

The integration needs a clearly assigned processing stage, a documented input state and inspection of outgoing data. A secret CAPI access token must not appear in publicly delivered browser code. Hashing replaces neither transport encryption nor a lawful processing arrangement.

Questions and answers

Are hashed customer data automatically anonymous?

No. In the matching process described here, association with a person remains intended and possible using additional data. Such a hash does not automatically anonymise the data. A lawful basis, transparency and any required consent need separate consideration.

How can double hashing be prevented?

Hashing a hash produces a different result from the expected single hash. The interface between processing stages therefore needs an explicit contract: raw value or already normalised SHA-256 value. A pattern of 64 hexadecimal characters proves neither provenance nor correct normalisation. An internal identifier can happen to have the same format. SDK detection rules need inspection and cannot establish provenance.

What about postcodes and external_id?

zp needs country-appropriate preparation. The Python SDK documentation describes five digits for the US and national standards elsewhere. At the same time, the inspected normaliser splits at the first hyphen: 00-950 would become 00. This SDK effect is not a general rule for Polish postcodes. The integration needs an explicit test case checked against the current field specification.

external_id is an internal identifier. The inspected Python UserData path passes this list without additional hashing; other helpers may behave differently. The current schema remains decisive. Documented rules need to specify where hashing occurs and whether each input is raw or already hashed. Pixel and CAPI need the same final representation. Silently treating the field like an email address is unsuitable.

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

A question or an idea for a web project? Tell me a little about it. This form uses reCAPTCHA to protect against spam.

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.