DEV FIELDNOTES
Advertising systems field guide 008Updated September 4, 2026

UID2 for DSP Retargeting: Email and Phone Normalization

Build a reliable UID2 audience pipeline: normalize email and phone data correctly, map identities, honor opt-outs, and activate through participating DSPs.

Consented email and phone signals passing through a secure normalization and tokenization gateway to several participating advertising platforms

A company exports the same customer from its CRM, CDP, loyalty system, and conversion pipeline. Each team hashes the email before sending it to an advertising partner. The audience files upload successfully, yet the DSP match rate is weak and the campaign cannot find enough of the customers who return on participating publisher inventory.

The problem is often not reach. It is identity drift. One system lowercases the address, another preserves a trailing space, a third hashes a Gmail alias, and a fourth sends a hexadecimal digest where the integration expects Base64. Those inputs describe the same person to a human and four different identities to a machine.

Unified ID 2.0, usually called UID2, gives advertisers, publishers, SSPs, and demand-side platforms a deterministic identity contract built from email addresses and phone numbers. That contract can support audience activation and retargeting across participating partners, but only when every producer implements the same normalization, encoding, refresh, and opt-out rules.

Interoperable does not mean universal

UID2 does not automatically reach every ad network or override a platform’s onboarding rules. It works where the DSP, publisher supply, and other required participants support UID2 and have the appropriate permissions. Treat partner coverage as an explicit capability matrix.

The two sides of a UID2 audience match

UID2 retargeting is easier to reason about when the advertiser path and the publisher bidstream path are separated. They meet inside the DSP at a raw UID2, not at an email address.

Audience activation flow
ADVERTISER SIDE
consented email or phone
  -> exact UID2 normalization
  -> optional SHA-256 + Base64 encoding
  -> UID2 Identity Map v3
  -> raw UID2 + refresh timestamp
  -> DSP audience segment

PUBLISHER / BIDSTREAM SIDE
publisher generates a UID2 advertising token
  -> SSP and bidstream carry the opaque token
  -> authorized DSP decrypts the token
  -> raw UID2
  -> audience-segment match
  -> bid or do not bid

The official advertiser and data provider workflow maps directly identifying information to raw UID2s, stores the returned refresh timing, sends raw UID2s to DSPs according to each DSP’s process, and monitors opt-out status.

On the supply side, publishers place encrypted advertising tokens in the bidstream. Authorized DSPs decrypt valid tokens into raw UID2s, check opt-out state, and match the result to an audience segment. Do not collapse these two identifiers in your data model: a raw UID2 used for audience building is not the rotating encrypted advertising token transported through the bidstream.

Normalize before you hash

A cryptographic hash is deterministic only when its bytes are identical. It does not understand that uppercase and lowercase letters, formatting punctuation, or Gmail aliases might represent the same account. Normalization creates the canonical UTF-8 string that every participant must hash.

UID2 email normalization

For email addresses, trim leading and trailing spaces and convert uppercase characters to lowercase. For gmail.com only, remove periods from the local part and remove a plus sign and everything after it before @gmail.com. Do not apply those Gmail-specific transformations to other domains.

identity/uid2.ts
export function normalizeUid2Email(input: string): string {
  const value = input.trim().toLowerCase()
  const at = value.lastIndexOf('@')

  if (at <= 0 || at === value.length - 1) {
    throw new Error('Invalid email address')
  }

  let local = value.slice(0, at)
  const domain = value.slice(at + 1)

  if (domain === 'gmail.com') {
    local = local.split('+', 1)[0].replaceAll('.', '')
  }

  if (!local) throw new Error('Invalid email address')
  return local + '@' + domain
}
Email normalization vectors
" USER@example.com "          -> user@example.com
"Jane.Doe+offers@gmail.com"    -> janedoe@gmail.com
"jane.doe+offers@example.com"  -> jane.doe+offers@example.com

UID2 phone normalization

UID2 requires phone numbers in E.164 format before they are sent, whether they are sent as normalized text or as a hash. The canonical value begins with +, includes the country code, contains no spaces or punctuation, and has no more than 15 digits.

Do not normalize a national-format number by deleting punctuation and guessing a country. Parse it at the collection edge with an established phone-number library and the user’s known country context. Store the resulting E.164 value. At the UID2 boundary, reject anything that is not already canonical.

identity/uid2.ts
const E164 = /^\+[1-9]\d{1,14}$/

export function assertUid2Phone(input: string): string {
  const value = input.trim()

  if (!E164.test(value)) {
    throw new Error('Phone must already be normalized to E.164')
  }

  return value
}
Phone normalization vectors
US input with known US context: 1 (234) 567-8901 -> +12345678901
Australian mobile with AU context: 0491 570 006 -> +61491570006
Ambiguous input without country context: 2345678901 -> reject

Encode the hash UID2 actually expects

