Skip to main content
Every member must turn an email into exactly the same hash, or the registry cannot match a user across platforms. This page is the specification. Implement it once, test it against the vectors below, and use the same function for checks and for reports.

The rules

Apply these steps in order:
  1. Remove whitespace from the start and end. Apply Unicode NFKC normalisation. Convert to lowercase.
  2. If whitespace remains inside the value, it is not an email.
  3. Split the value at the last @ into a local part and a domain. If either part is empty, the value is not an email.
  4. If the domain ends with one ., remove it. If the domain is then empty, the value is not an email.
  5. If the domain is googlemail.com, change it to gmail.com.
  6. If the domain is one of the providers below, remove everything from the first + in the local part: gmail.com, outlook.com, hotmail.com, live.com, msn.com, icloud.com, me.com, mac.com, proton.me, protonmail.com, pm.me, fastmail.com, fastmail.fm
  7. If the domain is gmail.com, remove every . from the local part.
  8. If the local part is now empty, the value is not an email.
  9. Join the local part, @ and the domain. Hash the result with SHA-256, and encode the digest as lowercase hexadecimal (64 characters).
Step 1 lowercases the whole address, including the local part. Almost no mail system treats the local part as case-sensitive, and if the hash kept case, a user could avoid a report by changing the case of one letter. If a value is not an email after these steps, do not check it and do not report it.

Why these rules and no others

The rules only join spellings that a mail provider delivers to the same inbox. Gmail ignores dots, and the listed providers deliver name+anything@ to name@. Other providers and company domains can treat + and . as part of the address, so the rules leave them alone. Joining two different inboxes would make two different people match, and a false match can refuse an innocent person a service.

Reference implementation

This JavaScript (Node.js 18 or later) implementation matches the specification and passes every test vector on this page.
Use the same function for every call:

Test vectors

Run your implementation against every row before you send a request. The SHA-256 column is the exact value to send. In the input column, ␠ marks a space character.

Common mistakes

Hash the normalised value. Jane.Doe@Example.com and jane.doe@example.com must produce the same digest.
Send lowercase hexadecimal. The API rejects any other format with 400 INVALID_REQUEST.
Hash the normalised email once. Do not hash the hexadecimal digest again before you send it.
Remove dots only for gmail.com, and remove +tags only for the listed providers. first.last+news@company.com stays exactly as it is.
Split at the last @. A quoted local part can contain @.
Use one function for both. If your check and your report hash differently, your own reports never match your own checks, and nobody else’s do either.

Versioning

The rules on this page are version 1. Every check response includes normalisation_version, so you can confirm which rules the registry expects. If the rules change, the version number changes, and members receive notice before the change.