Meta CAPI Hashing: Normalize User Data Before It Costs You Matches
Why Meta CAPI Hashing Depends on Normalization
Meta CAPI hashing exists so you can prove a conversion belongs to a person without sending raw personal data: you normalize the identifier, hash it with SHA-256, and Meta compares the digest against its own hashed records. The comparison is exact - the SHA-256 of [email protected] and the SHA-256 of [email protected] are two unrelated strings. If your normalization differs from Meta's by a single space or a capital letter, the hashes never match, no error is returned, and the event loses its strongest matching signal.
This is why normalization rules are documented per field with exact examples on Meta's Customer Information Parameters page. The failure mode is invisible in error logs: events deliver, but Event Match Quality drops and optimization degrades. The score itself is covered in Event Match Quality; this guide collects the field-level rules and the platform differences that trip up multi-network setups.
The Official Meta CAPI Normalization Rules
Meta documents a normalization recipe for every hashed field. The ones that matter in daily delivery:
| Field | Normalization before SHA-256 | Official example |
|---|---|---|
em email | Trim spaces, lowercase. Punctuation inside the address stays. | [email protected] → [email protected] |
ph phone | Remove symbols, letters, and leading zeros; keep the country code | (650)555-1212 → 16505551212 |
fn / ln names | Lowercase, no punctuation; Roman a-z recommended; non-Latin must be UTF-8, accents preserved | Mary → mary; Valéry → valéry |
ct city | Lowercase, no punctuation, no special characters, no spaces | New York → newyork |
st state | Two-character ANSI abbreviation, lowercase; outside the US - lowercase, no punctuation or spaces | az, ca |
zp zip | Lowercase, no spaces or dashes; US - first 5 digits; UK - area/district/sector format | 94035; m11ae |
country | Two-letter ISO 3166-1 alpha-2, lowercase | United States → us |
Three details from the official examples deserve emphasis. The email rule lowercases but does not strip underscores or dots - [email protected] stays intact. The phone rule always requires the country code even for single-country data, and it strips leading zeros, which matters for international traffic. And names keep accented characters: Valéry normalizes to valéry, not valery - transliterating it breaks the hash.
After normalization, every value is hashed with SHA-256 and sent as a 64-character lowercase hex string - or handed to a tool that does it. Meta's Business SDKs perform the hashing automatically, which removes the entire class of mistakes described above at the cost of running the SDK.
Where Platforms Differ: Meta vs TikTok vs OpenAI
Teams running several networks from one tracker copy normalization rules between platforms, and that is where matching breaks. The SHA-256 step is universal, but the field lists and geo handling are not:
| Rule | Meta | OpenAI | TikTok |
|---|---|---|---|
| Trim, lowercase | Trim, lowercase | Hashing required for matching keys | |
| Phone | Country code required, strip symbols and leading zeros | Same pattern, 8-15 digits | Hashing required |
| Geo (city/state/zip/country) | Hashed, strict lowercase/abbreviation formats | Raw strings, lowercased server-side, not hashed | Per matching-key docs |
| Names | Lowercase, no punctuation, UTF-8 accents preserved | Lowercase, ASCII punctuation removed, non-ASCII preserved | Not a TikTok matching key - TikTok matches on email, phone, and external_id only |
The geo row is the trap: Meta hashes city, state, zip, and country with strict format rules, while the OpenAI Conversions API explicitly expects raw geographic strings and hashes only identity fields. Piping Meta-formatted, hashed geo values into OpenAI wastes the field; piping raw geo into Meta's hashed fields breaks the match. TikTok's advanced matching expects hashed keys - email, phone, external_id - and powers TikTok enhanced matching, so the same discipline applies with its own field list. One field belongs in every tracker setup: external_id - your tracker's click or subscriber ID. Meta accepts it as a raw value with no hashing, but the same value must reach both the pixel and the server event; hashing it on one side only breaks the match. Hash once, exactly where the field is defined: a double-hashed value (normalized, hashed, then hashed again by a second tool or SDK layer) can never match, and it is the most common self-inflicted wound when teams stack a tag manager on top of a custom pipeline.
Verifying Normalization Without an Error Log
Because badly hashed values deliver without complaints, verification happens in measurement tools, not in error handling. The Meta Test Events tool shows field-level diagnostics for server events, including which user-data fields arrived and passed. Events Manager's overview then shows raw versus matched counts - a large raw-to-matched gap with no errors is the signature of a normalization problem.
The fix loop is mechanical: normalize per the table, hash, resend one test event, and confirm the field shows as valid. Teams that route tracker postbacks through MOST get the normalization and hashing applied per network automatically; the free manual path for a single landing is Pixel Activator. The deduplication side of the same payload - keeping event_id stable across retries - is covered in event ID deduplication, and the full delivery context lives in the Meta Conversions API complete guide.
Frequently asked questions
fbc and fbp cookie construction. Limited Data Use processing.