If you send raw or already-normalized email to an Operator-supported integration, the service can normalize and hash the email. Phone numbers must still be normalized first. If your company hashes identifiers before transmission, the documented value is a Base64-encoded SHA-256 digest of the normalized UTF-8 bytes.

A 64-character hex digest is not the final UID2 hash input

SHA-256 produces 32 bytes. Encode those bytes directly as Base64. Do not Base64-encode the 64-character hexadecimal text, and do not hash a value that another system already hashed.

identity/uid2.ts
import {createHash} from 'node:crypto'

function sha256Base64(value: string): string {
  return createHash('sha256').update(value, 'utf8').digest('base64')
}

export function uid2EmailHash(input: string): string {
  return sha256Base64(normalizeUid2Email(input))
}

export function uid2PhoneHash(input: string): string {
  return sha256Base64(assertUid2Phone(input))
}
Official email test vector
normalized: user@example.com
SHA-256 hex: b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514
SHA-256 bytes as Base64: tMmiiTI7IaAcPpQPFQ65uMVCWH8av9jw4cwf/F5HVRQ=

The UID2 normalization and encoding reference includes the canonical rules, hash format, test values, and hashing tool. Treat it as the protocol specification for this stage.

Create one identity boundary for the company

The safest architecture is a single server-side identity service or shared library used by every activation job. It accepts a typed identifier, verifies permission to use it for the requested purpose, normalizes it once, hashes it when required, and records the transformation version. CRM exports and media jobs should not carry their own slightly different normalization expressions.

Canonical activation record
type ActivationIdentity = {
  internalCustomerId: string
  identityType: 'email' | 'phone'
  normalizationVersion: 'uid2-2026-09'
  uid2InputHash: string
  consentPurpose: 'personalized-advertising'
  consentCapturedAt: string
  consentSource: string
  rawUid2?: string
  refreshFrom?: number
  lastOptOutCheckAt?: string
  suppressedAt?: string
}

Model email and phone as separate typed inputs. Do not concatenate them and hash the combined string. Map each valid identifier independently, then use the internal customer ID to keep both mappings attached to one customer and to prevent duplicate activation or measurement events.

Minimize retained directly identifying information

Perform normalization and mapping in a tightly controlled service. Keep raw emails and phone numbers out of job payloads, analytics events, exception messages, and application logs. A hash is pseudonymous data, not permission to distribute it broadly.

Map with Identity Map v3 and retain refresh state

For advertisers and data providers, the current Identity Map v3 endpoint maps email addresses, phone numbers, or their supported hashes to raw UID2s. Its response can include the current raw UID2, a previous raw UID2, and the Unix timestamp from which the value should be refreshed.

Store the mapping and its refresh timestamp. When the timestamp arrives, remap the original identity rather than assuming the raw UID2 is permanent. The endpoint can also return an unmapped reason of optout or invalid identifier. Both results belong in your audience pipeline, not in an ignored error log.

Mapper job behavior
for each consented identity due for mapping:
  normalize and validate with the shared UID2 contract
  submit to POST /v3/identity/map in a supported input field

for each ordered response item:
  if mapped:
    store current raw UID2, previous raw UID2, and refresh timestamp
  if reason is optout:
    suppress the identity and remove it from active audiences
  if reason is invalid identifier:
    quarantine the record with a non-sensitive reason code

retry transient failures with exponential backoff
never log the source email, phone, hash, raw UID2, or token

The Identity Map v3 endpoint reference documents encrypted requests and responses, ordered batch results, a maximum of 5,000 identities per batch, refresh timing, and unmapped reasons.

Honor opt-outs before audience size

UID2 does not replace your consent management platform, privacy policy, data inventory, or jurisdiction-specific review. It gives the ecosystem an identity and opt-out mechanism. Your company still decides whether it has permission to collect and use an identifier for personalized advertising before any UID2 request is made.

Apply two suppression layers. First, honor the user’s choice on your own site or product: when they withdraw the relevant permission, do not create or activate UID2 data for that participant relationship. Second, monitor UID2 opt-out status and remove opted-out raw UID2s from active audience destinations.

UID2’s user opt-out guidance states that participants must respect their own site-level opt-outs and the ecosystem-wide UID2 opt-out. The DSP workflow also requires a DSP to check opt-out state before bidding.

Deletion must travel downstream

Suppressing a customer in the source database is incomplete if yesterday’s DSP audience still contains the identifier. Define deletion and audience-replacement behavior with every destination, then test the complete withdrawal path.

Build a DSP capability contract

“Supports UID2” is not a complete integration specification. Each DSP can have its own audience upload endpoint, accepted identifier form, account permissions, update cadence, retention behavior, and reporting vocabulary. Capture those differences in configuration instead of branching on vendor names throughout the codebase.

DSP capability matrix
DSP / destination
  UID2 audience ingestion supported?        yes / no
  accepted audience identifier              raw UID2 / tokenized sharing
  transport                                 API / managed onboarding / file
  sharing permission required               yes / no
  replacement or incremental updates        replacement / add-remove
  delete and opt-out propagation SLA        documented duration
  minimum audience threshold                documented threshold
  participating inventory coverage          web / app / CTV / named markets
  match-rate and rejection reporting        available metrics
  last integration test                     timestamp + owner

