Blog

Hash emails for Meta CAPI without losing match rate

SHA-256 is the easy part. The normalisation that has to happen first is where match rates are made or broken.

Meta accepts SHA-256 hashed email addresses as a matching key for the Conversions API. The hashing function is one line of code. The normalisation step that has to come before the hash is twelve lines of code, and it is what determines whether your match rate sits at 40 percent or 75 percent.

The exact normalisation Meta expects

  1. Trim leading and trailing whitespace.
  2. Convert the entire string to lowercase.
  3. Do not strip dots before the @ sign. Meta does not normalise Gmail dots.
  4. Do not strip plus aliases. jane+promo@gmail.com stays as written.
  5. SHA-256 the resulting string. Output as a lowercase hex digest.

The Custom Template version

const sha256 = require('sha256');
const email = data.email.trim().toLowerCase();
return sha256(email, {outputEncoding: 'hex'});

That is the entire transformation. If your client-side code is sending the email already lowercased, the trim and lowercase steps are defensive. Keep them anyway. Half the time the form on the page does not lowercase, and that half is where the match rate suffers.

Common mistakes that drop match rate

  • Sending the plaintext email and the hashed email in the same request. Meta accepts the first valid value it sees, and "valid" is sometimes the plaintext one.
  • Hashing twice. If your client GTM is already hashing before sending to your tagging server, do not hash again.
  • Using SHA-1 or MD5 because someone copied the snippet from an old StackOverflow answer. Meta rejects these silently.
  • Including a trailing newline. Some form libraries append one. The hash will be valid but useless for matching.

For phone numbers, the E.164 normalisation rules are similar but more involved. Get email right first; the lift is bigger and the work is smaller.