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

Where does this data come from? Every tile on this page is backed by an analytics pipe today, with the per-workspace our analytics warehouse cutover landing through 2026-Q2. Read the warehouse migration story — ±1% parity gate, per-workspace cutover, instant rollback.

Which lens + model should I use?

Sources & Attribution has two big switches: the lens (which dataset is feeding the row) and the attribution model (how credit is split across touches in a path). Pick the wrong combination and ROAS will read $0.00 even when the orders are flowing.

The 4 lenses

Each lens answers a slightly different question. The numbers will not match across lenses — that is by construction, not a bug.

Pixel

What YOUR pixel attributed via UTM.

What it measures
First-party UTM-driven click attribution from visitor_payments + revenue_events. Deterministic and conservative.
Best for
Per-campaign breakdowns when your URL Builder is correctly tagging every paid link with utm_source / utm_medium / utm_campaign.
Gotcha
Ad-blockers + Safari ITP suppress 30–60% of pixel events; iOS 14.5 view-through is invisible. Pixel ROAS will always read lower than the platform claims.

Shopify

Shopify's customer-journey ledger.

What it measures
Shopify's Order.customerJourneySummary post-checkout journey graph. Doesn't depend on UTMs or the pixel — uses Shopify's server-side touch ledger.
Best for
Total revenue truth. The same lens Shopify's own "Top marketing channels" admin page uses, so the numbers reconcile to the merchant's expectation.
Gotcha
Requires the Shopify Custom App connection (paste-token) AND ≥1 order with customerJourneySummary populated. The lens pill disables itself with a tooltip when either is missing.

Platform

Meta / Google as-reported.

What it measures
Each platform's own attribution graph (Meta omni_purchase deduped, Google conversions, TikTok total_complete_payment).
Best for
Matching the numbers on Meta Ads Manager or Google Ads UI directly — required when the platform's optimizer is making the bidding decisions.
Gotcha
Includes view-through attribution (a view inside the attribution window counts, not just a click). Platform ROAS is typically 5–30× higher than Pixel or Shopify — that's by construction, not a bug.

All

Three lenses side-by-side.

What it measures
Renders pixel · Shopify · platform on the same row, with a provenance chip showing which lens won the best-of-three synthesis on each cell.
Best for
Diagnosing tracking gaps. The diagnostic surface — switch to a single lens once you trust the numbers and want a single source of truth.
Gotcha
Wider table, more visual noise. Don't park here in a board report — pick the lens you trust, then quote that one.

Default for first-time users: the /marketing-acquisition surface ships on the All lens so you can see the diagnostic surface up-front. Switch to a single lens once you trust the numbers.

Which attribution model?

Switching models is non-destructive — the underlying touch data is the same; only the credit-allocation rule changes. The default is Last Click — the dashboard and drill-down both open on it. For multi-touch credit on most DTC 5–21 day funnels, switch to Time Decay (H=7d) (the same model GA4 calls Time Decay).

Model What it does When to use Gotcha
Last Click 100% of the credit goes to the final touch before the order. Bottom-funnel campaigns + quick payback windows. Easiest to defend in a board meeting. Massively over-credits the closing channel; starves prospecting + creator content.
First Click 100% of the credit goes to the first touch in the path. Defending top-of-funnel spend — prospecting, creators, podcasts, influencers. Hides closer effectiveness; can credit the wrong introductory channel when first touch was an unrelated visit weeks earlier.
Linear All Credit split equally across every touch — paid AND organic. Long DTC consideration funnels with 5+ touches per conversion. Answers "where does revenue actually come from?" Each touch counts the same regardless of recency; can over-credit organic / direct touches that just happen to appear in long sessions.
Linear Paid Credit split equally across PAID touches only; organic gets 0%. Allocating next week's ad budget across paid channels. Answers "where should the next dollar go?" Not a revenue source-of-truth. Pair with Linear All — when Linear Paid ROAS is high but Linear All ROAS is lower, you're over-rotating into paid.
Time Decay Exponential decay (H=7d). Recent touches weighted more than older ones. Most DTC stores with 5–21 day consideration windows — the recommended multi-touch lens (and the same model GA4 calls Time Decay). The dashboard opens on Last Click; switch to Time Decay when you want multi-touch credit. Half-life fixed at 7 days. If your funnel is shorter (impulse buys) or longer (high-ticket B2B), cross-check with Markov.
Position-Based 40/20/40 First + last touches each get 40%; middle touches share the remaining 20%. Stores that value discovery and closing equally — typical for high-AOV considered purchases. Synthetic in two-touch journeys (collapses to 50/50). One-touch paths get 100% — same as Last Click for those rows.
Markov-Chain Causal removal-effect: the credit a channel deserves is the lift in conversion rate when that channel is removed from the graph. When you want a defensible, causal-graph answer — same algorithm Northbeam ships in Probabilistic 1.0. Free on every Admaxxer plan. Needs ≥1,000 attributable paths in the date range. Below that, falls through to Time Decay with a chip explaining the fallback.

Full formulas and source-additive guardrails for every model live in the Attribution Models deep-dive. Markov gets its own page at /documentation/markov-attribution.

FAQ: Why is my ROAS 0.00×?

