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.
Recommended architecture for a Shopify store
The reliable flow for a purchase:
- The customer pays → Shopify fires an
orders/paidwebhook (ororders/create, depending on what you've connected) - Your server receives the order, with the identifiers captured before payment:
fbp,fbc,ttclid - Meta receives a
Purchaseevent. TikTok receivesCompletePayment, not Purchase. Google doesn't receive an event by that name: the ID goes in theorderIdfield - Pixel and server share the same
event_id: the numeric ID of the Shopify order. Not a gid, not#1001, not a_purchasesuffix
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)
| Field | Typical source | Matching impact |
|---|---|---|
fbc | _fbc cookie or fbclid from the URL at click time | Very high for click attribution |
fbp | _fbp cookie set by the pixel | High |
em | Checkout email, normalized SHA-256 (lowercase, trimmed) | High |
ph | Hashed E.164 phone | Medium to high |
client_ip_address + client_user_agent | HTTP request at purchase time | Useful 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
fbcbecause checkout is on a Shopify domain where the cookie doesn't persist — fix: passfbclidas 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 example1842), identical on the pixel side and the server side- Same
Purchasename on both sides, for Meta only - Same
event_time(Unix timestamp) within a few seconds — a gap of several hours can prevent dedup - Same
valueandcurrency— 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_idand 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,fbpparameters 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,ttclidalong the checkout journey - Forward
orders/paidwith 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.