Skip to main content

Deduplication

When a single user converts, you're often sending the same conversion event to Snap through more than one integration — for example, the Snap Pixel SDK firing in the browser alongside a server-to-server Conversions API (CAPI) call, or a Mobile Measurement Partner (MMP) postback alongside direct CAPI for an app. Without deduplication signals, that same event can be counted more than once in your reporting and your optimization signal can be weaker than it should be.

This page describes how to set up deduplication on your side so that Snap can recognize redundant submissions as the same conversion. The first half describes the core principles which apply whether you're running web or app. The Implementation section at the bottom then splits into web-specific and app-specific guidance — pick the platform tab once and the entire implementation walkthrough will be tailored to your setup.


Core dedup principles

These apply regardless of which integrations you're sending events through.

Send the same unique identifier across each integration

For Snap to recognize the same conversion arriving via more than one source, each event needs to carry the same unique identifier on the corresponding parameter, for the same Snap asset (Pixel ID for web, Snap App ID for app).

When the same identifier is present on both events for the same asset, the two are treated as a single conversion in reporting and optimization rather than two separate ones. Beyond that match, Snap performs additional signal-quality evaluation behind the scenes to choose the strongest representation of the event — so the cleaner the dedup signals you send, the better that evaluation works on your behalf.

One id per conversion event

The id must be unique to that single conversion event — not per session, per visit, or per user. For example, a user who views three product pages and adds two items to their cart in one visit generates five distinct conversion events; each needs its own unique id. Re-using one value across multiple distinct conversions (e.g. passing the same session id on every PAGE_VIEW and ADD_CART in a session) will cause those events to be collapsed together in reporting, leading to under-counting.

Good sources for this id, in order of preference:

  1. An existing transaction id or order id (if your conversion has one).
  2. An existing lead id (for lead-form conversions).
  3. A freshly generated UUIDv4.

If your system already produces one, recycle it across your integrations — you don't need to invent a new identifier.

Send real ids, not placeholders

Make sure the id you send is a real, unique identifier — not a templating token or placeholder. Common mistakes to watch out for include unsubstituted template strings (e.g. {{order.order_number}}, %OrderId%), generic literals like null, undefined, or NA, and raw JavaScript expressions that leaked through from your tag manager configuration. Placeholder values may not be honored for deduplication.

If you see your conversion counts looking inflated in reporting, check the dedup diagnostics in Events Manager to confirm the values you're sending are being recognized. Once you fix the templating on your end, allow up to 24 hours for the change to be reflected before re-checking.

Dedup windows

How long after the first event arrives can a second event still be matched to it? The window depends on which identifier pair the dedup is keyed on:

Identifier pairDedup window
Cross-channel dedup id (e.g. Pixel SDK client_dedup_id ⇄ CAPI event_id for web)48 hours
transaction_idorder_idPURCHASE eventsup to 30 days

A couple of practical implications:

  • If your events are sent within minutes of each other (the usual case), the dedup windows are easily met and you have nothing to worry about.
  • The 30-day window on PURCHASE transaction_id / order_id is intentionally longer so that late-arriving offline confirmations (e.g. server-side purchase events that fire well after the on-site Pixel event) can still be matched. See Purchase event recommendations below.

Purchase event recommendations

For PURCHASE events we recommend sending the order or transaction reference in addition to the cross-channel dedup id. This gives Snap two independent signals to recognize the same purchase across integrations, and adds a longer (30-day) safety net for late-arriving confirmations.

When both pairs are present, the cross-channel dedup id pair takes precedence, with the 48-hour window. If those identifiers don't match between the two events, the transaction / order id pair acts as a fallback, with a longer window suited to late-arriving purchase confirmations.

In practice: if you can only ship one of the two pairs, ship the cross-channel dedup id for everyday correctness; ship the transaction / order id pair as well to add a generous safety net specifically for purchases.

The Pixel SDK accepts this value under the parameter name transaction_id. CAPI v3 accepts the same value under the parameter name order_id (inside custom_data). They are not separate fields — they are the same field with different names on either side of the integration.

For payload examples that show all of this in action, see the platform-specific Implementation section below.


Implementation

Pick your platform below — the setup steps, examples, and platform-specific notes are all in the tab you choose.

For web, the cross-channel match is between Pixel SDK client_dedup_id and CAPI event_id for the same Pixel ID.

What should agree. For the Pixel SDK event and CAPI event to be recognized as the same conversion:

  • The pixel — the asset id in the CAPI endpoint URL should be the same Pixel ID that fired the Pixel SDK event.
  • The event nameevent_name in CAPI (e.g. SIGN_UP) should match the event name passed to snaptr('track', ...).
  • The identifierclient_dedup_id in Pixel SDK and event_id in CAPI should be identical. Treat these values as opaque strings: send the same value on both sides, with the same casing, whitespace, and characters.

Setup steps. The example below uses Google Tag Manager (GTM), but the same pattern applies to any tag manager or hand-rolled tagging.

