Documentation · Acquisition reference · ~10 minute read
Marketing Acquisition: every metric that matters in one view
True attribution, pixel-vs-platform reconciliation, and source/medium breakdown. The flagship marketing dashboard that merges paid + organic into one unified table, drills down to the ad level, and explains every gap between platform-claimed and pixel-measured revenue.
Open Marketing Acquisition Sources & Attribution table Pixel Gap reconciliation Attribution models FAQ
What this page tells you
Four questions every operator asks every Monday. /marketing-acquisition answers all four on one screen.
Where your traffic comes from
Every visitor lands with a source and medium — utm_source / utm_medium when present, the smart-referrer classifier (30+ platforms recognized) when not, organic search when the referrer is google.com or bing.com, and (direct) only as a true fallback. The unified table shows you Meta paid, Google paid, TikTok paid, Klaviyo email, organic search, organic social, direct, and the long tail of referral sources side by side — paid AND organic in the same place, in the same units.
What it's worth
For each source we surface real revenue and orders pulled from your store, not platform-reported conversions. Pixel orders sit next to platform-reported conversions in the same row so you can see the gap; pixel revenue is normalized to your reporting currency so platform-currency mismatches do not distort comparisons. Drill into any paid row and you get campaign → adset → ad ROAS at the same fidelity.
How accurate the platforms are
Meta and Google self-report conversions with view-through, modeled, and cross-device attribution that your pixel cannot replicate. The Pixel Gap column quantifies the difference per row and the Reconciliation popover labels each gap with a likely cause: iOS 14.5 view-through over-claim, untagged-link under-claim, post-click delays, or low CAPI match rate. No more spreadsheet reconciliation passes.
What's working vs what's broken
Every metric carries a delta against the comparison period (previous period, previous year, previous month, or off). Deltas are color-coded by direction with cost-style metrics inverted — a falling CPA is green, a rising CPA is red. Sort the table by delta to see what is moving fastest; combine with the date range to scope the comparison.
The unified Sources & Attribution table
The unified table replaces three older surfaces (Attribution Drilldown, Source Reconciliation, Sources/Sessions) with one premium table that answers every channel question on one screen.
Row structure
Top-level rows are source / medium parents: Meta paid, Google paid, TikTok paid, Klaviyo email, organic search, organic social, direct, referral, and the long tail. Paid sources are expandable; click the chevron to drill into the next layer.
Expanded structure for paid rows: parent → campaign → adset → ad. Each child carries the same column set as the parent — spend, sessions, orders, revenue, ROAS, deltas — at the same fidelity. Organic and referral rows do not expand — there is no underlying campaign hierarchy to drill into.
Sort by any column; the chevron expanders persist across sorts so an expanded campaign tree stays expanded when you re-sort by ROAS.
Columns
| Column | What it shows |
|---|---|
| Source / Medium | The first-touch source and medium for every session in the window. UTM-driven when present; otherwise classified by the smart-referrer matcher. Click the chevron to expand into Campaign → Adset → Ad children for paid rows. |
| Spend | Ad spend pulled directly from the platform API (Meta Marketing API, Google Ads GAQL, TikTok Business API). For organic / referral / email rows: blank by design — there is no ad spend. |
| Sessions | Pixel-side sessions attributed to that source. A session is a sequence of pageviews ≤ 30 min idle gap; one visitor can produce multiple sessions across days. |
| Orders (pixel) | Pixel-attributed orders for the row, scoped by the active attribution model + lookback window. This is your first-party deterministic number. |
| Revenue (pixel) | Pixel-attributed revenue, FX-normalized to your reporting currency. Platform currency mismatches do not distort the column. |
| Pixel CV | Platform-reported conversions for the same window — what Meta or Google says happened. Useful only when you have the platform connected. |
| Pixel Gap | Platform CV minus pixel orders. Positive = platform claims more (likely view-through / cross-device / modeled); negative = pixel sees more (likely untagged links or platforms that cannot match the conversion). |
| ROAS | Pixel revenue / spend. The first-party deterministic ROAS. Compare against the platform-reported ROAS in the drilldown; see the Reconciliation section for which one to trust. |
| CPA | Spend / pixel orders. Cost per pixel-attributed order. Green when falling, red when rising — inverted delta semantics. |
| Δ vs prior | Percent change against the comparison period (configurable: previous period / previous year / previous month / off). Hover any delta chip to see the absolute prior-period value. |
Controls
Column picker. Show or hide any column from a single dropdown. Hidden columns persist per user across reloads. Useful when you want a focused view (just spend + ROAS + delta) or a wide-screen everything view.
Density toggle. Compact / comfortable row density. Compact gives you ~50% more rows on screen; comfortable adds breathing room around money columns. Default is comfortable.
CSV export. Click Export at the top-right to download the active view as a CSV. Hidden columns are excluded; expanded children are flattened into the export with parent identifiers preserved so you can pivot in Excel or Google Sheets.
Pixel vs Platform reconciliation
Platform numbers (Meta CV, Google conversions) almost never match pixel numbers, and that is by design. Each measures something slightly different. The Pixel Gap column on every row quantifies the difference; the Reconciliation popover labels the most likely cause.
iOS 14.5 view-through. Meta uses a 7d-click + 1d-view default attribution window; pixel is last-click. View-through attribution is something Meta’s graph can do that a first-party pixel cannot replicate — expect Meta to over-claim by 10–25% post-iOS 14.5 even with identical click data.
Untagged campaigns. When ad URLs miss utm_source / utm_medium / utm_campaign, the pixel cannot attribute the resulting order back to the source. Pixel under-claims; Platform CV over-claims by the same amount. The fix is on your ad-URL side — use the URL Builder at /url-builder.
Post-click delays. Meta’s Marketing API ships insights data with a 24–72 hour settlement window. Conversions that happen late on day N may not show in Meta’s data until day N+1. The pixel sees them immediately; the row temporarily shows a negative gap that closes overnight.
CAPI match rate. Below 60% match rate, Meta leans hard on modeled conversions. Pass email + phone + fbc + fbp + a clean external_id from the Conversions API to push match rate higher and tighten the gap. See attribution discrepancies for the full reconciliation methodology with a worked example.
Attribution models
Seven models, switchable from a single dropdown at the top of the table. Switching models is non-destructive — the underlying touch data is the same; only the credit-allocation rule changes.
Last Click
Best for: Bottom-funnel campaigns, retargeting, and quick payback windows. The default in GA4 prior to data-driven, easiest to defend in a board meeting.
Watch out for: Massively over-credits the closing channel and starves prospecting / creator content / podcast — the touches that started the journey.
First Click
Best for: Top-of-funnel prospecting, creator content, podcasts, influencers — anywhere you need to defend awareness spend.
Watch out for: Hides closer effectiveness; large new-customer acquisition windows can credit the wrong introductory channel if first touch was an unrelated visit weeks earlier.
Linear All
Best for: Long DTC consideration funnels with 5+ touches per conversion. Spreads credit equally across every touch — paid and organic.
Watch out for: Each touch counts the same regardless of recency; can over-credit organic / direct touches that just happen to appear in long sessions.
Linear Paid
Best for: Allocating ad budget across paid channels only. Equal split across paid touches; organic gets 0% of the credit.
Watch out for: Useful only as an ad-buying lens — never as a single source of truth for revenue. Pair with Linear All to spot when you are overspending in paid.
Time Decay
Best for: Most DTC stores with 5–21 day consideration windows. Exponential decay (H=7d) — recent touches weighted more than older ones. The recommended Admaxxer default.
Watch out for: Half-life is fixed at 7 days; if your funnel is shorter (impulse buys) or longer (high-ticket B2B), the model is mismatched. Cross-check with Markov in the Drilldown.
Position-Based 40/20/40
Best for: Stores that value both discovery and closing equally. First and last touches each get 40%, middle touches share 20%.
Watch out for: Synthetic in two-touch journeys (collapses to 50/50). Rare paths with one touch get 100% — same as Last Click for those.
Markov-Chain
Best for: Operators who want a causal-graph removal-effect credit — the same algorithm Northbeam ships in Probabilistic 1.0, free on Admaxxer above 1,000 attributable paths.
Watch out for: Below 1,000 paths the empty state shows; the model needs enough graph traffic to converge. Use the dedicated /api/v1/analytics/attribution/markov endpoint when you need the full per-channel credit table.
For the full algorithm, formula, and source-additive guardrails behind every model, see /documentation/attribution-models (last-click / first-click / linear-all / linear-paid / time-decay / position-based) and /documentation/markov-attribution (the seventh, removal-effect model).
Comparison periods
Every metric on every tile carries a delta against a comparison period. Pick the comparison from the global dropdown next to the date range; deltas refresh instantly without re-fetching the active window.
- Previous period. Same calendar length immediately before the active window. Last 7 days vs the 7 days before that. Best for week-over-week operator workflow.
- Previous year. Same calendar window one year earlier — accounts for seasonality and recurring promo cadence. Best for stores with strong YoY patterns.
- Previous month. The full prior calendar month vs the active window. Best for monthly reporting and finance hand-offs that align to the calendar.
- Off. Hides every Δ chip and frees screen real estate when you are doing pure absolute-number analysis (e.g. closing the books).
Cost-style metrics (CPA, CPM, CPC, CAC, bounce rate) carry inverted delta semantics: a falling number is a green chip, a rising number is a red chip. This is purely presentational — the underlying math is the same percent-change formula.
UTM coverage
UTM coverage is the percentage of paid sessions that arrived with at least utm_source + utm_medium populated. The coverage banner at the top of /marketing-acquisition surfaces a single number; below 90% the banner turns amber and links to the URL Builder.
Why coverage matters. When ad clicks land without UTMs, the smart-referrer classifier can usually still resolve the source from the referrer header (facebook.com, ads.google.com). But the medium becomes a guess, and worse, the campaign / adset / ad layer is completely lost — the row collapses into the parent source with no drill-down. ROAS for that source is artificially inflated because the spend is correct but the revenue per campaign is unattributable.
Cost of bad coverage. Operators running 50% UTM coverage typically see ROAS calculations off by 10–30% per channel because the (direct) and untagged buckets absorb revenue that should belong to specific paid campaigns. The fix is mechanical: tag every ad URL once using the patterns at /documentation/utm-best-practices, then verify the coverage tile climbs above 95% over the next 7 days.
Use the URL Builder to generate fully-tagged URLs in seconds for Meta, Google, TikTok, Klaviyo, Pinterest, Snapchat, Reddit, and Amazon.
Multi-currency
Workspaces commonly run a Shopify store in one currency (CAD, EUR, GBP) while their Meta or Google ad accounts bill in another (USD, EUR, etc.). Without normalization, a CAD store with a USD-billed Meta account would report ROAS in mixed currencies — a number that means nothing.
How Admaxxer normalizes. Every money value (spend, revenue, AOV, ROAS, MER) is converted to your selected reporting currency at query time using ECB reference rates from frankfurter.dev, cached 4 hours in Redis. Per-row historical accuracy — the rate applied to a row dated 2026-04-01 is the rate from 2026-04-01, not today’s rate.
FX rate badge. Hover any money tile and the tooltip shows: native amount in source currency → converted amount in reporting currency → the applied rate and the rate’s effective date. Conversions are never opaque.
Set the reporting currency at Settings → Exchange rates. See the multi-currency architecture deep-dive for the full provider chain (Postgres + Redis 4h cache, frankfurter.dev fallback, per-row historical lookup).
Edit dashboard
Click Edit dashboard at the top of /marketing-acquisition (or press E) to enter edit mode. Three editable layers:
- Section reorder. Drag the handle on any section header to move the entire section up or down. Sections collapse to a drag-handle row in edit mode for easier sorting.
- Tile reorder. Inside any section, drag any tile to reorder within that section. Reordering is local to the section — tiles do not jump between sections.
- Hide / show. Click the eye icon on any tile or section header to hide it. Hidden items move to a Hidden tray at the bottom of edit mode; click any item to restore.
Saved views. Save the result as a named view from the chips bar above the table. Each view has a default scope: a personal lock-icon view (visible only to you) or a workspace-shared people-icon view (visible to every member). Promote / demote a view from its kebab menu. Each user picks one default per dashboard surface; that view loads on every login.
Reset. Click Reset to layout default in edit mode to restore the factory section + tile order. Reset clears your per-user customization but does not delete saved views.
Keyboard. E toggles edit mode, focus a handle and use ↑/↓/←/→ to nudge the section or tile, Esc exits, Ctrl/⌘+Z undoes up to 10 steps. All animations respect prefers-reduced-motion.
See the dashboard customization deep-dive for the full keyboard cheatsheet, view-promotion semantics, and a 3-way comparison with Triple Whale + Datafast.
Pinned tiles
Click the pin icon on any tile to pin it to the Pinned section at the top of the page. Pinned tiles survive section reorder, view-switching, and sign-out; they are scoped per page (the pin set on /marketing-acquisition is independent of the pin set on /dashboard).
Pinning is orthogonal to layout — the original tile stays in its native section so visitors landing on a saved view do not see a phantom hole. Click pin again on either copy to remove it.
Recommended pins for ad operators: blended ROAS, MER, blended CPA, top-channel ROAS, top-campaign ROAS, CAPI match rate, the Pixel Gap delta. Aim for ≤ 6 pins so the row stays glanceable.
Frequently asked questions
Eight common questions, written so AI search engines can quote them cleanly.
- Why is my Meta-reported ROAS higher than my pixel ROAS?
- Meta uses a 7d-click + 1d-view default attribution window with modeled conversions for iOS 14.5+ traffic plus cross-device attribution from its logged-in graph. Admaxxer's pixel ROAS is deterministic, first-party, and last-click by default. A 10–25% gap is normal post-iOS 14.5; below 60% CAPI match rate the gap widens. Use Admaxxer for headline reporting and Meta for in-platform bidding signal — not the other way around. Open the Reconciliation popover on any row for a per-row labeled cause.
- What does Pixel Gap mean?
- Pixel Gap is Platform CV minus pixel orders for that row in the same window. Positive numbers mean the platform is claiming more conversions than your pixel sees — usually iOS 14.5 view-through, cross-device attribution, or modeled events the pixel cannot replicate. Negative numbers mean the pixel sees more — usually untagged links that the platform cannot attribute back to itself. Hover the gap value to see the labeled cause; click through to the Reconciliation deep-dive for a step-by-step methodology.
- Can I see attribution down to the ad level?
- Yes. Click the chevron on any paid source row (Meta, Google, TikTok) to expand it into Campaign → Adset → Ad children. Each child row carries the same column set — spend, sessions, orders, ROAS, deltas — at the same fidelity as the parent. Sort children by ROAS to find the winners and losers, sort by Δ vs prior to find the movers.
- How does Admaxxer handle multi-currency?
- Admaxxer FX-normalizes every money value to your selected reporting currency using ECB reference rates via frankfurter.dev, cached 4 hours in Redis. Per-row historical accuracy: each row uses the rate from its own date, not today's rate. The FX badge next to every money tile shows the native amount, the converted amount, and the applied rate so the conversion is never opaque. Set the reporting currency at Settings → Preferences. See the multi-currency architecture deep-dive for the full chain.
- How do I save a custom dashboard layout?
- Click Edit dashboard at the top of /marketing-acquisition. Drag any section to reorder it; drag any tile inside a section to reorder within that section; click the eye icon to hide a tile or section. Save the result as a named view from the chips bar — a personal lock-icon view by default, or promote it to a workspace-shared people-icon view via the chip's kebab menu. Each user picks one default per dashboard surface; that's what loads on every login. Press E to toggle edit mode, Esc to exit, Ctrl/⌘+Z to undo up to 10 steps.
- What's the difference between Linear All and Linear Paid?
- Linear All splits credit equally across every touch in the path — paid AND organic. A 5-touch path with 2 paid + 3 organic touches gives each touch 20%. Linear Paid splits credit equally across PAID touches only; organic touches get 0%. Use Linear All for the question 'where does revenue actually come from?' and Linear Paid for 'where should I allocate next week's ad budget?'. Pair them — when Linear Paid ROAS is high but Linear All ROAS is lower, you are over-rotating budget into paid.
- Why does my Conversion Rate look different from Google Analytics?
- Three reasons: (1) Admaxxer's denominator is sessions; GA4's default denominator can be either sessions or active users depending on the report. (2) Admaxxer's pixel uses fingerprint-hashed visitor_id without IP fallback; GA4 uses cookieless modeling that can over- or under-count uniques on Safari. (3) Different definitions of a session boundary — Admaxxer is 30 min idle gap; GA4 default is 30 min plus a per-day reset. Numbers will differ; trust the trend direction more than the absolute value.
- How recent is the data?
- Pixel data is real-time: a session that just landed shows up within seconds. Shopify orders arrive via admin webhooks (real-time + refund-aware) plus a +24-hour daily-poll backfill, so an order placed 30 seconds ago is on the dashboard immediately. Meta Ads Insights are pulled every 15 minutes from Meta's Marketing API; Google Ads via GAQL the same cadence. The Reconciliation columns refresh together — no stale-state mid-row. The Currency / FX rate cache is 4 hours.
- Why might my Meta spend look inflated?
- Two patterns explain almost every "my Meta spend looks 7-17× too high" report. First (most common): the daily ad-spend Materialized View in our analytics database accumulated duplicate rows from re-syncs of the same date range. Tinybird's underlying ClickHouse ReplacingMergeTree dedupes asynchronously, so a re-sync of the same 14 days writes 14 new rows that sit alongside the older rows until the engine merges them — and the consumer pipe summed all of them. Fixed in 2026-05-07's deploy by adding FINAL to the MV writer's source SELECT (or, on older ClickHouse versions, switching to a daily idempotent rewrite worker keyed on workspace+date+platform+account). Second: cross-currency summation. If your workspace is set to CAD but your Meta ad account reports in USD, the platform-aggregator view used to GROUP BY currency and sum each currency separately, then label the total as CAD. The fix is to convert at the write boundary using the per-day ECB rate and stamp every row with the workspace currency, so the consumer sums one currency only. If your Meta spend still looks wrong after this fix, open the per-row Reconciliation popover on the Sources & Attribution table — it shows the raw platform spend, the FX rate applied, and the final converted value for each row.
- Why does my Shopify revenue lag behind reality on today's date?
- Shipping a daily-cron polled-data snapshot at 02:00 UTC means a west-coast Pacific merchant captures "today" at 19:00 PT yesterday — when the day has 0-2 orders. Mid-day Pacific the merchant has 11+ orders, but the dashboard's revenue reducer (which prefers the highest-confidence source per-day across pixel orders, polled Shopify, and pixel visitor payments) was reading the morning-stale 02:00 UTC snapshot. As of 2026-05-07 we run an hourly re-poll cron during merchant business hours (gated by the workspace's timezone, debounced via a 30-min Redis SETNX so manual refreshes and the cron don't double-fire). Today's Shopify-reported row now refreshes at most one hour stale, every hour, while the merchant's storefront is open. Webhook-driven orders are unaffected — those land in seconds. The combined result: the dashboard's "today" revenue tile tracks real-time within a 60-minute lag in the worst case, dropping to seconds for any merchant with the Shopify Custom Pixel snippet installed (which captures checkout_completed events directly).