IntegrationsCross-Platform Deduplication

Cross-Platform Deduplication

When Signal runs alongside your existing vendor pixels (Meta Pixel, TikTok Pixel, Pinterest Tag, etc.), each conversion can reach the vendor twice — once from the browser pixel, once from Signal’s server-side delivery. Without coordination, the vendor counts the same conversion twice.

The fix is a shared identifier per event: pass the same value as event_id on Signal’s track() call and as the vendor pixel’s eventID parameter. The vendor matches the two events within their dedup window (usually ~48 hours) and counts it once.

This pattern is the foundation for dual-tagging deployments: keep the existing pixels live, deploy Signal alongside, and let the vendor dedup based on shared event IDs. Once you’re confident Signal is delivering correctly, you can remove the pixels and Signal continues to deliver server-side without disruption.

The pattern

Pick a stable identifier from your backend (order ID, lead ID, subscription ID — anything unique to the conversion). Pass it on both sides:

Signal (datafly.js)

datafly.track('purchase', {
  event_id: order.id,
  transaction_id: order.id,
  value: 99.99,
  currency: 'GBP',
});

The corresponding pixel call

// Meta Pixel
fbq('track', 'Purchase',
  { value: 99.99, currency: 'GBP' },
  { eventID: order.id }
);
 
// TikTok Pixel
ttq.track('PlaceAnOrder', {
  value: 99.99,
  currency: 'GBP',
  event_id: order.id,
});
 
// Pinterest Tag
pintrk('track', 'checkout', {
  value: 99.99,
  currency: 'GBP',
  event_id: order.id,
});

That’s it. As long as both sides see the same value within the vendor’s dedup window, the conversion counts once.

Vendor matrix

VendorPixel parameterServer fieldDedup window
Meta CAPIeventID (3rd arg to fbq)event_id48 hours
TikTok Events APIevent_id in pixel paramsevent_id48 hours
Pinterest Conversions APIevent_id in pixel paramsevent_id24–48 hours
Amplitude HTTP V2insert_idevent_id (mapped to insert_id)7 days
WP Adsevent_id in pixel paramsevent_id24 hours
Google Ads (Enhanced Conversions)transaction_id (gtag)order_id (CAPI)per-conversion
Google DV360 / CM360n/a (uses gclid for match)n/an/a
GA4 Measurement Protocoltransaction_id for purchase eventstransaction_idper-event
⚠️

Google Ads and DV360 don’t dedup by an arbitrary event_id — they dedup by Google Click ID (gclid) plus transaction_id. If you’re running Enhanced Conversions alongside Signal, set transaction_id to your stable order ID on both gtag and Signal’s track() call.

How Signal resolves the event_id

When a track() call includes event_id in its properties, Signal’s SDK promotes it to the top-level eventId envelope field and removes it from properties so it isn’t double-mapped. Inside the pipeline, the source path event_id resolves to the envelope eventId with messageId as the fallback when the customer didn’t supply one.

Crucially, all placement into the vendor payload is driven by the blueprint — there is no hidden auto-injection in the delivery layer. Each vendor’s default blueprint includes an explicit mapping that routes the resolved event_id to the field name the vendor’s API expects:

VendorMapping in default blueprint
Meta CAPIsource: event_idtarget: event_id
TikTok Events APIsource: event_idtarget: event_id
Pinterest Conversions APIsource: event_idtarget: event_id
WP Adssource: event_idtarget: event_id
Amplitude HTTP V2source: event_idtarget: insert_id

If you customise a blueprint or strip the mapping, the field is not sent — there’s no silent fallback. Open the blueprint Mapping tab to see exactly which fields are wired to event_id.

Amplitude: dedup also requires matching device_id

Amplitude dedupes on device_id + insert_id within a 7-day window. The shared insert_id (covered above) is necessary but not sufficient — device_id must also match between the browser Amplitude SDK and Signal’s server-side delivery.

By default they diverge:

  • Amplitude SDK auto-generates a device_id (UUID) and persists it in its own cookie.
  • Signal’s blueprint maps Datafly’s anonymous_id to device_id.

Align them by setting Amplitude’s deviceId to Datafly’s anonymousId once both SDKs are loaded:

amplitude.init('YOUR_AMPLITUDE_API_KEY', {
  deviceId: datafly.getAnonymousId(),
});

Or, post-init:

amplitude.setDeviceId(datafly.getAnonymousId());

For known users, pass the same user_id to both:

datafly.identify('user-123', { email: '...' });
amplitude.setUserId('user-123');
⚠️

If device_id differs between the pixel and server events, Amplitude treats them as separate users and dedup will not apply — even if insert_id matches.

Testing dedup

After deploying both pixel and Signal:

  1. Trigger a test conversion from a real browser session.
  2. Open the vendor’s test events tool:
    • Meta: Events Manager → Test Events tab
    • TikTok: Events Manager → Test Events
    • Pinterest: Conversion Tags → Test Events
  3. Confirm the event appears with both Browser and Server sources, marked as deduplicated.
  4. If the event appears twice (no dedup badge), check that:
    • Both calls use exactly the same event_id (string equality, no whitespace, same case)
    • Both events are within the dedup window (don’t test with one fired hours after the other)
    • The pixel eventName and Signal’s vendor event name match

Why this matters

Without dedup, vendor reports inflate revenue by the share of double-counted events. For a typical e-commerce setup with one pixel + one CAPI integration, that’s a 100% inflation on the conversions that fire on both sides. Multiplied across 6–8 vendor pixels, total reported revenue can hit 150–220% of actual revenue.

Sharing event_id across pixel and CAPI restores accuracy without requiring you to remove the pixel — the foundation for moving to a Signal-led attribution model where the pixels are eventually removed entirely.