1. Expose your unique id to your tag manager. In GTM, push the value into the dataLayer when the conversion happens, and read it back via a Data Layer Variable or Custom JavaScript Variable.

2. Inject the id into the Pixel SDK tag as client_dedup_id on the snaptr('track', ...) call:

snaptr('track', 'SIGN_UP', {
client_dedup_id: '{{ dlv - dedup_id }}',
user_email: '{{ dlv - user_email }}',
});

If you don't pass client_dedup_id explicitly, the Pixel SDK will auto-generate one client-side (prefixed with @-). That auto-generated id is only known to the browser — it cannot match anything your server sends to CAPI. You must set client_dedup_id explicitly for cross-channel deduplication to work.

3. Pass the same id as event_id in your CAPI payload. Whichever service fires your CAPI event needs the same id:

{
"data": [
{
"event_name": "SIGN_UP",
"event_time": 1746048000,
"event_source_url": "https://example.com/signup",
"action_source": "WEB",
"event_id": "txn_abc123",
"user_data": { "em": "..." }
}
]
}

See the full list of fields on the Parameters page.

Several Pixel SDK parameters have differently-named CAPI v3 counterparts. Be sure you're writing the value into the right field on each side:

ConceptPixel SDK parameterCAPI v3 parameter
Cross-channel dedup idclient_dedup_idevent_id (top-level)
Order / transaction referencetransaction_idorder_id (inside custom_data)
Category for custom conversionsitem_categorycontent_category (inside custom_data)

If you're migrating from CAPI v2 you may see transaction_id referenced on the CAPI side too — that's the legacy v2 name. In CAPI v3 the field is order_id; see the Migration Guide for the full v2-to-v3 rename list.

PURCHASE example. Set all four parameters across the two integrations. CAPI payload — order/transaction reference goes under order_id inside custom_data:

{
"event_name": "PURCHASE",
"event_id": "1001-2025-05",
"event_time": 1746048000,
"event_source_url": "https://example.com/checkout/complete",
"action_source": "WEB",
"user_data": { "em": "..." },
"custom_data": {
"value": 89.99,
"currency": "USD",
"order_id": "1001-2025-05"
}
}

Pixel SDK call — same value goes under transaction_id:

snaptr('track', 'PURCHASE', {
client_dedup_id: '1001-2025-05',
transaction_id: '1001-2025-05',
price: '89.99',
currency: 'USD',
});

Notice how 1001-2025-05 appears four times across the two integrations — twice on each side, once for each of the two recommended parameter pairs.


Verifying your setup

After you ship the changes, allow up to 24 hours for traffic to flow through and for the diagnostics to update, then:

  1. In Events Manager, open your Pixel (for web) or Snap App (for app).
  2. Navigate to the event in question (e.g. SIGN_UP or PURCHASE) and open Diagnostics.
  3. Confirm:
    • Web: the cross-channel dedup id is present on each integration (Pixel SDK client_dedup_id and CAPI event_id). For PURCHASE, also confirm transaction_id (Pixel SDK) and order_id (CAPI).
    • App: for PURCHASE, the transaction_id (forwarded by your MMP) and order_id (on direct CAPI inside custom_data) are both present and carry the same value.
    • The dedup-related diagnostics for the event look healthy.

Anyone on your team with access to Events Manager can self-serve these checks at any time.

You can also use Snap's Request Check Tool and the test event endpoints to validate your payloads before deploying to production.


Common pitfalls

  • Different ids across integrations. A common mistake is to generate a fresh UUID on the server when firing the CAPI event, rather than re-using the same id that was sent on the other integration. They must be the same value, not two independently-minted ids.
  • Mismatched event_name. Sending SIGN_UP from one integration and signup (or SIGNUP) from another will prevent matching. Use the canonical event names listed in Parameters.
  • Mismatched asset id. The Pixel ID (or Snap App ID) used on one integration must be the same asset id used by the other.
  • Sending the order/transaction id under the wrong CAPI field. Common when reading the Pixel SDK docs and the CAPI docs side by side: the Pixel SDK calls this field transaction_id, but CAPI v3 calls it order_id (inside custom_data). They hold the same value but have different parameter names. Sending transaction_id at the top level of a CAPI v3 payload won't have the intended effect.
  • Placeholder order/transaction values. A transaction_id (or order_id) of {{order.order_number}} is no id at all — make sure your templating is being substituted before the event fires.
  • Re-using one id across many conversions. Each conversion needs a unique id. Re-using the same event_id value across many real conversions can cause them to be collapsed together in reporting.
  • (Web only) Auto-generated Pixel SDK cdid. If you don't pass client_dedup_id explicitly, the Pixel SDK will mint a UUID prefixed with @-. That id is local to the browser session and cannot match what your server sends to CAPI. Always set client_dedup_id yourself.

Further reading

Was this page helpful?
Yes
No