Hashing Customer Data

Normalization and hashing guidelines for customer email and phone numbers

NB Orders API Guide: Normalization for Hashed Email & Phone

This document specifies normalization rules for emails and phone numbers prior to hashing for the hashed_customer_email and hashed_customer_phone_number fields in the NB Orders API. These rules are identical to the normalization used by integrated advertising platforms so that hashes generated by customers match 1:1 with platform-provided hashes.

Important:

• If you are sending raw emails/phone you should not send these hashed fields

• The emails and phone numbers should be first normalized and then hashed. The hashed versions should be sent via the NB API.

Hashing Output Format

  • Algorithm: SHA-256
  • Input: Normalized email or phone number
  • Output: Lowercase hexadecimal (64 characters), no prefix
    • Email: Hash the normalized email string
    • Phone: Hash the E.164 string returned by normalization (include the leading +)

Email Normalization

Purpose

Normalize email addresses to a consistent format so that variations of the same email are treated as equivalent. This is useful for deduplication, simplified email handling and creating consistent SHA-256 hashes from emails.

General Rules

  1. Lowercasing: Convert the entire email address to lowercase.
  2. Trimming: Remove any spaces from the beginning or end of the email address.
  3. Validation:
    • The email must contain exactly one @ symbol.
    • If not, use the trimmed and lowercased email as-is.

Domain-Specific Normalization Rules

Apply the following rules to the local part (the portion before the @) based on the email's domain:

Gmail (gmail.com)

  • Remove everything after and including the first + character.
  • Remove all periods (.).

Yahoo / Ymail (yahoo.com, ymail.com)

  • Remove everything after and including the first - character.
  • Remove all periods (.).

Outlook (outlook.com)

  • Remove everything after and including the first + character.

ProtonMail (protonmail.com, protonmail.ch, pm.me)

  • Remove everything after and including the first + character.

Apple Domains (icloud.com, me.com, mac.com)

  • Remove everything after and including the first + character.

Final Step

Recombine the cleaned local part with the domain part using @. Use the resulting normalized email address.

Examples

Reference Implementation (JavaScript)

function emailNormalize(email) {
  if (!email) {
    return null;
  }

  // Utility functions
  const stripPeriods = (value) => value.replace(/\./g, '');
  const plusAddressing = (value) => value.split('+')[0];
  const dashAddressing = (value) => value.split('-')[0];

  // Normalize format
  const normalized = email.trim().toLowerCase();
  const parts = normalized.split('@');

  // Invalid email format, return as-is
  if (parts.length !== 2) {
    return normalized;
  }

  let [localPart, domainPart] = parts;
  let localPartRules = [];

  // Domain-specific rules
  switch (domainPart) {
    case 'gmail.com':
      localPartRules = [plusAddressing, stripPeriods];
      break;
    case 'yahoo.com':
    case 'ymail.com':
      localPartRules = [dashAddressing, stripPeriods];
      break;
    case 'outlook.com':
    case 'protonmail.com':
    case 'protonmail.ch':
    case 'pm.me':
    case 'icloud.com':
    case 'me.com':
    case 'mac.com':
      localPartRules = [plusAddressing];
      break;
  }

  // Apply all relevant rules
  for (const rule of localPartRules) {
    localPart = rule(localPart);
  }
  return `${localPart}@${domainPart}`;
}

Phone Normalization

Purpose

Format a phone number into a consistent style using the libphonenumber-js library. This library:

  • Removes any non-digit characters (like spaces, dashes, parentheses).
  • Makes sure it starts with a + country code. If there's no country code, it adds +1 (for the US/Canada).
  • Returns null if the phone number is not valid.

Reference Implementation (JavaScript)

// Uses the libphonenumber-js library; import as follows:
// For ES6 modules (Node.js or modern bundlers)
import { parsePhoneNumber } from 'libphonenumber-js';
// OR for CommonJS (Node.js)
// const { parsePhoneNumber } = require('libphonenumber-js');

function normalizePhoneNumber(phone) {
    try {
        const parsed_phone = parsePhoneNumber(phone, 'US');
        if (!parsed_phone || !parsed_phone.isValid()) return null;
        return parsed_phone.number;
    } catch (e) {
        return null;
    }
}

Complete Workflow Example

Here's a complete example showing normalization → hashing for both email and phone:

import crypto from 'crypto';
import { parsePhoneNumber } from 'libphonenumber-js';

// Hash a normalized string using SHA-256
function sha256Hash(input) {
  return crypto.createHash('sha256').update(input, 'utf8').digest('hex');
}

// Example usage
const email = '[email protected]';
const phone = '(555) 123-4567';

const normalizedEmail = emailNormalize(email);  // "[email protected]"
const hashedEmail = sha256Hash(normalizedEmail);

const normalizedPhone = normalizePhoneNumber(phone);  // "+15551234567"
const hashedPhone = sha256Hash(normalizedPhone);

console.log('Hashed Email:', hashedEmail);
console.log('Hashed Phone:', hashedPhone);

Important Notes

  • Always normalize before hashing
  • Use SHA-256 with lowercase hexadecimal output
  • Phone numbers should include the + when hashing
  • Invalid phone numbers return null - handle these gracefully in your implementation
  • These normalization rules match advertising platform standards for consistent matching