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:
| Method | Who’s in it | Example vendors |
|---|---|---|
| Self-generation | Vendor accepts an ID Signal mints server-side and keeps consistent | Meta (fbp), TikTok (ttp), GA4 (client_id) |
| URL click ID capture | Visitor arrived via a paid ad and the vendor’s click parameter is in the URL | Google (gclid), Meta (fbclid), Pinterest (epik), Microsoft (msclkid) |
| Gateway identity sync | Vendor assigns an ID via its own first-party cookie; no public auth required | Pinterest (pin_unauth), Microsoft (MUID), Snapchat, Reddit, LinkedIn, X, Taboola, Outbrain, Nextdoor, Criteo, Trade Desk, LiveRamp, Index Exchange, Magnite, Amazon AAX |
| Server-proxied enrichment | Vendor requires an authenticated API call with a secret + hashed PII | LiveRamp 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
| Vendor | Redis field | Vendor endpoint | Partner ID | Default TTL |
|---|---|---|---|---|
pin_unauth | ct.pinterest.com/user/ | Pinterest Tag ID | 90d | |
| Microsoft Ads | microsoft_muid | bat.bing.com/action/0 | UET Tag ID | 90d |
| Snapchat | sc_cookie1 | tr.snapchat.com/cm/i | Pixel ID | 90d |
rdt_uuid | pixel.redd.it/sync | Advertiser ID | 90d | |
linkedin_bcookie | px.ads.linkedin.com/wa/ | Insight Partner ID | 90d | |
| X (Twitter) | x_personalization_id | analytics.twitter.com/i/adsct | Pixel ID | 90d |
| Nextdoor | nextdoor_ndid | ads.nextdoor.com/v2/ads/sync | Advertiser ID | 90d |
Native Ad Networks
| Vendor | Redis field | Vendor endpoint | Partner ID | Default TTL |
|---|---|---|---|---|
| Taboola | taboola_t_gid | trc.taboola.com/sg/… | Publisher / Advertiser ID | 90d |
| Outbrain | outbrain_obuid | widgets.outbrain.com/external/sync | Marketer ID | 90d |
AdTech DSPs / SSPs / Identity Graphs
| Vendor | Redis field | Vendor endpoint | Partner ID | Default TTL |
|---|---|---|---|---|
| Criteo | criteo_gum_id | gum.criteo.com/sync | Partner Code | 30d |
| Trade Desk | ttd_uid | match.adsrvr.org/track/cmf/generic | Partner ID | 90d |
| LiveRamp (graph) | liveramp_id | usersync.liveintent.com/sync | Publisher ID | 60d |
| Index Exchange | ix_id | ssum-sec.casalemedia.com | Partner Code | 30d |
| Magnite | magnite_id | pixel.rubiconproject.com/exchange/sync.php | Account ID | 30d |
| Amazon AAX | amazon_aax_id | aax.amazon-adsystem.com/x/px/usersync | Publisher ID | 30d |
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:
| Setting | Required | Default | Notes |
|---|---|---|---|
| Partner ID | Yes | — | The 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) | No | 90 | Shorter 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.
Consent Gating
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:
| Metric | Labels | Meaning |
|---|---|---|
datafly_sync_attempted_total | vendor | Sync redirect initiated — gateway 302 to vendor endpoint |
datafly_sync_succeeded_total | vendor | Vendor returned a non-empty ID on the callback and we stored it |
datafly_sync_failed_total | vendor, reason | no_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 domains — ct.pinterest.com, bat.bing.com, tr.snapchat.com, pixel.redd.it, etc. are on standard blocklists.
Typical coverage:
| Visitor scenario | Gateway Sync result |
|---|---|
| No ad blocker | Sync works. Vendor sets/reads cookie on their domain. High match rate. |
| Ad blocker active | Sync 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).
Consent withdrawn
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.