The advertiser sends audience data to a DSP through the process that DSP provides. Separately, UID2-enabled publishers and SSPs make eligible tokens available in bid requests. Retargeting is possible only where those paths overlap. Measure that intersection instead of advertising a theoretical total reach number.

Use sharing permissions as a boundary

Tokenized sharing requires an authorized relationship. Granting a sharing permission enables the selected receiver to decrypt tokens that you explicitly send; it does not itself transfer data. Prefer narrow, reviewed relationships and remove permissions that are no longer needed.

The official UID2 sharing overview distinguishes tokenized sharing from raw UID2 sharing and requires bidstream and pixel scenarios to use tokens. Raw UID2 sharing carries additional security obligations.

Measure the pipeline without leaking the audience

A useful dashboard reports counts and rates by source, identity type, normalization version, mapping result, DSP, and processing date. It does not expose per-person identifiers. The metrics should tell you which stage lost coverage and whether a release changed the contract.

Operational metrics
input_records_total
consent_eligible_rate
invalid_email_rate
invalid_e164_phone_rate
identity_map_success_rate
identity_map_optout_rate
identity_map_invalid_rate
raw_uid2_refresh_due_total
dsp_upload_acceptance_rate
dsp_reported_match_rate
suppression_propagation_age
normalization_version_distribution

A sudden email match-rate drop with a stable phone rate suggests an email normalization or encoding change. Rising invalid phone numbers usually points to lost country context upstream. A growing refresh-due queue is an operational failure even if the last audience upload succeeded.

Test the identity contract before connecting a DSP

Unit tests should include the official UID2 vectors, Gmail-specific cases, non-Gmail plus addressing, Unicode UTF-8 input, valid E.164 numbers, ambiguous national numbers, and values that are already hashes. Integration tests should exercise ordered batch responses, partial invalid inputs, opt-outs, refresh timing, retries, and downstream suppression.

Vitest contract example
import {describe, expect, it} from 'vitest'
import {
  assertUid2Phone,
  normalizeUid2Email,
  uid2EmailHash,
} from './uid2'

describe('UID2 identity contract', () => {
  it('matches the official email hash vector', () => {
    expect(uid2EmailHash(' USER@example.com ')).toBe(
      'tMmiiTI7IaAcPpQPFQ65uMVCWH8av9jw4cwf/F5HVRQ=',
    )
  })

  it('applies Gmail-only alias rules', () => {
    expect(normalizeUid2Email('Jane.Doe+offers@gmail.com')).toBe(
      'janedoe@gmail.com',
    )
    expect(normalizeUid2Email('jane.doe+offers@example.com')).toBe(
      'jane.doe+offers@example.com',
    )
  })

  it('rejects a phone without an international prefix', () => {
    expect(() => assertUid2Phone('(234) 567-8901')).toThrow()
  })
})
Version the transformation, not just the code

Write the normalization version beside every mapping. During a migration, compare old and new aggregate match rates on a controlled sample before rebuilding all audiences.

Production rollout checklist

Done when
[ ] Legal and privacy owners approved the intended advertising purpose
[ ] Consent is checked before normalization or mapping
[ ] Email normalization follows the UID2 rules exactly
[ ] Phone parsing happens with country context and outputs E.164
[ ] SHA-256 digest bytes are Base64-encoded once
[ ] Official test vectors pass in every producing system
[ ] Raw DII, hashes, raw UID2s, and tokens are absent from logs
[ ] Identity Map v3 responses and refresh timestamps are stored
[ ] Opt-out and invalid-identifier results cause explicit state changes
[ ] Email and phone stay as separate typed identities
[ ] Each DSP’s accepted identifier and upload behavior are documented
[ ] Sharing permissions are limited to intended participants
[ ] Downstream deletion and opt-out propagation are tested
[ ] Match rates are monitored by identity type and normalization version
[ ] Marketing claims say participating inventory, not every network

The standard is an operating discipline

UID2 can give companies a common identity key for audience building, bidding, measurement, and retargeting across participating advertising systems. The value does not come from hashing a spreadsheet. It comes from making consent, canonicalization, mapping, refresh, sharing, and suppression one repeatable pipeline.

Start with a shared normalization library and the official test vectors. Then connect Identity Map v3, persist refresh state, enforce opt-outs, and add DSPs one capability contract at a time. When both the advertiser audience and publisher bidstream resolve to the same valid raw UID2, cross-network activation becomes an engineering property you can test—not a match-rate promise you have to hope for.

UID2: Normalization and encodingUID2: Advertiser and data provider integration overviewUID2: POST /v3/identity/mapUID2: DSP integration guideUID2: User opt-outUID2: Sharing overviewUID2: Tokens and refresh tokens