Tracking

Meta CAPI + TikTok Events API: dedup, Event Match Quality and common mistakes

A checklist to send a Shopify purchase without counting it twice: numeric event_id, Purchase for Meta, CompletePayment for TikTok, orderId for Google.

Arthur Reboul13 minUpdated September 25, 2026
Contents

You've turned on Meta's Conversions API (CAPI) and TikTok's Events API. Events Manager shows purchases going up. The algorithm scales. Then Shopify shows you fewer orders than Meta counts conversions — or the other way around.

Often, the gap doesn't come from "iOS 14" in general, but from three details: event_id, matching identifiers, and no testing before raising the budget.

The reliable flow for a purchase:

  • The customer pays → Shopify fires an orders/paid webhook (or orders/create, depending on what you've connected)
  • Your server receives the order, with the identifiers captured before payment: fbp, fbc, ttclid
  • Meta receives a Purchase event. TikTok receives CompletePayment, not Purchase. Google doesn't receive an event by that name: the ID goes in the orderId field
  • Pixel and server share the same event_id: the numeric ID of the Shopify order. Not a gid, not #1001, not a _purchase suffix

Don't trigger the server purchase from the pixel alone: one ad blocker, or a tab closed before the thank-you page, and the paid order never gets sent if there's no webhook.

Meta CAPI: the fields that raise Event Match Quality

Meta shows an Event Match Quality (EMQ) score for each event type. Low EMQ = probabilistic matching = unstable optimization.

Identifier priority (Shopify purchase)

FieldTypical sourceMatching impact
fbc_fbc cookie or fbclid from the URL at click timeVery high for click attribution
fbp_fbp cookie set by the pixelHigh
emCheckout email, normalized SHA-256 (lowercase, trimmed)High
phHashed E.164 phoneMedium to high
client_ip_address + client_user_agentHTTP request at purchase timeUseful fallback

Common mistakes:

  • Sending the email in plain text — Meta requires a SHA-256 hash
  • Forgetting to normalize (spaces, uppercase, +33 vs 0…)
  • Not passing fbc because checkout is on a Shopify domain where the cookie doesn't persist — fix: pass fbclid as a cart attribute note or a first-party cookie before checkout
  • Sending Meta a billing email that differs from the one entered at checkout

event_id and Meta dedup

For each purchase, generate a stable ID shared between the pixel and CAPI. Best practices:

  • event_id = the order's numeric ID (for example 1842), identical on the pixel side and the server side
  • Same Purchase name on both sides, for Meta only
  • Same event_time (Unix timestamp) within a few seconds — a gap of several hours can prevent dedup
  • Same value and currency — a pixel at €49.90 and a CAPI event at €39.92 after a discount are two events in Meta's eyes

In Events Manager → Diagnostics, check the Deduplication line: a high rate of deduplicated "browser + server" events is healthy. If only server events come through, your pixel is probably blocked — not a big deal if CAPI is complete, but EMQ can drop without fbp.

TikTok Events API: how it differs from Meta

TikTok also deduplicates with event_id, the same number as for Meta. The event name, however, is CompletePayment — on the pixel side and on the Events API side. Key identifiers:

  • ttclid — the equivalent of fbclid, to capture on the landing page and persist until the purchase
  • Hashed email and phone (same normalization rules)
  • external_id — hashed Shopify customer ID, useful for repeat purchases

TikTok Events Manager shows an event quality indicator (its label varies with the interface). The same principles apply: without ttclid or email, TikTok guesses — and your CPA gets optimized on noise.

Multi-platform: one webhook, several destinations

A Shopify order must not trigger CAPI twice because of a webhook retry. A safe pattern:

  • Idempotency: before sending, check that this order hasn't already been sent
  • Save the order before calling Meta. If Meta doesn't respond, retry the send from a queue. Returning a 500 to Shopify replays the whole webhook: without idempotency, the purchase goes out twice
  • Log the event_id and the HTTP status, not the plain-text email

Validation checklist before scaling the budget

1. Test Events (Meta)

Use the Events Manager test code. Place a real or sandbox order. Confirm:

  • Purchase event received as Browser and Server
  • em, fbc, fbp parameters present on the server side
  • Dedup message OK for the same event_id

2. TikTok Test Event

Same procedure in TikTok Events Manager. The expected event is CompletePayment, with the same numeric event_id. Check ttclid on an order that came from a TikTok Ads click (not direct traffic). On the Google side, the test covers the orderId field, not an event named Purchase.

3. Shopify reconciliation (rolling 7 days)

Compare:

  • Paid Shopify orders (filtered by payment date)
  • Meta Purchase conversions (7-day click, 1-day view window — or your own setup)
  • TikTok conversions

A gap is common: an ad platform's attribution isn't an order count. A gap of around double points rather to broken dedup or a double send. Far fewer conversions than paid orders points to incomplete sending, or weak matching. There's no "normal" range to copy.

4. Value and refunds

Send the net amount paid (after discount, excluding shipping if you don't include it in your ROAS definition). When an order is refunded, some stacks send a cancellation event — if you don't, Meta keeps optimizing on phantom revenue.

Expensive mistakes (from the field)

  • Double server Purchase — webhook + cron + server-side pixel without idempotency → inflated ROAS, the algorithm scales into the red
  • Random event_id on each retry — dedup impossible, conversions multiplied
  • CAPI alone without a pixel — acceptable for the purchase, but you lose the funnel micro-events (ViewContent, AddToCart) for retargeting
  • Ignoring Consent Mode / the cookie banner — compliance risk + partial data in the EU; server-side doesn't exempt you from marketing consent
  • Testing only in private browsing without fbclid — a false impression of a "broken" EMQ

What an off-the-shelf stack should handle for you

If you implement it by hand, plan for ongoing maintenance (token rotation, API changes, monitoring). A solution built into the Shopify + ads stack should at least:

  • Capture fbp, fbc, ttclid along the checkout journey
  • Forward orders/paid with compliant PII hashing
  • Deduplicate pixel + server through a stable event_id
  • Expose a health status (last successful send, indicative EMQ, API errors)

ProfitPilotPro sends the Shopify purchase to Meta (Purchase), TikTok (CompletePayment) and Google (orderId field), with the same numeric order ID. Email and phone are sent hashed with SHA-256 when the visitor has consented. Net profit is calculated on these same orders. The step-by-step guide is in Documentation → Tracking.

The browser context is covered in the server-side tracking and iOS 14 and Meta ROAS guides.

The trial lasts 14 days, no credit card required. Server-side tracking is included during the trial and on Starter, Pro and Agency. The Free plan doesn't include it. Create an account.

Frequently asked questions

Which event_id should I use for a Shopify purchase?

The order's numeric ID, the same on the browser side and the server side. No prefix, no suffix, no gid.

Does TikTok receive a Purchase event?

No. Meta receives Purchase, TikTok receives CompletePayment, Google receives the ID in the orderId field.

AR

Arthur Reboul

Founder of ProfitPilotPro

14-day trial, no credit card

Your Shopify store, Meta, TikTok and Google spend, and net profit for the period.

Create an account

Read next