IdentityGateway Identity Sync

Gateway Identity Sync

Signal’s Gateway Identity Sync captures vendor first-party cookies (e.g. Pinterest _pin_unauth, Microsoft MUID, Snapchat sc_cookie1) for anonymous visitors — so server-side delivery to vendor Conversions APIs can include the vendor’s own match key alongside click IDs and hashed PII.

It runs with zero vendor JavaScript loaded on the customer’s page: the browser fires a single <img> to Signal’s gateway, the gateway 302-redirects to the vendor’s public match endpoint, the vendor 302s back with the assigned ID, and Signal stores it under the user’s canonical identity.

When Gateway Sync Is Used

Signal uses four different methods to establish vendor identity — use this table to see where Gateway Sync sits:

MethodWho’s in itExample vendors
Self-generationVendor accepts an ID Signal mints server-side and keeps consistentMeta (fbp), TikTok (ttp), GA4 (client_id)
URL click ID captureVisitor arrived via a paid ad and the vendor’s click parameter is in the URLGoogle (gclid), Meta (fbclid), Pinterest (epik), Microsoft (msclkid)
Gateway identity syncVendor assigns an ID via its own first-party cookie; no public auth requiredPinterest (pin_unauth), Microsoft (MUID), Snapchat, Reddit, LinkedIn, X, Taboola, Outbrain, Nextdoor, Criteo, Trade Desk, LiveRamp, Index Exchange, Magnite, Amazon AAX
Server-proxied enrichmentVendor requires an authenticated API call with a secret + hashed PIILiveRamp ATS (envelope), UID2, Acxiom RealID, Trade Desk TDID

Gateway Sync is the right answer when the vendor’s pixel would normally set a cookie on the user’s browser and match to their identity graph. Without the sync, we have no way to identify anonymous visitors to that vendor beyond URL click IDs and hashed PII — which means organic, direct, and email visitors have no match key at all.

How It Works

End-to-end flow per sync:

1. Browser fires /v1/batch event to Signal's gateway (normal event flow)

2. Gateway checks Redis for vendors this canonical identity doesn't yet have
   an ID for (or has an expired ID for). Emits pending_syncs in the response:

     {
       "pending_syncs": [
         { "vendor": "pinterest",
           "url": "/v1/sync/pinterest",
           "partner_id": "2612345678901" }
       ]
     }

3. Datafly.js receives the response and fires:

     <img src="https://collect.<customer>.com/v1/sync/pinterest
                ?pipeline_key=...&aid=<anonymous_id>&partner_id=2612345678901">

4. Gateway validates the pipeline key, builds the vendor match URL, and 302s:

     302 Location: https://ct.pinterest.com/user/
                     ?tid=2612345678901
                     &redirect=https://collect.<customer>.com/v1/sync/pinterest/callback
                               ?aid=<anonymous_id>&pipeline_key=...

5. Browser follows to Pinterest. Pinterest sets/reads its _pin_unauth cookie
   on ct.pinterest.com, then 302s back with the assigned ID:

     302 Location: https://collect.<customer>.com/v1/sync/pinterest/callback
                     ?aid=<anonymous_id>&pipeline_key=...&uid=<pin_unauth_value>

6. Gateway extracts pin_unauth, resolves canonical_id from anonymous_id,
   stores under the canonical identity in Redis (and PostgreSQL), serves
   a 1×1 GIF to complete the <img> request.

7. On subsequent events, the event-processor enrichment pipeline reads
   pin_unauth from the canonical identity and attaches it to any Pinterest
   delivery payload.

The stored ID survives across devices and sessions because it’s keyed by canonical identity, not anonymous ID. Cross-device identity resolution (via shared user_id, cross-domain identity chain, or device recognition) will carry the synced ID forward automatically.

Zero Vendor JavaScript

The critical property: no vendor SDK runs on the customer’s page. The browser makes a single HTTP redirect to the vendor’s public sync endpoint — no scripts executed, no DOM mutations, no third-party cookies on the customer’s domain, no performance overhead.

