Tracking and attribution: does the money reconcile
Internal working notes, pulled 1 Sep 2026. Not the client deliverable. Second person and byline treatment happens at the report-writing stage. Every call in this task is a GET: Meta Graph API and Meta Ads MCP read tools, the GA4 Data API, and the PII-free Shopify order aggregate. Nothing was created, updated, paused, or deleted.
What would falsify these findings
The headline of this document dies if any exact-window, three-way pull disagrees with the tables below on re-run. Every figure here traces to a dated snapshot in data/_snapshots/2026-09-01/ or to a Meta Ads MCP call made today, not to memory or to the brief's own numbers taken on faith. Re-running any table's query against the same window should reproduce the same figures; if it does not, that re-run is the finding, not this document.
The "Meta under-claims, not over-claims" conclusion dies if any comparison in this document uses two different date ranges on the two sides. Every table below states both sides' exact range. Where a range could not be made identical (Meta has no data before its account was created), that row is left out rather than forced.
The "GA4 misses real orders" finding dies if the missing orders turn up in GA4 under a different transaction ID, a different date (timezone drift), or a refunded/cancelled state. Checked: GA4's transactionId dimension was pulled for June 2026 at the transaction level (not just the monthly total) and cross-referenced against Shopify's order count and zero-value order list for that month; the gap holds at that granularity, not just in aggregate.
The CAPI finding dies if a server-sourced event turns up in a wider window. The Meta Ads MCP's ads_get_dataset_stats tool caps at 28 days lookback, so "no SERVER events" is only checked and confirmed for the last 7 days (the tool's default), inside the available 28-day ceiling. This is flagged explicitly below as a scope limit, not asserted as a lifetime fact.
The 11-vs-13 resolution dies if a third Meta field disagrees with both purchase and omni_purchase. It was tested against a second, independently-shaped query (a fixed calendar-month range via the ad-account entity tool, not the insights-lifetime tool) and both fields reproduced exactly: offsite_conversion_fb_pixel_purchase = 11 ($755.45), actions:omni_purchase = 13 ($770.54), in both the insights-lifetime pull and the 2025-12-01-to-2026-08-31 ad-account pull.
What SHOULD differ, before anything is called wrong
Shopify counts an order the moment its ledger says it exists: a fact about the store, not about marketing. Meta counts an attributed conversion: a purchase credited to an ad because a person clicked or viewed it inside a configured window, which can run past the edge of any reporting range in either direction. GA4 counts a session-tagged event: a purchase event fired by the browser reaching the order-confirmation page with a working analytics tag, which requires cookies, no ad blocker, and no interrupted session. These three are not the same measurement and were never going to match to the order. The question this document answers is not "do they match" but "does the size and direction of the difference make sense," and where it does not, whether a cause can be named.
Reconciliation 1: how the withdrawn "2x" figure was manufactured
Meta side, 90 days, 2026-06-03 to 2026-08-31 (data/_snapshots/2026-09-01/meta.txt, re-pulled today): 6 purchases, $455.96. Cross-checked at the campaign level (meta-insights-campaign-90d.json): Venda 2 + Remarketing 3 + Engajamento 1 = 6, matching exactly.
Shopify side, as originally pulled: the Admin API, called without read_all_orders, silently truncates to the last 60 days and returns no error at that boundary. A 60-day pull ending 1 Sep 2026 starts 3 Jul 2026, not 3 Jun 2026: one month short of Meta's 90-day window. That truncated pull read 3 orders. 6 Meta purchases / 3 Shopify orders = "2x over-reporting." Both numbers were real. The windows were not the same length, and the Shopify side was missing a full month of orders it should have had. This is the entire defect: not a measurement bug in Meta or Shopify, a window bug in the comparison itself.
Same window, GA4 side (data/_snapshots/2026-09-01/ga4-tracking-reconciliation.txt, queried today, 2026-06-03 to 2026-08-31): 9 transactions, $695.92 revenue. Included here to show all three sides of what was actually a two-source comparison at the time.
Reconciliation 2: corrected, like-for-like, June-August 2026
All three sides queried for the identical range 2026-06-01 to 2026-08-31 (calendar months, matching the Shopify order-aggregate's monthly buckets exactly).
| Source | Metric | Value | Exact range queried |
|---|---|---|---|
| Shopify | Orders (all) | 11 | 2026-06-01 to 2026-08-31 |
| Shopify | Revenue-generating orders | 8 | 2026-06-01 to 2026-08-31 |
| Shopify | Net revenue | $815.90 | 2026-06-01 to 2026-08-31 |
| Meta | Purchases (purchase / fb_pixel_purchase) | 7 | 2026-06-01 to 2026-08-31 |
| Meta | Purchase value | $455.96 | 2026-06-01 to 2026-08-31 |
| GA4 | Transactions | 9 | 2026-06-01 to 2026-08-31 |
| GA4 | Purchase revenue | $695.92 | 2026-06-01 to 2026-08-31 |
Shopify: data/_snapshots/2026-09-01/orders-aggregate.md (Jun 7/4/$461.95 + Jul 1/1/$59.99 + Aug 3/3/$293.96). Meta: meta-insights-account-monthly.json, summed Jun+Jul+Aug (4+1+2 purchases, $281.98+$59.99+$113.99). GA4: ga4-tracking-reconciliation.txt, queried directly for this exact range.
Meta claims 7 purchases against Shopify's 8 revenue-generating orders in the identical window. Meta under-claims by one order and $359.94 in value. This is the corrected version of the figure the brief's injected context already gave (6 vs 8, using the 90-day window above rather than calendar months); both framings point the same direction. Meta does not over-report. It under-reports, which is exactly what attributed, windowed conversion counting does: it can only credit a purchase to a click or view it actually saw.
Reconciliation 3: full Meta-account-era, Dec 2025-Aug 2026
Meta's ad account and pixel were both created 2025-12-03. Before that date there is no Meta data to compare against, by construction, not by omission. All three sides queried for 2025-12-01 to 2026-08-31, the widest range that is both a clean calendar-month boundary and fully inside Meta's data window (confirmed: no purchase activity fell on 1-2 Dec 2025 or 1 Sep 2026, so this range and Meta's true 2025-12-03-to-2026-09-01 window return identical purchase totals -- see meta-purchase-window-dec2025-aug2026.json).
| Source | Metric | Value | Exact range queried |
|---|---|---|---|
| Shopify | Orders (all, non-cancelled) | 23 | 2025-12-01 to 2026-08-31 |
| Shopify | Revenue-generating orders | 17 | 2025-12-01 to 2026-08-31 |
| Shopify | Net revenue | $1,571.34 | 2025-12-01 to 2026-08-31 |
| Meta | Purchases (purchase field) | 11 | 2025-12-01 to 2026-08-31 |
| Meta | Purchase value | $755.45 | 2025-12-01 to 2026-08-31 |
| Meta | omni_purchase (broader metric, see TRACK-04) | 13 | 2025-12-01 to 2026-08-31 |
| Meta | omni_purchase value | $770.54 | 2025-12-01 to 2026-08-31 |
| GA4 | Transactions | 19 | 2025-12-01 to 2026-08-31 |
| GA4 | Purchase revenue | $1,206.40 | 2025-12-01 to 2026-08-31 |
Sources: orders-aggregate.md (Dec+Jan+Feb+Mar+Apr+May+Jun+Jul+Aug summed); meta-purchase-window-dec2025-aug2026.json, a direct exact-window pull, not a re-sum of monthly buckets; ga4-tracking-reconciliation.txt.
Over nine months: Meta claims 11 of Shopify's 17 revenue-generating orders (65%), or 13 of 17 (76%) on the broader metric. GA4 claims 19 transactions against 17 real revenue-generating orders -- more transactions than paying orders, because GA4's transaction count also captures $0 orders (see TRACK-05) while separately missing several real paid ones (see TRACK-02). Both patterns are internally consistent with what each system is built to measure; neither is evidence of double-counting or fabrication.
Reconciliation 4 (supplementary, two-way): full order history, Shopify vs GA4
Meta has no data before 2025-12-03, so this range is Shopify-vs-GA4 only, not three-way. Included because it is the range where the GA4 gap (TRACK-02) is easiest to see month by month. Both sides queried for 2025-11-01 to 2026-09-01 (Shopify's first order is 2025-11-06; GA4 confirms zero transactions in the four days before that).
| Month | Shopify orders / paying / revenue | GA4 transactions / revenue | Gap |
|---|---|---|---|
| 2025-11 | 2 / 2 / $216.96 | 1 / $146.98 | GA4 misses 1 paying order, $69.98 |
| 2025-12 | 7 / 7 / $539.48 | 5 / $389.49 | GA4 misses 2 paying orders, $149.99 |
| 2026-01 | 1 / 1 / $120.00 | 1 / $120.00 | Exact match |
| 2026-02 | 1 / 0 / $0.00 | 1 / $0.00 | Exact match (the zero-value order, see TRACK-05) |
| 2026-03 | 1 / 1 / $95.96 | 0 / $0.00 | GA4 misses the entire month's 1 paying order |
| 2026-04 | 0 / 0 / $0.00 | 0 / $0.00 | Exact match |
| 2026-05 | 2 / 0 / $0.00 | 3 / $0.99 | GA4 shows one extra $0-adjacent transaction (see TRACK-06) |
| 2026-06 | 7 / 4 / $461.95 | 5 / $341.97 | GA4 misses 2 paying orders, $119.98 (see TRACK-02, verified at transaction level) |
| 2026-07 | 1 / 1 / $59.99 | 1 / $59.99 | Exact match |
| 2026-08 | 3 / 3 / $293.96 | 3 / $293.96 | Exact match |
| Total | 25 orders / 19 paying / $1,788.30 | 20 tx / $1,353.38 | GA4 under-counts revenue by $434.92 (24.3%) |
Shopify totals here match orders-aggregate.md's own top-line summary exactly (19 revenue-generating orders, $1,788.30 net revenue), which is a useful internal cross-check on the monthly breakdown itself. Currency is ruled out as a cause: three months (Jan, Jul, Aug) match Shopify's revenue to the exact cent, which is only possible if both systems are reporting the same currency at a 1:1 rate; GA4's own currency setting could not be confirmed independently (the GA4 Admin API returns 403 SERVICE_DISABLED for this service account, per docs/audit/21-google-ads.md), so this is an empirical test, not a settings read, and it is a strong one: an exact-cent match across three separate months rules out any conversion factor other than 1.000.
Candidate explanations, tested one by one
Attribution window and view-through. Tested directly: meta-adset-attribution-settings.json shows every purchase-optimizing (OFFSITE_CONVERSIONS) ad set runs Meta's default 1d_view_7d_click_1d_ev (credit a purchase within 1 day of a view or 7 days of a click). This is a real, legitimate source of Meta-vs-Shopify difference at window edges, and explains part of the under-claim in Reconciliation 2 and 3: Meta can only credit what falls inside its own attribution logic, which does not line up with a calendar boundary. It does not explain the direction of the original "2x" error, which was a comparison-window defect, not an attribution-model one (see Reconciliation 1).
Deduplication failure / missing event_id. N/A, tested and ruled out structurally rather than statistically: ads_get_dataset_stats shows the account's only Purchase event in the last 7 days came from event_source=BROWSER, and no SERVER event of any type appears in that window. There is only one purchase signal source. A dedup failure requires two sources disagreeing; with one source, there is nothing to deduplicate against. This is also why Meta's purchase action_type matches offsite_conversion.fb_pixel_purchase exactly at every level checked (11 = 11 lifetime, and in every monthly bucket) -- consistent with a single, non-duplicated event stream, not with double-firing.
Zero-value orders counted as purchases. Tested directly at the transaction level, not inferred. GA4: confirmed yes. The 2026-02 zero-value order (orders-aggregate.md order #1011) shows up as GA4 transaction count 1, revenue $0.00, an exact match to the month having one zero-value order and no paying ones. June's three zero-value orders (#1016, #1021, #1022) show up as three separate GA4 transactionId rows at $0 each (ga4-tracking-reconciliation.txt), alongside two real paid transactions. Meta: not directly testable (no order-level ID crosswalk exists between the Shopify export and Meta's aggregate insights, and Meta's API does not expose purchase value at a per-conversion granularity here), but circumstantially unlikely to be material: Meta's own average value per purchase action, lifetime, is $755.45 / 11 = $68.68, close to Shopify's overall paying-order AOV of $94.12 and nowhere near what the average would be if several of the 11 were $0 orders. INFERRED, not HARD.
Currency. Ruled out. See Reconciliation 4: three separate months match Shopify revenue to the exact cent in GA4, and Meta's account currency is confirmed USD directly (meta-account.json), same as the Shopify aggregate.
CAPI and pixel plumbing
Pixel/dataset 894327239922738, "String Protech Official Pixel," created 2025-12-03 (same day as the ad account), active, last fired 2026-09-01.
No Conversions API integration exists. ads_get_dataset_details returns "gateway_status": "NOT_ONBOARDED" for Meta's Conversions API Gateway. ads_get_dataset_stats with an event_source breakdown, for the only Purchase event in the last 7 days (the tool's lookback ceiling is 28 days; this is stated as a 7-day check, not extrapolated further), shows BROWSER and nothing else. The pixel is browser-only. There is no server-side backstop for the browser events that ad blockers, Safari ITP, or cookie consent choices drop.
Event Match Quality is low and PII-thin, consistent with browser-only. ads_get_dataset_quality returns composite EMQ scores of 5.2-5.3 (Meta's scale runs 0-10) for PageView and ViewContent, the only two events with enough volume to score. Email and phone match-key coverage sit at 1.1%; ip_address, user_agent, and external_id sit at 100% (automatic, browser-supplied). This is the signature of a pixel with no hashed customer-data channel feeding it -- exactly what "no CAPI" predicts, and consistent with, not independent from, the gateway finding above.
The two configured custom event rules are shallow. AddToCart and InitiateCheckout were both created via Meta's "Manual Event Setup Tool" -- text-matching on page content ("add to cart", "check out") rather than a theme-level dataLayer or Shopify checkout-extensibility event. Purchase itself has no custom rule and is not one of these two, meaning it fires as Shopify's native standard pixel event, not a manual match -- the more reliable of the three, which is consistent with purchase and fb_pixel_purchase agreeing exactly everywhere they were checked.
The 11-vs-13 lifetime purchase count: resolved
Task 2's audit (docs/audit/20-meta-ads.md, finding META-02) recorded this as unresolved and classed it CORRUPTS/HARD: "Meta's own lifetime purchase count, re-pulled today, is 11, not the previously-established 13... Meta's own attributed-conversion count for a closed, 9-month-old month changed between pulls." That framing implied instability in a single metric. It is not instability. It is two different Meta metrics, both stable, both correct for what each measures:
purchase(=offsite_conversion.fb_pixel_purchase): the standard
website-pixel Purchase event. Lifetime: 11, $755.45. December 2025: 4, $299.49.
omni_purchase: a broader Meta metric that additionally captures
purchase-equivalent conversions outside the website pixel. Lifetime: 13, $770.54. December 2025: 6.
The arithmetic closes exactly: December's extra 2 (omni_purchase 6 vs purchase 4) matches December's onsite_conversion.messaging_order_created_v2 count of exactly 2 in the same monthly insight row (meta-insights-account-monthly.json). The account ran three MESSAGING_PURCHASE_CONVERSION ad sets in December 2025-January 2026 (all now paused, under the "JET" campaign, meta-adset-attribution-settings.json), optimizing toward orders created inside a Messenger conversation rather than a website checkout. Those two orders would not fire a website Purchase pixel event at all -- they never touched the storefront -- so omni_purchase counts them and purchase does not, by definition, not by drift.
This is TRACK-04 below: the earlier "13" pull almost certainly queried omni_purchase; the later "11" pull queried purchase. Both are correct readings of different fields, re-verified today against a second, independently-shaped query. Neither number is wrong, and no data corruption occurred between the two pulls. This corrects, not just supplements, META-02's classification.
Findings
ID format TRACK-nn. Class: BLOCKS / CORRUPTS / WASTES / SUPPRESSES. Tier: HARD (pulled from a system, dated) / DERIVED (arithmetic on HARD) / INFERRED (reasoned from HARD facts) / UNKNOWN. Remit: unowned / media / client.
| ID | Finding | Evidence | Class | Tier | Remit |
|---|---|---|---|---|---|
| TRACK-01 | The previously-reported "Meta over-reports purchases by roughly 2x" is withdrawn. It compared a 90-day Meta window (6 purchases) against a 60-day-truncated Shopify Admin API pull (3 orders) -- a window-length defect in the comparison, not a measurement defect in either system. Corrected to identical windows, Meta under-claims: 7 purchases vs 8 Shopify revenue-generating orders (Jun-Aug 2026), and 11 vs 17 over the full Dec 2025-Aug 2026 window. | meta.txt, orders-aggregate.md, meta-insights-account-monthly.json, ga4-tracking-reconciliation.txt, 1 Sep 2026 | CORRUPTS | HARD | unowned |
| TRACK-02 | GA4's purchase/ecommerce tracking misses real, revenue-generating Shopify orders across multiple months, verified at the transaction level, not just in monthly totals. Over Nov 2025-Aug 2026: GA4 recorded 20 transactions and $1,353.38 against Shopify's 19 revenue-generating orders and $1,788.30 -- a 24.3% revenue undercount, driven by roughly 6 real paid orders (Nov 1, Dec 2, Mar 1, Jun 2) that never produced a GA4 purchase event at all, most clearly evidenced in June 2026 where GA4's own transactionId rows show exactly 3 zero-value orders captured correctly but only 2 of 4 real paid orders present. | ga4-tracking-reconciliation.txt (monthly + June transaction-level detail), orders-aggregate.md, 1 Sep 2026 | CORRUPTS | DERIVED | client |
| TRACK-03 | No Conversions API (CAPI) integration is active on the Meta pixel. ads_get_dataset_details reports the Conversions API Gateway as NOT_ONBOARDED; ads_get_dataset_stats shows the only Purchase event in the last 7 days sourced from BROWSER, with zero SERVER events of any type. The pixel has no server-side backstop for browser events lost to ad blockers, Safari ITP, or consent choices, and Event Match Quality (5.2-5.3/10, email/phone coverage ~1%) is consistent with a browser-only signal. | meta-dataset-quality.json, 1 Sep 2026 (7-day event-source check; 28-day tool ceiling, stated as a scope limit) | SUPPRESSES | HARD | media |
| TRACK-04 | The "11 vs 13" lifetime Meta purchase discrepancy flagged as unresolved CORRUPTS/HARD in Task 2 (20-meta-ads.md, META-02) is resolved. It is two different, both-correct Meta metrics -- purchase (standard website pixel, 11 lifetime) and omni_purchase (broader metric that also counts 2 Messenger-based messaging_order_created_v2 conversions from now-paused MESSAGING_PURCHASE_CONVERSION ad sets, 13 lifetime) -- not an unstable count. December 2025's extra 2 in omni_purchase (6 vs 4) matches December's messaging_order_created_v2 count exactly. Re-verified via a second, independently-shaped query. | meta-purchase-window-dec2025-aug2026.json, meta-insights-account-monthly.json, meta-adset-attribution-settings.json, 1 Sep 2026, corrects META-02 in 20-meta-ads.md | CORRUPTS | DERIVED | unowned |
| TRACK-05 | Zero-value (100%-discount) orders fire a real purchase/transaction event in GA4 at $0.00, confirmed at the individual transaction level for the 2026-02 order (#1011) and all three 2026-06 orders (#1016, #1021, #1022). This does not distort GA4 revenue (correctly recorded as $0), but would distort a conversion-rate or AOV figure computed naively from GA4's raw transaction count without excluding $0 transactions. Whether Meta's Purchase pixel does the same could not be tested directly (no order-level ID crosswalk to Meta); Meta's lifetime average purchase value ($68.68) sitting close to Shopify's real AOV ($94.12) argues against material inclusion, but this is inferred, not proven. | ga4-tracking-reconciliation.txt, orders-aggregate.md, 1 Sep 2026 | CORRUPTS | HARD (GA4) / INFERRED (Meta) | client |
| TRACK-06 | GA4 shows one unexplained transaction in May 2026 beyond what Shopify's export accounts for: 3 GA4 transactions at a combined $0.99, against Shopify's 2 real zero-value orders that month (both $0.00 exactly, per the discount-code structure). The extra transaction and the non-zero $0.99 total do not match any order in the Shopify export. Possible causes (duplicate purchase-event firing on a page reload, or a checkout that reached the confirmation page but was later voided/deleted from the CSV export before the 12-month cutoff) are plausible but not distinguishable from the access available to this task. | ga4-tracking-reconciliation.txt, orders-aggregate.md, 1 Sep 2026 | CORRUPTS | INFERRED | unowned |
Conclusion
The claim that Meta over-reports purchases against Shopify is false and is withdrawn for the reasons stated above. Every like-for-like window tested -- June-August 2026 and the full Meta-account-era window, December 2025 through August 2026 -- shows Meta under-claiming relative to Shopify's revenue-generating order count, which is the expected direction for attributed, windowed ad-platform reporting compared against a store ledger, not a symptom of broken tracking.
There is a genuine, evidence-backed discrepancy, but it sits on the GA4 side, not the Meta side. GA4 misses roughly 24% of tracked revenue over the ten months checked, because a handful of real, paid Shopify orders each month never produce a GA4 purchase event at all. This is worth fixing independently of anything to do with Meta: it means GA4 (and by extension the Google Ads cost/revenue picture in 21-google-ads.md, which draws on the same GA4 transactions metric) is undercounting store revenue, not just mis-attributing it.
No Conversions API integration exists on the Meta side. The pixel is browser-only, with no server-side signal reaching Meta at all. This is not what caused the withdrawn "2x" claim (that was a comparison-window defect, tested and ruled out as the CAPI explanation above), but it is a real, separate finding: Meta's optimization is running on a thinner, more ad-blocker-exposed signal than it needs to.
The 11-vs-13 lifetime purchase question is resolved, not unknown. It is two different Meta metrics measuring two different things, both internally consistent, both re-verified today by an independent query shape. This supersedes Task 2's classification of the same fact as an unresolved data integrity problem.