Documentation · Attribution · 3-lens drill-down
Sources & Attribution — three lenses, one defensible row per channel
Channel → campaign → adset → ad with the Pixel lens, the Shopify customer-journey lens, the Platform-reported lens, and an All synthesis lens side by side. Every cell is auditable; every gap has a plain-English reason. The marquee column is True ROAS — Shopify-attributed revenue divided by ad spend, the column Shopify's own admin can't compute and the platform's dashboard refuses to show.
Citation-friendly summary. Admaxxer ships a 4-lens attribution drill-down (Pixel + Shopify customer-journey + Platform-reported + All synthesis) on every paid plan starting at $9/month. The Shopify customer-journey lens reads Order.customerJourneySummary via Shopify Admin GraphQL — the post-checkout journey graph that includes UTM-less server-side touches Shopify itself stitched. True ROAS (Shopify-attributed revenue ÷ ad spend per cell) is included; Triple Whale gates the equivalent metric behind their $129+/month Sonar add-on, Northbeam at $1k+/month, Shopify Marketing Reports doesn't compute it at all. A coverage headline shows what percentage of revenue has a known channel versus Direct / Unknown; the platform-vs-pixel compare breaks the platform's claim into a click-only column and a separate view-through (saw-but-didn't-click) column beside your verified pixel floor; attribution windows now run 1 / 7 / 14 / 28 / 90 days plus Lifetime (the 90-day window for considered, high-AOV purchases); any order opens to a full timestamped touch path even without a store-platform connection; and an opt-in beta "Total Impact" lens blends multi-touch attribution, marketing-mix modeling, a global paid-vs-organic incrementality lift, and post-purchase surveys into one per-channel number reconciled to actual banked revenue, shown with a confidence range.
Why three lenses (not one)
Every paid-acquisition team has had this week. You open the platform's dashboard. ROAS reads 4x. You open your pixel-side analytics. ROAS reads 2x. You open Shopify's Marketing Reports. Channel revenue says something else again. Three numbers. Three different stories. Most analytics tools resolve this by picking one number and calling it canonical — and if the operator doesn't trust it, that's the operator's problem.
Admaxxer's premise is the opposite: render all three side by side, per row, with the gap explicitly auditable. The pixel lens is conservative (deterministic UTM clicks only — the number your accountant will trust). The Shopify customer-journey lens is post-checkout truth (the version Shopify itself credited the channel for). The platform-reported lens is what the platform's optimizer is using (includes view-through and modeling — inflated, but the number you must use for in-platform pacing). All three are correct in their own coordinate system. None agrees with the other two. The gap is the most useful signal — and Admaxxer is the only DTC tool at the $9/month price point that surfaces every gap with a plain-English Why drawer.
Attribution coverage — how much of your revenue has a known channel
Before you trust any attribution number, you should know how much of your revenue it can even see. The coverage headline at the top of the Sources table answers that: the share of orders and revenue that landed on a known channel (Paid Social, Paid Search, Email, Organic, Referral) versus the share that fell into Direct / Unknown — visitors who arrived with no UTM tag and no identifiable referrer, so no channel can be credited.
The Direct / Unknown dollar amount is called out explicitly — not buried in a row — because it is the single number that caps how good your attribution can get. If 40% of revenue is Direct, then at best 60% of your spend decisions rest on solid ground. Raising coverage is the highest-leverage thing most stores can do for attribution accuracy, and it is almost always a tagging or click-ID problem rather than a real "people just type our URL" problem.
Worked example — the coverage headline in practice
A store does $120,000 across 1,500 orders this month. The coverage headline reads:
| Bucket | Orders | Revenue | Share |
|---|---|---|---|
| Known channel | 1,140 | $92,400 | 77% |
| Direct / Unknown | 360 | $27,600 | 23% |
77% attribution coverage, with $27,600 sitting in Direct / Unknown. That $27,600 is the prize: every dollar you move out of Direct and onto a real channel is a dollar of budget you can now allocate with confidence. A healthy DTC store runs 80–90% coverage; below ~70% means real channels are being mislabeled as Direct.
How to improve coverage
- Tag every paid link with consistent UTMs. An ad that lands without
utm_source/utm_mediumcan't be credited to its channel — it falls into Direct. The URL builder and the per-platform templates in UTM best practices keep the tags consistent so the same campaign never splits across two spellings. - Let server-side click-ID recovery do the rest. Even when a UTM is missing, the pixel persists the platform's click-ID (gclid, fbclid, ttclid, and 9 more) for 90 days and re-attaches it to the eventual purchase server-side — so a click that lost its UTM on a redirect, or a buyer who returned days later on a different device, still resolves to the right channel instead of landing in Direct. This recovery runs automatically on every plan; see server-side tracking.
- Add a post-purchase survey for the genuinely-unattributable slice. Word-of-mouth, podcasts, and offline channels never carry a UTM or a click-ID. When real Direct stays high after tagging is clean, the post-purchase survey tells you which of that Direct is genuine word-of-mouth versus a tagging leak.
The three lenses
Each lens reads a different source of truth and is correct in its own coordinate system. The drill-down lets you toggle between them at the top of the table — same channel, same campaign, same adset, same ad row, just a different lens applied per cell.
Pixel
Deterministic UTM-driven click attribution from Admaxxer's first-party pixel.
- Source
- Admaxxer's first-party pixel — visitor sessions joined to attributed purchase events.
- Strengths
- Deterministic. You own every row. No view-through inflation. Survives ATT / iOS 14.5 because the pixel runs first-party in the merchant's domain. Click-IDs persist 90 days. Server-set cookie carries first-touch identity 365 days even on Safari.
- Weaknesses
- Misses anything that landed without a UTM. Misses purchases that happened on a different device after the pixel saw the visit. Direct/none can balloon when UTM hygiene drifts.
- Use when
- You need conservative, defensible attribution numbers — board reports, P&L lines, anything an accountant will read. Also use when comparing two ad creatives on the same channel with the same UTM scheme.
Shopify customer-journey
Shopify's own post-checkout journey graph via the Order.customerJourneySummary GraphQL field.
- Source
- Shopify Admin GraphQL: Order.customerJourneySummary { momentsCount, ready, moments { ... } }.
- Strengths
- Post-checkout truth. Deduped against the order at the order grain — no risk of attributing a non-converter. Includes UTM-less server-side touches Shopify resolved (Shop Pay autofill, Shopify Audiences network, signed-in customer device-stitch). Aligns with what your bookkeeper and Shopify Marketing Reports already show.
- Weaknesses
- 30-day journey window cap (Shopify-side limit, not configurable per-shop). momentsCount can be NULL for the first ~10 minutes after order creation while Shopify processes the journey graph. Read access is gated by the read_orders scope already granted on every install — no merchant action needed.
- Use when
- You want to know what Shopify itself credited for the order — the version your Shopify-side analytics, Shopify Marketing Reports, and Shopify Audiences pacing all use. The post-checkout view your accountant trusts.
Platform-reported
Conversion + revenue counts from each ad platform's own attribution graph.
- Source
- Meta omni_purchase deduped data via /v25.0/<ad-account-id>/insights, Google Ads conversions + conversion_value, TikTok total_complete_payment.
- Strengths
- Captures what each platform attributes to itself, including the deterministic CAPI server-match data that survives ATT. The only lens that has spend (the merchant's pixel doesn't see ad cost). The lens the platform's own optimizer is using.
- Weaknesses
- Inflates against pixel by 26%+ on average (community consensus from r/PPC + r/dtcecommerce). Includes view-through, which most operators argue over-credits the platform vs. earned/organic. Modeled conversions are unverifiable from outside the platform.
- Use when
- Comparing the platform's own optimizer to your pixel-side measurement (the gap = how much view-through inflation you have). Also use when budget pacing per-platform — you must use the same number the platform's optimizer is using.
All (synthesis)
Best-of-three per cell with a provenance chip showing which lens won.
- Source
- Computed: spend always from platform; revenue prefers Shopify, falls back to pixel, falls back to platform.
- Strengths
- One defensible number per row. Provenance chip (Sho / Pix / Plt) on every cell so the operator can audit which lens won. The Why drawer expands the per-cell decision tree.
- Weaknesses
- Requires the operator to trust the synthesis logic — that's why every cell is auditable. Not a magic number.
- Use when
- Default for the first-time user. Set workspace-wide via Settings → Defaults. Use when you want one row per channel to take into a leadership conversation, not three.
Click vs view-through vs your pixel truth
When you put the Platform-reported lens next to your Pixel lens, the platform almost always claims more revenue. Most of that gap is view-through — sales the platform credits to an ad the shopper saw but never clicked. Until now that inflation layer was bundled into one platform number. The compare now breaks the platform's claim into two separate columns so you can see the inflation, not just suspect it. Read the compare left to right, purest to most inflated:
- Platform click-only — revenue the platform credits to a real, deliberate click on the ad. The purest slice of the platform's own claim, and the part that has a click for your pixel to follow.
- Platform view-through — the platform's 1-day-view revenue, sales credited to an ad that was shown but never clicked. Modeled and inflated by construction; no click here for any other system to verify. This is the inflation layer, now its own column instead of hidden inside the total.
- Your pixel truth — what your first-party pixel deterministically followed from a real click to a purchase on your store. No views, no models — the conservative floor an accountant trusts, and the number you scale on.
Worked example — one campaign, three reads
A Paid Social retargeting campaign on a single month:
| Column | Revenue | What it means |
|---|---|---|
| Platform claim (total) | $11,400 | Everything the platform credits to itself. |
| → Click-only | $4,300 | Credited to a real click — has a click your pixel can follow. |
| → View-through | $7,100 | Saw the ad, never clicked. The inflation layer. |
| Your pixel truth | $4,100 | Deterministic click-to-purchase floor. |
The platform claims $11,400, but $7,100 of it is view-through — ads that were seen, never clicked. Strip that and the platform's click-only number ($4,300) lands within ~5% of your pixel floor ($4,100), which is the healthy outcome: once you remove the saw-but-didn't-click layer, the platform and your pixel basically agree. The view-through column is what you were always paying for and never got to see broken out.
How to use it: scale and budget on the pixel truth (or store-banked orders), not the inflated total. Use the view-through column to understand why the platform's optimizer is bidding the way it is — a campaign with a huge view-through column and a thin click-only column is winning the impression lottery, not driving incremental sales. For the structural reasons platforms over-report, see why your numbers differ.
Attribution models — in plain language
A lens decides which source of truth to read. An attribution model decides which touchpoint on the shopper’s path gets the credit for the sale. Same order, same revenue — only the credit-split rule changes. Switch the model from the dropdown above the table and every row re-allocates instantly. Most shoppers take several steps before they buy (an ad, an email, a search, a direct return visit), so the model you pick can move a channel’s reported revenue by a lot.
- Last-click — 100% of the credit goes to the final click before the purchase. The simplest, most conservative model and the default almost everywhere. Example: a shopper clicks Google → Email → Meta then buys; last-click gives the whole sale to Meta. Use it for defensible, easy-to-explain numbers.
- First-click — 100% goes to the very first click that brought the shopper in. On the same path, first-click credits Google — the channel that created the demand. Use it when judging top-of-funnel / prospecting channels.
- Linear — credit is split evenly across every touch. Three touches each get one-third; a $300 order splits $100 / $100 / $100. Use it when you believe every step mattered.
- Time-decay — touches closer to the purchase get more credit, decaying on a 7-day half-life; nothing is fully ignored. A touch yesterday outweighs one from three weeks ago, but the early touch still counts. Use it for shorter consideration cycles.
- Position-based (40/20/40) — first and last touches get 40% each; everything in the middle shares 20%. On Google → Email → Meta that’s Google 40% / Email 20% / Meta 40%. Use it when the introducer and the closer matter most but assists still count.
- Last-click (non-direct) — like last-click, but if the final touch was a Direct/untagged visit it passes credit to the last identifiable marketing touch before it. If a path ends Meta → Direct, plain last-click credits Direct while non-direct credits Meta. Use it when direct traffic is large and you don’t want bookmarks to swallow earned credit.
Worked example — one path, six different answers
A shopper’s real path to a $300 order: Google Search (day 1) → Email (day 4) → Meta retargeting (day 6, the click that closed it). Here is how each model splits that same $300:
| Model | Meta | ||
|---|---|---|---|
| Last-click | $0 | $0 | $300 |
| First-click | $300 | $0 | $0 |
| Linear | $100 | $100 | $100 |
| Time-decay | ~$60 | ~$90 | ~$150 |
| Position (40/20/40) | $120 | $60 | $120 |
Every row totals $300 — attribution never invents or loses revenue, it only re-allocates it. That is why two honest dashboards can disagree: they may be running different models on the exact same orders.
Not every model is available on every lens. The Pixel lens has the full path of touches, so it supports all of these models. The Shopify customer-journey lens supports last-click, first-click, and linear (its moments list is short and doesn’t carry precise touch timestamps, so time-decay and position-based aren’t meaningful on it). The Platform-reported lens always uses each platform’s own built-in model. For the full formulas, see the Attribution Models deep-dive.
Attribution window — now from 1 day to 90 days, plus Lifetime
A lens picks the source, a model splits the credit — and the attribution window decides how far back a touch is allowed to still get credit. If a shopper clicks an ad on day 1 and buys on day 40, a 28-day window forgets that click while a 90-day window keeps it. The window dropdown above the table now offers 1, 7, 14, 28, and 90 days, plus Lifetime (every touch on record, no cutoff).
The new 90-day window was added for considered, high-AOV purchases — furniture, mattresses, high-ticket apparel, B2B, anything with a long deliberation cycle. On those stores a 28-day cap silently drops the discovery touch that actually started the sale, so prospecting channels look weaker than they are and you under-invest in the thing filling your pipeline. Widen the window and that first touch comes back into credit.
Worked example — a 45-day consideration cycle
A $1,800 mattress buyer's path: Paid Social (day 1, discovery) → Email (day 20) → Paid Search (day 44, the click that closed it), bought day 45. On last-click:
- 28-day window: only the day-44 Paid Search touch is in range. Paid Social gets $0 — the channel that found the customer looks worthless.
- 90-day window: all three touches are in range. Paid Social is back as the discovery touch, and a multi-touch model can credit it for starting the $1,800 sale.
Same order, same revenue — the window just changes which touches are eligible. Pick a window that matches your real buying cycle: 7–14 days for impulse DTC, 28 days for mid-consideration, 90 days for high-AOV / considered purchases.
Channel → campaign → ad set → ad
The Sources table starts at the channel level — Paid Search, Paid Social, Email, Organic, Direct, and so on. Click any row to expand one level deeper. Your lens and your attribution model stay locked as you drill, so the numbers always add up: a campaign’s revenue is the sum of its ad sets, and an ad set’s revenue is the sum of its ads.
- Channel — the widest view. Spot that Paid Social is doing $42K at a 2.1x True ROAS while Paid Search is doing $18K at 4.4x.
- Campaign — expand Paid Social to see
ww-prospecting-v3,retargeting-evergreen, and the rest, each with its own spend, revenue, and ROAS. - Ad set — expand a campaign to compare audiences or targeting groups within it, so you can tell which audience is carrying the campaign and which is dragging it down.
- Ad — the deepest level: the individual creative. Find the one ad inside a winning ad set that is doing all the work, then scale it.
Depth depends on what each source can tell us. The Pixel and Platform-reported lenses go all four levels (channel → campaign → ad set → ad). The Shopify customer-journey lens reaches channel and campaign — its journey graph doesn’t carry ad-set or ad identifiers — so on that lens the deepest two levels are hidden rather than shown as zero. The page is source-additive: any lens you haven’t connected is simply hidden, and the table still works with whatever you do have.
The full journey behind any single order
Channel totals tell you which channel earned the sale. Sometimes you need to see the whole path a specific buyer took. Open any order on the Pixel lens and the journey panel expands to show the full timestamped touch path that visitor took before buying — every ad click, email open-click, organic visit, and direct return, in order, with the timestamp on each.
The decisive part: this works on your own first-party pixel, so you get the per-order journey even without a store-platform connection. Most tools can only show you a customer journey if your store platform happens to expose one. Admaxxer reconstructs it from the touches the pixel recorded itself, so a pixel-only store sees the same per-order path a fully-connected store does.
Worked example — one order, four touches
Order #10472 ($240). Click it open and the journey reads:
- Mar 2, 9:14 AM — Paid Social ad (
utm_campaign=ww-prospecting-v3) — first touch, discovery. - Mar 5, 7:02 PM — Email click (welcome-flow #2) — re-engagement.
- Mar 8, 8:41 AM — Direct visit — typed the URL, browsed, didn't buy.
- Mar 8, 8:46 AM — Purchase $240 — the order.
Now the disagreement between models is obvious for this exact order: last-click hands all $240 to that Direct return, last-click (non-direct) skips Direct and credits the Email, first-click credits the Paid Social ad that started it. You can see the reasoning instead of taking the channel total on faith.
The All synthesis lens
The All lens picks best-of-three per cell:
- Spend — always from platform (only source that has it).
- Attributed revenue — Shopify journey when
momentsCount > 0andjourney_status='ready', else pixel whenpixel_revenue > 0, else platform-reported. - Orders / conversions — Shopify journey when ready, else pixel, else platform.
- CTR / CPC / CPM / CPA — derived from the above.
Each All cell renders with a small provenance chip (Sho / Pix / Plt) so you can see at a glance which lens won. The Why drawer (next section) shows the per-cell decision tree.
The All lens is the default for first-time users; you can change the workspace-wide default via Settings → Defaults. Pixel-only workspaces see the All lens collapse to best-of-two (Pixel + Platform); the page never breaks.
Total Impact — one trustworthy number, reconciled to your real revenue (beta)
The lenses and models above show you the disagreement on purpose. But sometimes you just want one number per channel to take into a budget meeting. Total Impact is an opt-in lens that blends your four signals — multi-touch attribution, marketing-mix modeling, incrementality, and your post-purchase survey — into a single per-channel figure, reconciled so the channel totals add up to your actual revenue (no double-counting, no number larger than the bank deposited).
It is shown with a confidence range and a plain-English "how this was computed" breakdown that names which signals pushed the number up or down for each channel — so it is a defensible estimate you can audit, not a black box.
What Total Impact is — and isn't
- It's a beta. The reconciled model is labeled Beta in the product and ships with confidence ranges precisely because it is an estimate that blends modeled signals — treat the range, not just the point, as the answer.
- It's not the default. The default stays the auditable per-lens view; Total Impact is a lens you switch on when you want one consolidated number. Your raw lenses never change underneath it.
- Incrementality here is a global paid-vs-organic lift — how much of total revenue your paid activity caused overall — not a separate causal lift per channel. It calibrates the blend; it does not claim a clean incrementality figure for every single channel.
- It can't exceed your real revenue. Reconciliation is the whole point: the channel numbers are scaled to sum to what your store actually banked, so Total Impact never over-credits the way an unreconciled sum of platform claims would.
Worked example — four signals, one reconciled number
A store banked $100,000 this month. For Paid Social, the four signals disagree: multi-touch attribution credits $38k, MMM estimates a $30k contribution, the global incrementality test says paid overall drove ~70% of revenue, and the survey's "where did you hear about us" share points lower. Total Impact reconciles these into a single channel figure that, summed across every channel, equals the real $100,000:
| Channel | Total Impact | Confidence range |
|---|---|---|
| Paid Social | $33,000 | $27k – $39k |
| Paid Search | $21,000 | $18k – $24k |
| $19,000 | $16k – $22k | |
| Organic / Direct / WoM | $27,000 | $22k – $32k |
The four rows sum to $100,000 — your real revenue. The "how this was computed" note on the Paid Social row reads, in plain English: "Started from $38k multi-touch credit, trimmed toward MMM's $30k contribution and the survey's lower word-of-mouth share, then scaled with every channel so the totals reconcile to banked revenue."
For the underlying models that feed Total Impact, see the attribution models catalog, the marketing-mix model, the incrementality test, and post-purchase surveys.
True ROAS — the column nobody else shows
True ROAS = Shopify-attributed revenue ÷ ad spend per cell. It's the column nothing else in the merchant's stack can compute:
- Shopify's own admin doesn't ingest ad spend, so it can't divide by it. Shopify Marketing Reports show channel revenue but never channel ROAS.
- Meta / Google / TikTok dashboards divide their own revenue (platform-reported, view-through-inflated, model-padded) by their own spend. Their ROAS is by construction higher than reality.
- Triple Whale + Northbeam compute a similar number (TW calls it Pixel ROAS or Sonar ROAS, NB calls it Probabilistic ROAS), but both gate it behind a $129/month minimum (TW Sonar) or a $1k+/month enterprise quote (NB).
Admaxxer's True ROAS column ships on AD_STARTER ($9/month), no extra connection charge. The math is simple — Shopify-attributed revenue divided by platform spend, per (channel, campaign, adset, ad), with FULL OUTER JOIN so a campaign that exists in only one source still shows up. The join key is marketing_event_remote_id (Shopify's ID for the platform campaign), with fallback to a normalized UTM tuple when the platform doesn't surface a remote ID.
The Why drawer — auditable per-cell reasoning
Click any cell in the drill-down and the Why drawer slides over with three rows:
- Pixel saw — count of pixel-attributed conversions or revenue with the active attribution model applied (e.g. "Pixel saw $4,210 via time-decay H=7d on the visitor's UTM-tagged path").
- Shopify journey credited — Shopify-resolved attributed revenue and the moments-array source (e.g. "Shopify credited $5,830 via UTM source=facebook campaign=ww-prospecting-v3").
- Platform reported — what the platform's own attribution graph claims (e.g. "Meta reported $7,210 via 7-day-click + 1-day-view including modeled conversions").
Plus a fourth row when you're on the All lens: which lens won the synthesis and why ("Sho — momentsCount > 0 and journey_status='ready'"). Plus a pre-computed plain-English reasoning summary for the row's gap ("Platform claims much more than Shopify journey — likely view-through + modeling" / "High direct/untagged share — likely UTM hygiene issue" / "Within 10% — reconciliation clean").
Backfill + first-run delay (~24h)
When a workspace connects Shopify, Admaxxer enqueues a background backfill worker that walks the last 90 days of orders sequentially per shop using the existing read_orders access scope (the same scope that gates Order.customerJourneySummary per Shopify's GraphQL reference). Shopify Admin API rate limits are 2 req/s with a 40-call leaky bucket — concurrency >1 risks throttling, so the worker stays at concurrency=1 per shop. For a high-volume shop (5,000 orders/day × 90 days), that's ~1,800 GraphQL pages × ~1.5s wall-clock per page = ~45 minutes minimum, but the worker is concurrency-1 across the whole worker pool so a queue of 50 backfills bottoms out at ~24h.
The drill-down shows a Backfilling X% chip on the Shopify column header until the worker reports complete. Incremental updates flow through the existing Shopify webhook handler (orders/create + orders/updated); no additional cron is required after the initial backfill.
Why no extra Shopify scope is needed
Order.customerJourneySummary is gated by the existing read_orders access scope per Shopify's GraphQL reference. Admaxxer requests read_orders on every install (it is also what powers revenue, refunds, AOV, and returns elsewhere in the dashboard), so the customer-journey lens lights up the moment the 90-day backfill finishes — no merchant action, no re-authorization, no “App is requesting new permissions” dialog.
For the avoidance of doubt (GL#421, 2026-05-07): there is no read_customer_journeys scope in Shopify's canonical access-scopes table. If you have seen that name elsewhere — including in earlier drafts of this very page that promised a “Reconnect Shopify CTA” — it was a misreading of the field name; the actual scope-gate on this field has always been read_orders, and existing installs already have it.
Known limits
- 30-day Shopify journey window cap. Shopify only retains the moments array for 30 days post-order. Older orders return
customerJourneySummary { momentsCount: 0, ready: true, moments: [] }. The drill-down's date-picker honors this — selecting a window >30 days back disables the Shopify lens with an inline tooltip. - momentsCount nullable while Shopify processes. First ~10 minutes after order creation,
journey_status='processing'and the moments array is empty. The query filters these rows out of the Shopify lens; theAlllens falls back to Pixel for the affected cells. - scope_missing sentinel row (legacy code path). The query still defends the
scope_missingcase for historical reasons, but it is effectively dead code per GL#421 — the journey query is gated byread_orders(granted on every install), so the sentinel will not fire in production. See “Why no extra Shopify scope is needed” above. - Time-decay / position-based / Markov fall through to Pixel on the All lens. Shopify's moments array doesn't include touch timestamps with sufficient precision. The Why drawer explains the fallback.
- marketing_event_remote_id not always present. Platforms that don't expose a remote campaign ID fall back to a normalized UTM tuple for the join key.
Compared to alternatives
Admaxxer's 3-lens drill-down ships on every paid plan starting at $9/month. The closest equivalents either gate the post-checkout truth lens behind a $129+/month add-on, refuse to compute True ROAS, or limit the drill-down depth.
| Capability | Admaxxer | Triple Whale | Shopify Marketing Reports | Datafast |
|---|---|---|---|---|
| Pixel-side click attribution (deterministic) | Yes — first-party, 90-day click-ID, 365-day server cookie. | Yes — TW Pixel, ad-blocker workarounds via TW Sonar add-on. | Limited — Shopify pixel only, no cross-device. | Yes — first-party, UTM-driven. |
| Shopify customer-journey lens (post-checkout truth) | Yes — Order.customerJourneySummary via existing read_orders scope, 30-day window, 7 attribution models. | Indirect — Sonar attribution layer, $129+/mo add-on. | Yes — Marketing Reports (revenue only, no ROAS). | No — Datafast doesn't ingest Shopify journey data. |
| Platform-reported lens (Meta + Google + TikTok) | Yes — Meta omni_purchase deduped, Google conversions, TikTok total_complete_payment. | Yes — across the same platforms. | No — Shopify doesn't ingest platform data. | No — Datafast doesn't ingest platform data. |
| Auditable per-row Why drawer | Yes — explains which lens won each cell + plain-English gap reasoning. | No — synthesis is opaque. | No — single-source view per report. | No — single-source view per report. |
| True ROAS (Shopify-attributed revenue ÷ ad spend) | Yes — included on AD_STARTER ($9/mo). | Sonar ROAS — $129+/mo Sonar add-on. | No — Shopify doesn't ingest ad spend, so it can't divide by it. | No — Datafast doesn't ingest ad spend. |
| Drill-down depth | Channel → campaign → adset → ad. | Channel → campaign → adset → ad. | Channel → campaign (no adset / ad). | Channel → UTM source/medium (no campaign breakdown). |
| Source-additive (works with any subset of sources) | Yes — pixel-only workspaces hide unconnected lenses; the page never breaks. | Partial — minimum Pixel + Shopify required for most surfaces. | N/A — Shopify-only. | Yes — UTM-only is the design. |
| Starting price | $9 / month (AD_STARTER) — the True ROAS column included from day one. | Starts at ~$129 / month (Sonar add-on) for the equivalent attribution lens. | Free with Shopify subscription (Marketing Reports built into admin). | Starts at $19 / month. |
FAQ
- What's the difference between the Pixel lens and the Shopify customer-journey lens?
- Pixel is what Admaxxer's first-party pixel saw deterministically — UTM-driven click attribution, conservative, the most defensible number. Shopify customer-journey is what Shopify itself stitched into the order's post-checkout journey graph via the Order.customerJourneySummary GraphQL field — includes UTM-less server-side touches Shopify resolved (Shop Pay autofill, Shopify Audiences network, signed-in customer device-stitch). They will disagree on most rows; the Why drawer explains the gap per row. Pixel is the conservative truth; Shopify is the post-checkout truth your bookkeeper trusts.
- What is True ROAS and how is it different from the ROAS Meta shows me?
- True ROAS = Shopify-attributed revenue ÷ ad spend per cell. Meta's ROAS uses Meta's own revenue (platform-reported, view-through-inflated, model-padded) divided by Meta's own spend, which is by construction higher than reality. Shopify can't compute True ROAS because Shopify doesn't ingest ad spend — Shopify Marketing Reports show channel revenue but never channel ROAS. Admaxxer's True ROAS column ships on AD_STARTER ($9/mo) — the same number Triple Whale gates behind their $129+/mo Sonar add-on and Northbeam gates behind a $1k+/mo enterprise quote.
- Why does the Shopify lens show empty cells right after I get an order?
- Shopify's customerJourneySummary.ready field is false for the first ~10 minutes after order creation while Shopify computes the journey graph asynchronously. During this window momentsCount is NULL and the moments array is empty. The drill-down filters these rows out of the Shopify lens with WHERE journey_status='ready' and the All lens falls back to Pixel for the affected cells. The Shopify column lights up within 10 minutes.
- Do I need to re-authorize Shopify or grant a new scope to enable the customer-journey lens?
- No. Order.customerJourneySummary is gated by the read_orders access scope, which Admaxxer requests on every install. Shopify's GraphQL reference at https://shopify.dev/docs/api/admin-graphql/latest/objects/CustomerJourneySummary states the field requires read_orders or read_marketing_orders — there is no separate journey-specific scope in Shopify's canonical access-scopes table. Your existing connection already has the access required. Once the 90-day backfill worker runs (queue capacity-dependent, typically within ~24h of connection or a fresh deploy), the Shopify lens populates automatically — no merchant action needed.
- How does the All lens decide which lens wins per cell?
- Spend always from platform (only source that has it). Attributed revenue prefers Shopify journey when momentsCount > 0 and journey_status='ready', else falls back to pixel when pixel_revenue > 0, else falls back to platform-reported. Orders / conversions same priority. CTR / CPC / CPM / CPA derived from the above. Each cell gets a provenance chip (Sho / Pix / Plt) so you can see at a glance which lens won. The Why drawer expands the per-cell decision tree in plain English.
- Does the drill-down support all 7 attribution models?
- Pixel lens supports all 7 (last-click, first-click, linear-all, linear-paid, time-decay H=7d, position-based 40/20/40, Markov-chain). Shopify lens supports last-click, first-click, linear-all, and linear-paid (filtered to paid moments) — Shopify's moments array doesn't include touch timestamps with sufficient precision for time-decay, and the moments count rarely exceeds 3 so position-based and Markov are mathematically meaningless on this lens. Platform lens uses each platform's own model (Meta default 7d-click-1d-view, Google default last-click). The All lens delegates to whichever lens supports the active model — if you pick time-decay, All uses Pixel for that cell and the Why drawer explains.
- Why is my drill-down slow on the first load?
- The query runs FULL OUTER JOIN across three sources at the (channel, campaign_id, adset_id, ad_id) grain — for a workspace with 500+ campaigns × 5+ adsets × 5+ ads × 90 days, that's ~1.1M rows pre-aggregation. An in-memory stale-while-revalidate cache layer absorbs the cost — first hit ~3-5s, subsequent hits ~80ms. The Refresh button triggers a forced re-fetch (throttled to 1 click / 10s).
- Can I use the drill-down without connecting Shopify?
- Yes. The page is source-additive — a pixel-only workspace sees the Pixel and Platform lenses (Shopify column hidden), the All lens collapses to best-of-two, and the page never breaks. Connecting Shopify unlocks the third lens and the True ROAS column on the same UI; nothing else changes. This is the same source-additive principle the rest of Admaxxer follows: 1 source = useful, more sources = better fidelity, missing sources = chip not break.
- What is an attribution model, in plain language?
- Most shoppers take several steps before they buy — maybe an ad, then an email, then a return visit. An attribution model is the rule that decides which of those steps gets credit for the sale. Last-click gives 100% to the final step; first-click gives 100% to the step that started it; linear splits credit evenly; time-decay favors the most recent steps (7-day half-life); position-based gives 40% to the first step, 40% to the last, and 20% to the middle; last-click (non-direct) is last-click but skips a Direct/untagged final touch and credits the last real marketing touch instead. Same order, same revenue — only how the credit is split changes. The drill-down lets you switch models from a dropdown above the table and re-allocates every row instantly.
- Why do two dashboards show different revenue for the same channel?
- Two honest dashboards can disagree for two reasons. First, they may be reading different sources of truth (a lens) — your pixel, Shopify's customer-journey graph, or the ad platform's own count. Second, they may be applying different attribution models to the exact same orders — e.g. one on last-click and one on linear. Attribution never invents or loses revenue; it only re-allocates a fixed total across the touches on the path. A $300 order is always $300 — last-click might hand it all to Meta while linear splits it three ways. Admaxxer shows every lens side by side and lets you pick the model, so the gap is explicit rather than a mystery.
- What is attribution coverage, and why does it matter?
- Attribution coverage is the share of your orders and revenue that landed on a known channel (Paid Social, Paid Search, Email, Organic, Referral) versus Direct / Unknown — visitors who arrived with no UTM tag and no identifiable referrer. The coverage headline at the top of the Sources table shows this percentage and calls out the Direct / Unknown dollar amount explicitly, because that number caps how good your attribution can get: if 40% of revenue is Direct, only 60% of your budget decisions rest on solid ground. A healthy DTC store runs 80–90% coverage. You improve it three ways: tag every paid link with consistent UTMs (via the URL builder), let server-side click-ID recovery re-attach lost clicks for 90 days, and use a post-purchase survey for the genuinely-unattributable word-of-mouth slice.
- What is the view-through column in the platform-vs-pixel compare?
- When you put the Platform-reported lens next to your Pixel lens, the platform almost always claims more revenue. The compare now breaks the platform's claim into two separate columns: click-only (revenue credited to a real, deliberate click — the purest slice, and the part your pixel can verify) and view-through (the platform's 1-day-view revenue — sales credited to an ad the shopper saw but never clicked, modeled and inflated by construction). Your pixel truth sits beside them as the conservative click-to-purchase floor. Read it click (purest) → view-through (modeled/inflated) → pixel (truth). Example: a platform claims $11,400, of which $7,100 is view-through; strip that and the click-only $4,300 lands within ~5% of the $4,100 pixel floor. Scale on the pixel truth, not the inflated total; use the view-through column to understand why the platform's optimizer bids the way it does.
- Can I see the full journey for a single order?
- Yes. Open any order on the Pixel lens and the journey panel shows the full timestamped touch path that visitor took before buying — every ad click, email click, organic visit, and direct return, in order, each with its timestamp. It's reconstructed from your own first-party pixel, so you get the per-order journey even without a store-platform connection — a pixel-only store sees the same per-order path a fully-connected store does. Seeing the path makes the difference between attribution models obvious for that exact order: last-click might credit a Direct return visit, last-click (non-direct) skips it to the prior Email, first-click credits the ad that started the path.
- Why does Admaxxer now offer a 90-day attribution window?
- The window dropdown above the table offers 1, 7, 14, 28, and 90 days, plus Lifetime. The 90-day window was added for considered, high-AOV purchases — furniture, mattresses, high-ticket apparel, B2B — where deliberation runs longer than a month. On those stores a 28-day cap silently drops the discovery touch that started the sale, so prospecting channels look weaker than they are. Example: a $1,800 mattress buyer clicks Paid Social on day 1 and closes on Paid Search day 44 — a 28-day window credits Paid Social $0, a 90-day window brings that discovery touch back into credit. Pick a window that matches your real buying cycle: 7–14 days for impulse DTC, 28 for mid-consideration, 90 for high-AOV / considered purchases.
- What is the Total Impact model, and should I trust it?
- Total Impact is an opt-in (beta) lens that blends four signals — multi-touch attribution, marketing-mix modeling, incrementality, and your post-purchase survey — into a single per-channel number, reconciled so the channel totals add up to your actual banked revenue (no double-counting, never larger than the bank deposited). It's shown with a confidence range and a plain-English 'how this was computed' breakdown, so it's an auditable estimate, not a black box. Three honest caveats: it's labeled Beta because it blends modeled signals (treat the range, not just the point, as the answer); it's not the default (your raw auditable per-lens view stays the default — Total Impact is a lens you switch on); and the incrementality input is a global paid-vs-organic lift, not a clean causal figure for every single channel. Use it for one consolidated number in a budget meeting; use the per-lens view to audit any disagreement.