Three most likely causes — work through them in order. The data is almost always there; one of these knobs is just turned wrong.

  1. Your pixel was just installed. Pre-install dates structurally return $0 because no UTM-attributed sessions exist before the snippet was on the page. Move the date range forward 24 hours after install and the rows populate. Check the install timestamp on /documentation/install.
  2. Your URL Builder is producing inconsistent UTMs. The channel match key is case-sensitive (e.g. utm_source=facebookutm_source=Facebook) — half your revenue is credited to one row and the spend to another, so ROAS reads as $0 on both. Open /url-builder, pick a canonical lowercase convention, and re-tag every paid link.
  3. You are on the Pixel lens but your traffic is heavily ad-blocked. Ad-blockers and Safari ITP suppress 30–60% of pixel events on most DTC traffic. Switch to the Shopify lens — the Shopify Custom App connection feeds Shopify's customer-journey ledger, which does not depend on the pixel and is not suppressed by ad-blockers. Numbers usually populate immediately.

Still stuck after walking through all three? Open the Why drawer on any row in the unified table — it shows what each lens saw and explains the gap in plain English. The same explanation backs the in-product chat agent at /api/v1/analytics/chat.

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

ColumnWhat 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.

Sort + filter your data

Every numeric column header in the unified Sources & Attribution table is click-to-sort. Default order is Spend descending; click any numeric column header (Spend, Sessions, Orders, Revenue, Pixel CV, Pixel Gap, ROAS, CPA, AOV, NC ROAS, Δ vs prior) to sort by it. Click the same header again to flip ascending/descending. Click a third time to clear the sort and return to the default Spend-desc order. The chevron expanders persist across sorts so an expanded campaign tree stays expanded when you re-sort.

Date-range chips above the date inputs offer eight common presets: Today · Yesterday · 7d · 14d · 30d · 90d · MTD · Custom. Click any chip to set both the start and end date in one action. To pick non-standard dates, use the two date inputs directly — Admaxxer auto-switches to the Custom chip the moment you edit either input. The active chip stays highlighted so the comparison-period filter (next to the date range) has a clear anchor.

Tips

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 multi-touch lens; the dashboard opens on Last Click, so switch to Time Decay when you want multi-touch credit.

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).

Google sync schedule

Google Ads has two sync paths feeding the Sources & Attribution surface — a fast channel-level loop and a deeper per-campaign loop. They run on different cadences by design, so the channel total can be current while the drill-down is still indexing.

See Google Ads setup for the connection walkthrough and Sources & Attribution for the full lens semantics behind the channel total.

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.

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 our job queue. 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 (our primary database + our job queue 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:

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

Fourteen common questions, written so AI search engines can quote them cleanly.

Which lens should I use — Pixel, Shopify, Platform, or All?
Start on the Shopify lens for total-revenue truth (it's the same lens Shopify's own "Top marketing channels" admin page uses). Switch to the Pixel lens for per-campaign breakdowns when your URL Builder is correctly tagging every paid link with utm_source / utm_medium / utm_campaign. Switch to the Platform lens when you need numbers that match Meta Ads Manager or Google Ads UI directly — that's the number the platform's optimizer is using, so it's required for in-platform pacing decisions. Use the All lens when you're diagnosing a tracking gap and want to see all three lenses side-by-side on the same row. Pixel + Shopify will read materially lower than Platform — that's not a bug; Platform includes view-through attribution while the other two are click-based.
Why is my ROAS 0.00×?
Three most likely causes. (1) Your pixel was just installed — pre-install dates structurally return $0 because no UTM-attributed sessions exist before the snippet was on the page. Move the date range forward 24h after install and the rows populate. (2) Your URL Builder is producing inconsistent UTMs (e.g. utm_source=facebook on some ads and utm_source=Facebook on others) — the channel match key is case-sensitive, so half your revenue is being credited to one row and the spend to another. Open /url-builder, pick a canonical lowercase convention, and re-tag. (3) You're on the Pixel lens but your traffic is heavily ad-blocked or Safari-ITP'd — switch to the Shopify lens (the Shopify Custom App connection feeds Shopify's own customer-journey ledger, which doesn't depend on the pixel) and the numbers usually populate immediately.
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 our job queue. 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 rollup in our analytics warehouse accumulated duplicate rows from re-syncs of the same date range. The warehouse deduplicates asynchronously, so a re-sync of the same 14 days writes 14 new rows that sit alongside the older rows until the background merge collapses them — and the consumer query summed all of them. Fixed in 2026-05-07's deploy by reading only the latest deduplicated row at query time (or, on older warehouse 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 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 a cache lock 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).
Why does the same campaign appear twice in the drill-down?
If your Shopify journeys captured two slightly-different utm_campaign strings for the same ad — for example one with an extra trailing space, or one in mixed case — each variant produced a separate row pre-2026-05-15. As of GL#486 we normalize whitespace + case on both sides of the JOIN key before aggregating, so one campaign = one row. If you still see duplicates after that date, file a ticket with the campaign name + workspace ID and we will inspect the underlying journey rows.
Why doesn't my Google Ads drill-down show campaigns yet?
Per-campaign Google breakdowns sync daily at 04:30 UTC. If you just connected Google Ads, your campaigns will appear within 24 hours. The channel-level total is current — it uses the hourly account-level sync. See the Google sync schedule section.
Why does the date range show 'Pixel data starts {date}' instead of my selection?
When you select the Pixel lens for a date range that starts before your pixel was installed, we clamp the visible data to the pixel install date — otherwise the days before install would render as $0 and the page would look broken. Click 'Show full range' to see the structural zeros if you want, or switch to the Shopify lens to see your full historical revenue (Shopify's data goes back further than your pixel).