Platform signals not deduplicated
Platform signals not deduplicated is one real sale being counted twice because the browser hit and the server hit reach a platform carrying no shared dedup key. Each side fires correctly once. Meta has no matching event_id and event_name, or Google no matching transaction_id, so the two copies are never reconciled into one conversion.
Symptom and cause
- Symptom
- Meta and Google report more conversions than actually happened. The browser hit and the server hit both claim the same sale.
- Cause
- Server-side conversions sent without the dedup key the platform needs: Meta CAPI and the Pixel firing without a shared event_id, or Google's server and browser conversions without a shared transaction_id, so the two hits are never recognised as one sale.
- Where it's caught
- Meta Events Manager showing low or zero deduplication; conversion counts running above actual order counts.
How browser and server both claim one sale
One sale produces two hits by design. The browser sends the Meta Pixel or the Google web tag, and the server sends the Conversions API, the Measurement Protocol or the sGTM tag. Each hit fires exactly once and each is individually correct. The platform collapses the pair into one conversion only when both copies carry the same dedup key, and the key is not the same across platforms. Meta deduplicates a Pixel event against a Conversions API event only when the event_id and the event_name both match, on the same dataset, within roughly a 48-hour window. The event_id has to be byte-for-byte identical, so "order_12345" does not match "12345" and the string "12345" does not match the number 12345, and the event_name match is case-sensitive, so a Pixel sending "Purchase" against a CAPI event labelled "purchase" double-counts even with an identical event_id. If either field differs, or one side carries no event_id, both copies count, unless Meta's weaker fbp/external_id fallback applies (browser event received first, with fbp and/or external_id consistent on both copies; see the fix). Google reconciles on a different key. Google Ads treats a second conversion on the same conversion action with the same transaction_id (the order id) as a duplicate and drops it, so an imported, server or Measurement-Protocol conversion deduplicates against the browser conversion tag on a shared transaction_id. The ids must match exactly, since a different prefix, suffix or an extra space breaks the match, and the id is capped at 64 characters. The keys are not interchangeable. event_id reconciles Meta, transaction_id reconciles Google, and sending the wrong key to a platform fixes nothing. fbp, fbc, fbclid and hashed user data are user-matching and attribution signals, not dedup keys, and timing is not the matcher either. Firing the browser event first and the server event second is good practice and influences which copy Meta keeps, but correctly-keyed events match regardless of order and identical-timing events still fail without the shared key. Meta's window is the only documented one, approximately 48 hours. Google publishes no fixed window for Ads or GA4.
| event | Pixel / browser | CAPI / server | shared key? | counted |
|---|---|---|---|---|
| page_view | evt ████-a01 | evt ████-a01 | yes | 1× |
| add_to_cart | evt ████-c14 | evt ████-c14 | yes | 1× |
| Purchase / purchase | Purchase · order_████-9f3 | purchase · ████-9f3 | no, id and name differ | 2× |
How to catch it
- Read the dedup readout in Meta Events Manager first. Open the dataset, select the purchase event and open its details view, where the source breakdown (browser / server / both) and the deduplication readout live, and check whether the event was received from the browser, the server, or both, and whether it was deduplicated. A low or zero deduplication rate while reported conversions run above true order counts is the F-102 fingerprint. The exact menu wording shifts over time, so read the function, not a fixed label.
- Separate this from F-101 before touching anything. In DevTools, Network, switch on Preserve log, complete one purchase, then filter on /tr for the Meta Pixel and on /g/collect for GA4. DevTools Network confirms only the browser leg: one /tr for the Meta Pixel, one /g/collect for GA4. GA4 batches several events into one request, so confirm the purchase inside the payload (the en=purchase parameter) rather than counting request rows. For an sGTM first-party setup the browser posts to the server container path, not /g/collect, so filter on the server container hostname instead. The server copy (Meta CAPI, or the sGTM / Measurement-Protocol call) is server-to-server and does not appear here. Read it in Meta Events Manager (event received from the server) or in the sGTM container / server logs. The separator is this: two hits from the same source in Network is F-101; one browser hit in Network plus a server-side copy confirmed in Events Manager or server logs, with a missing or mismatched key, is F-102. The two can co-exist, so confirm which you have.
- Inspect the event_id on both copies. On the Pixel /tr request read the dedup id from the query string (it rides as the eid parameter, the on-the-wire form of the fbq eventID property). On the server copy read event_id where the server leg is observable, in the sGTM container Preview for an sGTM/CAPI setup, read the incoming value on the request's Event Data tab, then confirm the value actually sent to Meta on the CAPI tag's Outgoing HTTP Request (the event_id field in the request body), since a tag- or transformation-minted id will not appear in the inbound event data, or in Meta Events Manager Test Events (the server-received event's parameters) for a direct CAPI integration. It is not in the browser Network tab. Confirm the /tr eid and the server event_id are the identical string for one sale: "order_12345" against "12345", or a string against a number, will not match.
- Check the event_name on both sides for an exact case match. Meta treats "Purchase" and "purchase" as two different events, so a Pixel firing standard "Purchase" against a CAPI event labelled "purchase" will not deduplicate even when the event_id is identical. Read the event name on the Pixel hit and on the server payload and confirm they are the same string, same case.
- For the Google side, confirm the transaction_id is present and exactly equal on the browser conversion tag and the server, imported or Measurement-Protocol conversion. In Google Ads, open Goals > Summary and select the Diagnostics tab. If the conversion action ships data from both the tag and a server/imported source, a "Conversions using multiple data sources" card appears, open View Details and read the transaction-ID match-rate warning (the "Your conversions may be overcounted" alert fires when fewer than 10% of transaction IDs match between the tag and the additional data source over the past two days). The "Conversions using multiple data sources" card and the 10%/past-2-days overcount alert live in Google's multiple-data-sources diagnostics, which is a beta surface and may not appear on every account. Where it is absent, fall back to comparing transaction_id presence and exact equality on the tag against the imported/server source directly, or the conversion-action tally against the order count. A different prefix, suffix or a stray space breaks the match, and a blank transaction_id is treated as a real value, which under-counts rather than over-counts.
- Rule out Event Match Quality as the cause. In Events Manager read the EMQ score separately from the dedup status: a dataset can hold a high EMQ and still double-count when event_ids do not match, and can deduplicate correctly on a mediocre EMQ. If conversions run above orders while EMQ looks healthy, the fault is the missing shared key, not match quality.
What putting it right involves
- Mint one dedup key per sale at source and carry it to both hits. Generate a single stable id from the order id at order creation, set it as event_id on the Meta Pixel and on the Conversions API event, and as transaction_id on the Google browser tag and on the server, imported or Measurement-Protocol conversion (the Google Ads imported/server conversion is the reliable transaction_id matcher; for the GA4/Measurement-Protocol leg the shared id is best-effort only, web-stream and undocumented-window, per the FAQ). The same value must reach both copies of the same sale; a per-fire or per-hit id defeats the whole mechanism. Keep the shared id within Google's 64-character transaction_id limit and free of leading or trailing spaces, since that is the tighter of the two platform constraints, a value chosen for Meta can still fail Google if it runs too long.
- Match the event_name exactly on Meta. Send the same name, same case, from the Pixel and the CAPI event ("Purchase" on both, not "Purchase" against "purchase"). Meta needs the event_id and the event_name to agree, so a correct id with a mismatched name still double-counts.
- Send the right key to the right platform and do not swap them. event_id is the Meta key; transaction_id is the Google key. Sending a transaction_id to Meta or an event_id to Google reconciles nothing. Sending the matching key to both browser and server is what collapses the pair into one conversion.
- Keep the firing order as good practice, not as the matcher. Fire the browser event first and the server event second: this influences which copy Meta keeps and enables Meta's secondary fbp/external_id fallback, which deduplicates on event_name plus fbp and/or external_id only when the browser event arrives first. The primary match still comes from the shared event_id within Meta's window (approximately 48 hours), so do not rely on timing to reconcile, and treat the fbp/external_id route as a weaker, order-dependent backstop, not a substitute for minting the shared event_id.
- Treat the shared key and Event Match Quality as separate jobs. A shared event_id deduplicates the two hits; it does not raise EMQ, and a high EMQ does not deduplicate anything. If match quality is the real concern, send hashed first-party data (email, phone) on the CAPI event, which is the F-402 work, not this fix.
- Verify in the platform readouts, not against the store. Re-run one purchase and confirm Events Manager shows the event received from both browser and server and marked deduplicated, with the dedup rate climbing, and confirm Google Ads no longer flags the imported conversion as a second count on the same transaction_id. The Meta Events Manager dedup readout is the fast check. The Google-side confirmation lags because imported/offline conversions and Ads reporting take time to process, so read the Google duplicate-flag check after Google's processing delay, not immediately on the test purchase. Keep the revenue-versus-store reconciliation on F-302; F-102's proof is the platform-side dedup readout and conversions falling back to the order count.
Sources on file
- Meta for Developers: Handling Duplicate Pixel and Conversions API Events (matches on event_id and event_name; an event with no event_id cannot be deduplicated; ~48-hour window)developers.facebook.com
- Meta Business Help Centre: About deduplication for Meta Pixel and Conversions API events (Meta keeps one redundant event and discards the rest; run both Pixel and CAPI)www.facebook.com
- Google Ads Help: Use a transaction ID to minimize duplicate conversions (same transaction ID makes the second conversion a duplicate; prefixes, suffixes and spaces break the match; 64-character limit)support.google.com
- Google Analytics Help (GA4): Minimize duplicate key events with transaction IDs (deduplicates purchase events with the same transaction_id; web streams only; empty string collapses to one)support.google.com
- Stape: Set up Facebook event deduplication in GTM (shared event_id and event_name is the dedup key; Events Manager verification)stape.io
- Enalitica: Meta Conversions API (CAPI) ecommerce guide (~48h window; event_name case-sensitivity, 'Purchase' is not 'purchase'; EMQ is a separate match-rate metric from deduplication)enalitica.com
Cited in these case files
F-102 turns up as evidence across the archive. It is cited in:
- SYMPTOM GA4 revenue runs ahead of Shopify, every week The secondary hand-off, never the primary F-102 exhibit. That note's primary diagnosis is the purchase event double-firing on the page (F-101); it frames event_id and transaction_id as the detection net and platform safety net, 'not a substitute for firing once', and routes the residual here, so whatever still disagrees across the platforms after the double-fire is fixed is filed under F-102.
- RECONCILIATION Reconciliation No. 01 — what moved this period A logged, dated instance under observation, not a settled fact: in ledger entry 01.2 late-arriving server events appear to be falling outside Meta's dedup window (their browser twin already aged out), so they miss the event_id match and the same sale counts twice quietly. Status is 'check', carried forward to No. 02 for re-test once a full week of post-change data lands.
Questions on this file
How do I tell F-102 (signals not deduplicated) apart from F-101 (duplicate conversion firing)?
Count the hits for one platform in DevTools, Network, remembering that only the browser leg shows there. F-101 is a single source sending the same event more than once: two /g/collect hits, or the GTM purchase tag firing twice for one order, milliseconds apart. F-102 is two different systems, the browser and the server, each firing once but never reconciled because they carry no shared event_id or transaction_id, so the total double-counts across them. GA4 batches several events into one request, so confirm the purchase inside the payload (the en=purchase parameter) rather than counting request rows. For an sGTM first-party setup the browser posts to the server container path, not /g/collect. The server copy (Meta CAPI, or the sGTM / Measurement-Protocol call) is server-to-server and does not appear in Network. Read it in Events Manager (received from the server) or in the server logs. The clean separator is the source: two hits from the same source in Network is F-101; one browser hit in Network plus a server-side copy confirmed in Events Manager or server logs, with a missing or mismatched key, is F-102. They can run at once, so check both.
Do I need different keys for Meta and Google, or does one shared id fix both?
Different keys. Meta reconciles the Pixel and CAPI copies on event_id (with a matching, case-sensitive event_name); Google reconciles the browser and server copies on transaction_id (the order id). One id can drive both as long as you send it as event_id to Meta and as transaction_id to Google, on both browser and server. Sending the wrong key to a platform reconciles nothing, so the requirement is the matching key per platform, not a single magic value.
Will firing the server event after the browser event make the deduplication work?
No, that is good practice but not the matcher. Meta matches the two copies on the shared event_id and the matching event_name within roughly a 48-hour window, regardless of order, and identical-timing events still fail to deduplicate when the event_id is missing or mismatched. Firing the browser event first influences which copy Meta keeps and enables the secondary fbp/external_id fallback (event_name plus fbp and/or external_id, browser sent first); that fallback is a weaker backstop, not a reason to skip the shared event_id.
Why does Meta show double conversions even though my event_ids match?
Check the event_name. Meta needs the event_id and the event_name to agree, and the name match is case-sensitive: a Pixel sending "Purchase" against a CAPI event labelled "purchase" is two different events to Meta and will not deduplicate even with an identical event_id. Confirm both sides send the same name in the same case. Also confirm the event_id is the identical string type on both, since "12345" the string does not match 12345 the number.
Does a high Event Match Quality score mean my events are deduplicated?
No. EMQ and deduplication are separate. EMQ scores how well the customer data you send (email, phone, fbp, fbc) identifies a Meta user; deduplication is whether the event_id and event_name collapsed two copies of one sale into one. A dataset can have perfect EMQ and still double-count when event_ids do not match, and can deduplicate correctly on a mediocre EMQ. A shared event_id does not improve EMQ, and a high EMQ does not deduplicate anything.
Does GA4 reconcile my browser and server purchase for me?
Not for this fault. GA4's own transaction_id deduplication is web-stream only, runs in an undocumented processing window, and does not stop a true double-fire from counting twice in practice, so do not lean on GA4 as the cross-system dedup engine. For F-102 the transaction_id is the Google Ads dedup key (and a detection signal), not proof GA4 reconciles browser against server. Note too that a blank transaction_id collapses all blank-id purchases into one, which under-counts, the opposite of the F-102 over-count. Note that since the April 2026 GA4 update, GA4's purchase transaction_id de-duplication has been reported to operate on roughly a 24-hour uniqueness window (a change from a previously longer lookback). Treat this as observed practitioner behaviour, not published Google policy, so a recent jump in conversion counts may trace to it.
Is there one deduplication window I can rely on across platforms?
No. Only Meta's window is documented, at approximately 48 hours, and it is Meta-specific. Google does not publish a fixed dedup window for Google Ads or GA4, so the practitioner "24 to 48 hour" figures you will see for Google are anecdotal. Build on the shared key, not on a window: a matching event_id and event_name for Meta, a matching transaction_id for Google, sent on both browser and server.