focosys.io
← Writing
Essay

Your CAC numbers disagree because they're measuring different things

GA4, ad platforms, and finance report different CAC numbers by design. Here's the three-layer measurement architecture that resolves the disagreement.

Your CAC numbers disagree because they're measuring different things
Key takeaways
  • GA4, Meta, Google Ads, and your CRM will rarely agree on conversion counts because each uses a different attribution window, a different identity graph, and a different counting method. Reconciliation isn't a bug fix, it's an ongoing process.
  • Multi-touch attribution measures touch, not causation. It tells you who was present at the sale, not who caused it. That distinction is where a lot of media budget gets misallocated.
  • MMM and incrementality testing answer 'would this have happened anyway,' which is the question that should drive budget decisions above the campaign-optimization level.
  • Build a measurement stack with three layers: a source-of-truth ledger (usually your CRM or order system), a platform-attribution layer for optimization, and a periodic incrementality layer for budget allocation. Stop trying to make one number do all three jobs.

Picture a CMO pulling up two reports before a board meeting. Marketing's dashboard says paid social drove a few hundred conversions last month at a certain CAC. Finance's report, built off actual orders in the payment processor, shows fewer new customers and a noticeably higher real CAC, spend divided by new customers.

Nobody committed fraud. Nobody fat-fingered a formula. Both numbers can be correct, for what they're measuring. The problem is that marketing and finance are often measuring two different things and calling them the same name. This is the core reason so many marketing data attribution disagreements never get resolved by a better dashboard: the mismatch is architectural, not a formula error. This guide walks through why that happens and what a working measurement architecture actually looks like.

In my own audits of measurement stacks, I've seen this pattern show up repeatedly, across companies of different sizes and industries, with the gap between the two numbers ranging from modest to multiples apart. The response is almost always the same: someone spends a week trying to find the "real" number, as if one exists. Usually it doesn't. The fix isn't a better spreadsheet. It's understanding why the numbers disagree by design, and building a stack that accounts for it instead of pretending it away. If you want the finance-side version of this exact problem, see why marketing and finance see different revenue numbers.

Why the numbers don't match, mechanically

Start with the plumbing. Every platform in your stack tends to count conversions differently, and the differences compound.

Attribution windows are not the same across platforms

Google Ads, Meta, and GA4 each ship with their own default attribution windows, and those defaults have shifted over the years and vary by account and campaign type. Broadly, and treat these only as illustrative starting points rather than current specs: Google Ads has typically defaulted to a click window measured in weeks with a much shorter view-through window, Meta has typically defaulted to a shorter click window with a short view window, and GA4's default lookback has generally landed in a similar range for most channels, layered with its Data-Driven Attribution model, which weights touchpoints differently than either ad platform's rules-based approach. Check your own account settings before you argue about a discrepancy, since these defaults change over time and vary by account. I've walked through a real case of this window mismatch destroying a channel's reported performance in a case study on an attribution window mismatch that tanked a strong-performing paid channel.

So a single customer who clicked a Meta ad on day 1, saw a Google display ad on day 5, and converted on day 12 can get attributed differently in every system:

  • Meta's own reporting may not credit itself past its click window unless it's the last touch inside that window.
  • Google Ads may claim the conversion because the click falls inside its longer window.
  • GA4 might split credit across both, or neither, depending on the model selected in Advertising > Attribution settings.

Three platforms, three different stories, from the exact same customer journey. None of them are lying. They're using different rulers.

Identity resolution breaks differently on every platform