This is why Signal can claim 100% tag removal while still providing vendor match-key coverage for anonymous visitors.

Gateway Sync is distinct from Server-Proxied Enrichment. Server-proxied enrichment uses authenticated API calls from Signal’s gateway to vendor APIs (with credentials). Gateway Sync is an unauthenticated browser redirect to the vendor’s public match endpoint. Use enrichment when the vendor requires an API key; use Gateway Sync when the vendor exposes a public cookie-sync URL.

Supported Vendors

Walled Gardens

VendorRedis fieldVendor endpointPartner IDDefault TTL
Pinterestpin_unauthct.pinterest.com/user/Pinterest Tag ID90d
Microsoft Adsmicrosoft_muidbat.bing.com/action/0UET Tag ID90d
Snapchatsc_cookie1tr.snapchat.com/cm/iPixel ID90d
Redditrdt_uuidpixel.redd.it/syncAdvertiser ID90d
LinkedInlinkedin_bcookiepx.ads.linkedin.com/wa/Insight Partner ID90d
X (Twitter)x_personalization_idanalytics.twitter.com/i/adsctPixel ID90d
Nextdoornextdoor_ndidads.nextdoor.com/v2/ads/syncAdvertiser ID90d

Native Ad Networks

VendorRedis fieldVendor endpointPartner IDDefault TTL
Taboolataboola_t_gidtrc.taboola.com/sg/…Publisher / Advertiser ID90d
Outbrainoutbrain_obuidwidgets.outbrain.com/external/syncMarketer ID90d

AdTech DSPs / SSPs / Identity Graphs

VendorRedis fieldVendor endpointPartner IDDefault TTL
Criteocriteo_gum_idgum.criteo.com/syncPartner Code30d
Trade Deskttd_uidmatch.adsrvr.org/track/cmf/genericPartner ID90d
LiveRamp (graph)liveramp_idusersync.liveintent.com/syncPublisher ID60d
Index Exchangeix_idssum-sec.casalemedia.comPartner Code30d
Magnitemagnite_idpixel.rubiconproject.com/exchange/sync.phpAccount ID30d
Amazon AAXamazon_aax_idaax.amazon-adsystem.com/x/px/usersyncPublisher ID30d
⚠️

The same customer often has two different vendor IDs to enter: a tag/pixel ID (for Gateway Sync, public) and an advertiser account ID + access token (for server-side delivery to the vendor’s Conversions API). They look similar but are not interchangeable. The management UI labels each field with the correct vendor terminology and includes inline help.

Configuration

Per-pipeline, under Identity Sources in the JS Builder tab. For each enabled vendor:

SettingRequiredDefaultNotes
Partner IDYesThe vendor’s public tag/pixel ID. Inline help in the UI shows exactly where to find it in each vendor’s admin console.
Cookie Lifetime (TTL, days)No90Shorter TTL = fresher matches but more traffic

The customer doesn’t provide any secrets for Gateway Sync — the flow is public, so only the tag/pixel ID is needed. Secrets (API tokens, OAuth credentials) are required only for server-side delivery to the vendor’s Conversions API, which is configured separately on each integration.

Gateway Sync is server-side consent-gated. Each vendorSyncSpec declares a requiredConsent category (default marketing), and the gateway silently omits any vendor from pending_syncs when the user has not granted that category.

Because consent is enforced at the server, the browser never fires the sync pixel for denied vendors — there is no race, no flash-of-sync-then-cancel, and no cookies are read/written on the vendor’s domain.

Consent mode per pipeline:

  • Explicit (GDPR default): absent signal → denied. The safest default for UK/EU traffic.
  • Implicit (CCPA): absent signal → granted. Appropriate for US-only pipelines configured for CCPA opt-out.

Mode is configured on the pipeline (pipelines.consent_mode column); the gateway respects it automatically.

⚠️

Even though no cookies are set on the user’s device by Signal itself, the browser→vendor redirect causes the vendor to read/write cookies on the vendor’s own domain. That triggers ePrivacy / PECR cookie-consent requirements. Server-side storage of the resulting ID does not remove the consent requirement — don’t disable the gating. See Consent Enforcement for the full legal picture.

