Event Deduplication
When you run both a client-side browser pixel and a server-side Conversions API for Meta or TikTok, every conversion fires twice: once from the browser, once from the server. Without deduplication, the platform counts both as separate conversions, and your reported conversion numbers double overnight.
Deduplication is the mechanism that tells the ad platform: “these two events describe the same conversion — count it once.” It is not optional for dual-signal (browser + server) setups. It is also not automatic — you must implement it correctly on both the client side and the server side, using a shared identifier.
The mechanism: shared event_id
Section titled “The mechanism: shared event_id”Every ad platform that supports both browser events and server-side events uses the same deduplication mechanism: a shared event_id that is identical on both the browser-fired event and the server-fired event.
The browser pixel sends the event_id in its payload. The server-side CAPI call includes the same event_id. The platform compares incoming events: if two events have the same event_name and event_id within the deduplication window (typically 48 hours for Meta), only one is counted.
The shared event_id must originate from your site’s JavaScript and travel through your sGTM container unchanged. The most reliable pattern:
- Generate a UUID on the purchase confirmation page
- Push it to the dataLayer alongside the purchase event
- The client-side pixel reads it from the dataLayer and sends it to the browser pixel
- The same value flows through the GA4 Measurement Protocol to sGTM
- sGTM reads it from the Event Model and passes it to the server-side CAPI call
Generating event IDs in the dataLayer
Section titled “Generating event IDs in the dataLayer”// Generate UUID v4 in the browserfunction generateUUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = Math.random() * 16 | 0; var v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); });}
// On purchase confirmation pagedataLayer.push({ event: 'purchase', event_id: generateUUID(), // THE deduplication key transaction_id: order.id, // Also useful as a fallback ecommerce: { transaction_id: order.id, value: order.total, currency: 'USD', items: order.items, }});The event_id is separate from transaction_id. transaction_id identifies the order. event_id identifies this specific event firing — it can include a timestamp component to handle cases where the same order triggers multiple events (e.g., a partial payment followed by a full payment).
A common pattern: event_id = transaction_id + '_' + timestamp_ms
const event_id = order.id + '_' + Date.now();This ensures that even if the same order fires purchase events twice (page reload on the thank-you page), each firing has a unique event_id — and the duplicate is suppressed by the platform.
Meta CAPI deduplication
Section titled “Meta CAPI deduplication”Meta’s deduplication window is 48 hours. Any two events with the same event_name and event_id within 48 hours are deduplicated.
Client-side pixel (via GTM):
In your Meta Pixel tag in client-side GTM, map event_id to the Meta pixel parameter:
// In the fbq() callfbq('track', 'Purchase', { value: '{{DL - ecommerce.value}}', currency: '{{DL - ecommerce.currency}}',}, { eventID: '{{DL - event_id}}' // Maps to deduplication key});Server-side CAPI tag:
In your Meta CAPI tag in sGTM, map the same value:
| Tag Parameter | Value |
|---|---|
| Event ID | {{Event Data - event_id}} |
| Event Name | Purchase |
| Event Source URL | {{Event Data - page_location}} |
| Action Source | website |
The event_id flows from the dataLayer → GA4 client → Event Model → Meta CAPI tag.
Verifying Meta deduplication
Section titled “Verifying Meta deduplication”Meta’s Test Events tool shows you what events are being deduplicated:
-
Go to Meta Events Manager → Test Events
-
Enter your test event code in the sGTM Meta CAPI tag
-
Trigger a purchase on your site (with client-side GTM and sGTM both active)
-
In Test Events, you should see two events arrive with the same
event_id -
Meta will show a “Deduplicated” indicator next to the duplicate event
-
If you see the indicator, deduplication is working. If you see two separate events without the indicator, the
event_idvalues differ between browser and server.
Checking for duplicates in Meta Events Manager
Section titled “Checking for duplicates in Meta Events Manager”Navigate to Events Manager → Overview → select your pixel → look at the Server Events tab. The Match Key Breakdown section shows what percentage of server events matched to a browser event via event_id. A healthy implementation shows 80–95% deduplication for purchase events. Lower percentages indicate that browser events are being sent without event_id, or the values differ.
TikTok Events API deduplication
Section titled “TikTok Events API deduplication”TikTok uses the same mechanism: event_id on both the browser pixel and the server-side Events API call. The deduplication window is 48 hours.
Client-side TikTok Pixel:
ttq.track('CompletePayment', { content_id: '{{product_id}}', value: '{{order_value}}', currency: 'USD',}, { event_id: '{{DL - event_id}}'});Server-side Events API tag:
Map {{Event Data - event_id}} to the event_id parameter in your TikTok Events API tag.
The same verification approach applies: use TikTok’s Test Events in the TikTok Events Manager to confirm both events arrive with matching IDs and the deduplication indicator fires.
Google Ads deduplication
Section titled “Google Ads deduplication”Google Ads Enhanced Conversions do not use event_id for deduplication in the same way Meta does. Instead, Google deduplicates using:
-
Transaction ID: For purchase conversions, the
transaction_idparameter. If Google receives two conversion events with the sametransaction_idfor the same conversion action, it deduplicates them. This works across client-side and server-side only if both send the sametransaction_id. -
GCLID + conversion time: Google links conversions to ad clicks via the
gclidparameter. If a conversion is sent twice with the samegclidand occurs within a short time window, it may be deduplicated automatically.
For practical deduplication with Google Ads:
- Always pass
transaction_idin your purchase events - Ensure both client-side Google Ads Conversion Tag and server-side sGTM tag send the same
transaction_id - Enable Click ID deduplication in your Google Ads Conversion action settings
The server-side approach also benefits from the natural deduplication inherent in running only server-side Google Ads (not both browser and server simultaneously). Many practitioners disable the client-side Google Ads Conversion tag and run exclusively through sGTM, eliminating the duplication problem entirely.
Handling the timing problem
Section titled “Handling the timing problem”The most common deduplication failure is a timing mismatch: the browser pixel fires immediately when the purchase event enters the dataLayer, and the sGTM call fires a second later via the GA4 Measurement Protocol. If the event_id is generated at the time each fires (rather than once before both fire), the two values will differ.
Correct approach:
- Generate
event_idonce, before both pixels fire - Store it in the dataLayer push
- Both the browser pixel tag and the GA4 tag read from
{{DL - event_id}}
Incorrect approach:
// DON'T generate event_id inside the tag templates// This fires at tag execution time, not at the event push timeIf you generate the event_id inside a GTM variable (JavaScript variable type), it will generate a new UUID every time the variable is evaluated — which means the browser pixel and the sGTM call will have different IDs. Always generate in the dataLayer push, before any tags fire.
Reading event_id in sGTM
Section titled “Reading event_id in sGTM”In sGTM, event_id comes through the Event Model if the client-side GA4 tag includes it as a custom parameter. Verify this:
-
In your client-side GA4 configuration tag, add
event_idas an Additional Event Parameter with value{{DL - event_id}} -
Alternatively, add it in each individual GA4 event tag where deduplication matters (purchase, lead, etc.)
-
In sGTM Preview, check the Event Model for your purchase event. You should see
event_idin the Event parameters section. -
If it is not there, the parameter is not being forwarded from client-side GTM to sGTM. Go back to step 1.
In your sGTM Meta CAPI tag, read it as {{Event Data - event_id}}. In your TikTok Events API tag, same variable.
Diagnosing duplicate conversions
Section titled “Diagnosing duplicate conversions”If you suspect deduplication is not working, check these indicators:
Conversion volume doubles immediately after enabling sGTM: You added the server-side CAPI call but did not implement deduplication. All purchases are now counted twice.
Meta Events Manager shows event_id match rate under 50%: Either the browser pixel is not sending event_id, or the server-side events have a different value.
TikTok events show no deduplication: Check whether your browser pixel event_id parameter name matches exactly — TikTok is case-sensitive.
GA4 shows inflated conversion counts after adding GA4 server tag: This is expected if both the browser-side GA4 tag and the GA4 server tag are both firing to the same GA4 property. Resolve by disabling the browser-side GA4 tag and routing all hits through sGTM.
Common mistakes
Section titled “Common mistakes”Not generating event_id before the dataLayer push. The UUID must be in the dataLayer object. Any generation that happens after the event fires (inside a variable template or tag template) produces different values on each evaluation.
Using transaction_id as event_id. A transaction_id identifies an order; it may not be unique per event firing. If a user reloads the purchase confirmation page, the same transaction_id fires again. Using event_id = transaction_id + '_' + timestamp prevents this.
Only implementing deduplication for purchase events. Meta and TikTok deduplicate all event types, not just purchases. If you send both browser and server events for ViewContent, AddToCart, and InitiateCheckout, those need deduplication too. Implement event_id on all events where you run dual-signal.
Assuming platform deduplication is retroactive. Deduplication only works within the platform’s lookback window (48 hours for Meta, 48 hours for TikTok). Events sent before you implemented event_id matching that are still within the window cannot be retroactively deduplicated.