GA4 identifies users through a combination of client ID (browser-based, cookie), user ID (if you've implemented it), and Google Signals (if enabled, cross-device via logged-in Google accounts). Meta identifies users through the Pixel's fbp/fbc cookies, matched against logged-in Facebook/Instagram identity graphs, plus whatever hashed PII arrives via CAPI.

These are not the same identity graphs. A user on Safari with ITP enabled can get a new GA4 client ID after a fairly short window, because Safari caps how long script-writable cookies persist, while the same user stays perfectly identified in Meta's system because they're logged into Instagram in-app. The exact cap has changed across ITP versions and shouldn't be treated as a fixed, permanent number, but the category of problem is durable: Safari and Firefox both actively work against long-lived first-party cookies set by JavaScript. Cross-domain setups make this worse; see why cross-domain tracking in GA4 can misreport journeys as direct for a specific version of this failure.

The result: GA4 might report this as multiple separate users across multiple sessions. Meta reports it as one user across multiple touches. Same person, two different user counts, before you've even gotten to attribution logic.

Check this before you argue about attribution models

Before debating first-touch vs. last-touch vs. data-driven, check whether your GA4 property has cross-domain tracking configured correctly and whether Google Signals is enabled. I've seen teams spend weeks arguing about attribution windows when the real problem was that checkout ran on a different subdomain and GA4 was creating a new session ID at the point of purchase, every time. For a broader diagnostic pass, see how to tell if your GA4 attribution is wrong.

Deduplication logic varies, and sometimes doesn't exist

If you're running Meta Pixel and CAPI without a shared event_id, you're likely double-counting on the Meta side specifically. I've written about that failure mode in detail in Facebook CAPI deduplication and why your conversions are double-counting: when dedup is broken, reported conversions can run meaningfully above actual conversions, and in the migrations I've reviewed it's broken more often than teams expect because nobody checks Events Manager's server/browser overlap numbers. Low event match quality compounds the problem further, covered in how to validate your Meta CAPI event match quality.

GA4 has its own deduplication problem: if you fire both a GA4 tag and a server-side GTM container hitting the Measurement Protocol directly, without matching client_id and transaction_id, you get duplicate purchase events. In the server-side GTM migrations I've reviewed, this kind of overlap shows up often enough that it's worth auditing for before cutover, not after. See why your server-side tracking migration keeps failing and how to debug server-side GTM for the process.

Finance counts something else entirely

Your CRM or payment processor doesn't care about clicks or sessions. It counts an order when the charge clears. That number is downstream of refunds, failed payments, and often a delay between "lead" and "closed revenue" that spans weeks or months for anything with a sales cycle.

If your finance team pulls CAC as (ad spend) / (new customers in Stripe this month), and marketing pulls CAC as (ad spend) / (platform-reported conversions this month), you're comparing a cash-basis metric to an attribution-model metric. They will rarely match, and trying to force them to match by adjusting the attribution model is treating a structural mismatch like a settings problem.

The deeper issue: multi-touch attribution measures presence, not cause

The deeper issue: multi-touch attribution measures presence, not cause

Even if you fixed every technical mismatch above, perfectly deduplicated events, matched identity graphs, synced attribution windows, you'd still have a conceptual problem underneath the technical one.

Multi-touch attribution (MTA) answers the question "which touchpoints were present before this conversion?" It does not answer "which touchpoints caused this conversion to happen at all, or to happen sooner, or to happen instead of at a competitor?"

Those are different questions, and the gap between them is where a lot of wasted ad spend hides. This is also the mechanism behind randomly assigned attribution credit in some MMM setups, covered in when perfect correlation breaks attribution.

An illustrative example

Take a hypothetical SaaS company with strong organic brand search. A prospect googles the company name directly (they already knew about it from a conference), clicks a branded search ad that happens to be running, browses the pricing page, gets retargeted on LinkedIn for two weeks, and eventually converts.

Last-click attribution credits LinkedIn retargeting. Multi-touch, U-shaped or data-driven, spreads credit across branded search and LinkedIn. Both models agree the paid channels mattered.

But would this prospect have converted anyway? They already knew the company name before they searched. The branded search ad probably captured a click that would have gone to the organic listing directly below it for free. The LinkedIn retargeting kept the brand in front of someone who'd already decided to evaluate the product.

MTA has no mechanism to answer that question, because it only sees touches, not counterfactuals. It cannot tell you what would have happened in a world where the ad didn't run. That's not a flaw in the implementation. It's a structural limit of the method. MTA was built to distribute credit across observed touchpoints, not to model an unobserved alternate reality. I've written up a full case study on this exact pattern in the retargeting fallacy: incrementality vs attribution.

The tell: check your organic conversion rate

The higher your organic conversion rate and brand awareness, the more your MTA numbers tend to overstate the effect of retargeting and branded search campaigns specifically. In holdout tests I've run on retargeting campaigns, it's common to see platform-reported ROAS look strong while the incremental ROAS measured against a suppressed control group comes in far lower, sometimes close to zero, depending on the account. The platform wasn't lying about touch. It just wasn't measuring cause.

Where MTA is still useful

None of this means rip out multi-touch attribution. It's still the right tool for a specific job: day-to-day optimization inside a channel, and relative comparison between similar campaigns competing for the same budget pool.

If you're deciding which of several Meta ad sets to scale this week, MTA data (even last-click) is fast, directionally useful, and cheap to act on. The problem is using it for decisions it wasn't built for, like "should we cut the entire retargeting line item" or "is paid search actually growing the business, or just harvesting brand demand that would convert anyway."

Use MTA for tactical, in-platform optimization. Don't use it to justify channel-level or company-level budget allocation. That's a different measurement problem, with a different tool. For a direct comparison of the two approaches, see multi-touch attribution vs MMM: which one do you actually need.

What actually answers "did this cause growth": MMM and incrementality testing

Marketing mix modeling (MMM) and incrementality testing exist because MTA structurally cannot answer causal questions. They approach the problem from different angles, and most mature measurement stacks need both.

Incrementality testing: the direct method

This is the randomized controlled trial approach. Split your audience into a treatment group (sees the ads) and a holdout group (doesn't), then compare conversion rates between the two groups over a fixed window.

The math that matters isn't total revenue between groups, it's the conversion rate delta, multiplied by audience size, to isolate the actual lift the campaign caused.

Incremental conversions = (Treatment conversion rate - Control conversion rate) x Total audience size
Incremental ROAS = Incremental revenue / Ad spend

In holdout tests like the retargeting example above, it's not unusual to see a small gap between the exposed group and the holdout group, a real but modest lift, sitting underneath a platform-reported ROAS that made the campaign look like a top performer. Treat this as an illustration of a pattern I've seen recur, not a documented benchmark: in cases like this, a campaign that looked strong on platform-reported ROAS can show only a small difference in conversion rate against its holdout, once isolated. The exact gap varies enormously by account, audience overlap, and baseline brand strength.

You can run this in Meta's Ads Manager directly using a geo or audience-based holdout, or through a dedicated incrementality platform if you need more statistical rigor across multiple simultaneous tests. Setup is usually a matter of configuring the split correctly, not a heavy technical lift. The value is in actually looking at the result and acting on it, even when it contradicts the platform dashboard.

Run a 90/10 holdout before you cut anything

If a client won't agree to a 50/50 split on a channel they're nervous about, take the 90/10 compromise. It costs you statistical power, not the validity of the test. As a rough rule of thumb from running these, a 90/10 split held for a few weeks is often enough to tell whether the lift is close to zero or genuinely substantial, though the exact duration you need depends heavily on your baseline conversion volume. If the conversion rate gap is small, that's a strong signal in itself.

MMM: the aggregate method

Incrementality testing works channel by channel, campaign by campaign. It doesn't naturally answer questions about the whole budget, how TV affects paid search brand terms, how offline spend affects online conversion rates, how seasonality confounds everything.

MMM uses historical spend and outcome data, run through a regression or Bayesian model to estimate each channel's contribution to revenue, while controlling for seasonality, pricing changes, competitor activity, and macro trends. As a rough practitioner heuristic, not a fixed rule, you generally want a couple of years or more of history at weekly granularity before the model has enough to work with, and more history plus more spend variation produce a more stable model. There's no fixed minimum that works for every business, and the right amount depends on how much your spend has actually varied.

MMM doesn't require tracking pixels, doesn't break with iOS privacy changes, and works even for channels that don't have click-level attribution at all, like TV, podcast, or out-of-home. That's its main advantage over MTA, which depends entirely on being able to observe and stitch together individual user touchpoints.

The tradeoff: MMM output is directional and requires enough historical data and channel-spend variation to produce a stable model. If you've spent a flat amount on paid search for a long stretch with no test periods, the model has little to learn from. It can't tell you what would happen at a higher or lower spend level because it's never seen that variation.

MMM needs deliberate variance to work

If every channel gets a flat, unchanging budget month over month, your MMM has no signal to separate channel effect from baseline trend. Build in intentional spend variation, holdout weeks, budget pulses, geo splits, specifically so the model has data to learn from. A model trained on a flat line will produce a flat, largely useless coefficient.

Using both together

Mature stacks run MMM periodically, often quarterly or semi-annually, for top-of-house budget allocation (how much goes to paid social vs. paid search vs. TV vs. affiliate, in aggregate), and incrementality tests continuously at the campaign level to validate individual line items inside that allocation.

MTA still runs underneath both, for daily optimization decisions, because it's fast and cheap and directionally fine for in-channel tactical calls. The mistake isn't using MTA. It's using it for the wrong layer of decision.

The three-layer stack that resolves the disagreement

Instead of chasing one unified number that everyone agrees on, build a stack that assigns each measurement method to the layer it's actually good at. This is the core of any workable marketing data attribution guide: stop expecting one number to serve three different audiences.

Layer 1: Source-of-truth ledger. This is your CRM, order system, or payment processor. It's the number finance reports to the board, and it should be. This layer answers "how much revenue and how many customers did we actually get," full stop, no attribution model involved. Nothing here gets touched by marketing's dashboard preferences.

Layer 2: Platform attribution for tactical optimization. GA4, Meta Ads Manager, Google Ads, whatever platform-native reporting you use day to day. This layer answers "which creative, audience, or campaign is performing better than another, right now, this week." It's fast, it's noisy, and it's fine for that specific job. Don't report this number to the board as your CAC. Do use it to decide which ad set gets more budget tomorrow.

Layer 3: Incrementality and MMM for budget allocation. Run holdout tests continuously on your largest spend lines, and refresh your MMM periodically. This layer answers "is this channel actually growing the business, and how much of our total budget should go here." This is the number that should move channel-level and company-level budget, not platform-reported ROAS.

The disagreement between marketing and finance stops being a crisis once everyone agrees which layer answers which question. Finance stays anchored to Layer 1 because that's their job. Marketing uses Layer 2 for weekly optimization because that's what it's built for. Both sides use Layer 3 together, on a recurring cadence, to decide where the next dollar of budget actually goes. If you're building this out for a longer sales cycle or B2B motion specifically, see how to build a measurement roadmap for a B2B SaaS company.

What to do with your stack this week

Pull your last full month of numbers from GA4, your top two ad platforms, and your CRM or finance system, for the same conversion event and time window. Write down all four numbers side by side. If they're reasonably close, your tracking is in decent shape and the gap is mostly attribution-window noise. If they're off by a large margin, you likely have a deduplication or identity-resolution problem sitting underneath the attribution disagreement, and that's worth fixing before you touch any model.

Then pick your single largest spend line, the channel eating the most budget, and run a holdout test on it this month. Not a survey, not a "brand lift study," an actual suppressed control group compared on conversion rate. Whatever the platform dashboard says that channel's ROAS is, the holdout will tell you what's real.

You don't need to resolve the disagreement between marketing and finance. You need to stop expecting one number to do three jobs.

Christopher Landaverde — Marketing Systems Engineer More writing →