Observability

Three Prometheus counters are exported by the ingestion gateway:

MetricLabelsMeaning
datafly_sync_attempted_totalvendorSync redirect initiated — gateway 302 to vendor endpoint
datafly_sync_succeeded_totalvendorVendor returned a non-empty ID on the callback and we stored it
datafly_sync_failed_totalvendor, reasonno_id (vendor returned empty), no_canonical (no canonical_id resolved yet), store_failed (Redis write failed)

Per-vendor match rate = sync_succeeded / sync_attempted. A healthy rate is typically 60–70% in UK/EU traffic (the remainder is ad-blocker users, see below). A sustained rate below 40% for a specific vendor usually indicates the endpoint has changed or the Partner ID is wrong.

Ad Blockers and Match-Rate Expectations

Gateway Sync uses a browser redirect to the vendor’s domain. Ad blockers (EasyPrivacy / uBlock / Brave Shields) block requests to most vendor tracking domainsct.pinterest.com, bat.bing.com, tr.snapchat.com, pixel.redd.it, etc. are on standard blocklists.

Typical coverage:

Visitor scenarioGateway Sync result
No ad blockerSync works. Vendor sets/reads cookie on their domain. High match rate.
Ad blocker activeSync blocked at the vendor-domain hop. No ID captured. Visitor invisible to vendor’s identity graph.
Arrived via paid click (e.g. ?epik=…)URL click ID captured before any sync fires; match key exists regardless of blocker.
Has submitted hashed PII (email, phone)CAPI delivery uses hashed PII as match key; sync is additive.

Expected aggregate coverage in UK/EU desktop traffic: 60–70% for sync-dependent matches. That’s consistent with every server-side tag management product on the market — no vendor has solved this.

For customers heavily reliant on organic/direct traffic (content sites, B2B), this is a genuine gap and should be discussed during implementation. Mitigations are limited to: capturing identity earlier in the journey (forms, accounts), and relying on Enhanced Conversions / CAPI hashed-email matching when available. Uploading Customer Match audience lists to vendors is NOT a mitigation — that targets existing known customers, not anonymous visitors.

Redis Storage

The vendor-assigned ID is stored under the canonical identity, not the anonymous ID:

Key:    cvid:{org_id}:{canonical_id}
Type:   Hash
Fields:
  pin_unauth   → { value, source: "sync", collected_at, expires_at }
  microsoft_muid → { value, source: "sync", collected_at, expires_at }
  sc_cookie1   → { value, source: "sync", collected_at, expires_at }
  ...

A companion hash tracks last-sync-time per vendor so re-syncs fire only when TTL expires:

Key:    syncs:{org_id}:{canonical_id}
Type:   Hash
Fields:
  pinterest    → { last_synced: 2026-04-24T12:34:56Z, ttl_days: 90 }
  ...

The event-processor enrichment step reads from cvid: on every event and attaches the relevant IDs to the delivery payload for each integration that needs them. See Vendor Identity Sync for the full enrichment flow.

Dirty-Recovery and Edge Cases

Sync fires before canonical resolution

If the sync <img> fires before the first batch event resolves a canonical_id (possible on first-ever page load if the server is under unusual latency), the callback returns with an anonymous_id but no canonical_id. The gateway logs this as sync callback: no canonical for anonymous_id and increments sync_failed_total{reason="no_canonical"}. The sync is retried automatically on the next batch event once canonical is resolved.

Vendor returns empty ID

Some vendors return the callback redirect with no ID if the user has denied tracking in their own preferences (e.g. Snapchat opt_out=1). The gateway records sync_failed_total{reason="no_id"} and stores nothing; the sync will not be retried until the next TTL expiry (default 24h for failed attempts).

When a user withdraws marketing consent mid-session, subsequent pending_syncs omit the vendor. Already-captured IDs remain in Redis until their TTL expires — Signal does not actively purge on consent withdrawal. If per-user deletion is required (right-to-be-forgotten request), use the management API’s identity deletion endpoint.