If Meta Ads reports 127 purchases and a tracker reports 103, that does not automatically prove a broken integration. The systems may use different event definitions, attribution windows, timezones and report dates. But “Meta models conversions” is not a sufficient explanation either. Break the gap into testable causes.
Reconciliation is not about forcing two interfaces to display the same total. It builds a bridge between a platform-attributed event, a received callback and a confirmed business transaction.
Define the comparison first
For both reports, capture the event definition, time range, timezone, click-time versus conversion-time reporting, attribution window, click-through and view-through rules, modeled events, raw/pending/approved status, value semantics, and account/campaign/GEO/device filters.
Without that contract, comparing Meta Purchases with tracker Sales can combine different populations.
Cause map
| Cause | Usually raises Meta | Usually raises tracker | Proof |
|---|---|---|---|
| View-through or modeling | ✓ | Attribution-setting breakdown | |
| Different windows | ✓/— | ✓/— | Same cohort and window |
| Different timezones | ✓/— | ✓/— | Convert timestamps to UTC |
| Pixel + CAPI without dedup | ✓ | Compare event_id | |
Lost fbclid/fbc | ✓ | Click record and CAPI payload | |
| Missing postback | ✓ | Order exists, callback does not | |
| Different status filters | ✓/— | ✓/— | Raw-to-canonical mapping |
| Organic/direct events | ✓ | Missing paid click ID | |
| Later rejection/refund | ✓ | Status history and corrections | |
| Duplicate callback | ✓ | Transaction ID and duplicate log |
1. Align time
A deposit at 23:30 UTC can fall on different calendar days across an ad account and tracker. Export timestamps, normalize them to UTC, and retain the source timezone.
Also check the report date. Meta may attribute a later conversion back to the day of the ad interaction while the CRM groups it by transaction time. Daily totals will diverge even if a longer cohort eventually matches.
2. Compare equivalent attribution
A tracker usually follows a deterministic click ID. An ad platform may add its own click/view windows and modeled signals. Begin with the strict common subset: both systems recorded the paid click, the event falls inside the same window, event type and status match, and organic/test traffic is excluded.
Use separate fields for tracker_attributed, meta_attributed and business_confirmed. A missing match is a classification problem before it is proof that one system is wrong.
3. Verify fbclid → fbc → CAPI
Meta can provide fbclid after an ad click. Preserve it through landing, PWA and offer redirects and use valid click context in server-side events. Preserve your tracker’s internal click ID in parallel.
If fbclid disappears, the tracker can still receive a deposit via its own key while Meta receives a weaker event. See Click ID vs Sub ID, fbclid, fbc and ttclid for the role map.
4. Validate Pixel + CAPI deduplication
The same purchase is often sent by Browser Pixel and Conversions API. Both copies must describe the same event and use a coordinated event_id.
Common failures include different IDs in browser and server, one permanent ID for every purchase, a new ID on each CAPI retry, mismatched event names, or sending an approved status later as an unrelated second purchase.
Check Events Manager and your own delivery log. Queued is not final evidence; retain the API response and its connection to the transaction. See Pixel vs CAPI vs Postback and Meta’s official Conversions API overview.
5. Reconcile status semantics
A network may send registration, deposit, approved and rejected. A team may optimize Meta on Purchase at first deposit while the tracker report filters only approved sales after hold. Meta will then be higher before maturation even if delivery is correct.
Write the mapping explicitly:
| Raw event | Internal status | Meta event | Revenue |
|---|---|---|---|
reg | Registration | Lead | 0 |
ftd | First deposit | Purchase | payout/value |
approved | Approved | Do not send a second purchase, or apply policy | confirmed |
rejected | Rejected | Policy-dependent correction | 0 |
The exact mapping depends on the business, but it must be explicit, versioned and shared by reporting.
6. Separate callback loss from platform-feedback loss
The pipeline has two boundaries:
Network/CRM → postback → tracker → CAPI → Meta
- CRM order but no tracker event: inspect postback, click ID and status.
- Tracker event but no Meta delivery: inspect policy, payload, token,
fbc/fbp, event ID and API response. - Meta event later rejected by the CRM: preserve status history rather than deleting the original fact.
Use the postback troubleshooting checklist for the first boundary.
7. Distinguish duplicates from repeat purchases
One customer can make two real purchases. One callback can be delivered twice. A transaction ID separates those cases:
| click_id | transaction_id | event | Result |
|---|---|---|---|
c101 | tx900 | purchase | accepted |
c101 | tx900 | purchase | duplicate |
c101 | tx901 | purchase | accepted |
Deduplicating only by click ID would discard tx901; no deduplication would count tx900 twice.
Build a transaction-level reconciliation table
Export one matured cohort and join rows by transaction ID:
| transaction_id | click_id | event_at UTC | tracker status | Meta sent | Meta accepted | business status | value |
|---|---|---|---|---|---|---|---|
tx900 | c101 | 12:04 | FTD | yes | yes | approved | 45 |
tx901 | c102 | 12:11 | FTD | yes | no | approved | 30 |
tx902 | — | 12:19 | — | — | modeled/unknown | approved | 55 |
Assign every unmatched row a reason code: TIMEZONE_SHIFT, WINDOW_MISMATCH, CLICK_ID_LOST, POSTBACK_MISSING, STATUS_FILTERED, CAPI_REJECTED, PIXEL_CAPI_DUPLICATE, ORGANIC_OR_UNATTRIBUTED, MODELED_PLATFORM_EVENT, or BUSINESS_REJECTED.
The discrepancy is now a distribution of causes that can be prioritized.
A reliable reconciliation routine
- Use a cohort mature enough for most holds to resolve.
- Freeze report settings during the comparison.
- Limit scope to one account/GEO/campaign cohort.
- Use transaction-level exports, not totals alone.
- Keep events without deterministic matching separate.
- Never add modeled platform events directly to confirmed CRM revenue.
- Validate fixes on a new cohort instead of rewriting history.
FAQ
What discrepancy percentage is acceptable?
There is no universal safe percentage. A small gap can hide systematic loss in an expensive GEO; a large gap may be explained by windows. Judge reason codes and business impact.
Should I trust Meta, the tracker or the CRM?
For different questions, each. Meta owns its attribution and optimization; the tracker owns deterministic click routing; the CRM/payment system owns transaction truth and final status. Reconciliation connects the layers.
Why did conversions rise after enabling CAPI?
It can reflect better matching or Browser Pixel+CAPI duplicates. Verify event IDs, transaction mapping and Events Manager responses before claiming uplift.
Can the totals match perfectly?
They can approach one another for a strictly defined deterministic subset. Interface totals can still differ because of windows, view-through, modeling and attribution date. The goal is an explained difference that does not distort budget decisions.
Outcome
Start with the data contract: one click record, one transaction ID, explicit status mapping and an auditable Meta delivery log. DarkCore Analytics and Finance connect source context to confirmed revenue while keeping the result exportable and verifiable.