Documentation · Attribution · Models + Reconciliation Panel
Attribution that works at every connection level
Six attribution models, three data sources, one honest 3-way view. Pick the model that fits your funnel, connect what you have today, and let the Reconciliation Panel tell you why Meta, your pixel, and Shopify never quite agree.
The three-number problem 6 attribution models Reconciliation Panel FAQ
The three-number problem
Every DTC operator has been here. You open Meta Ads Manager and it says you spent $10,000 and made $40,000 — a clean 4x ROAS. You open your Admaxxer pixel and it says you spent $10,000 and tracked $20,000 of attributed revenue — 2x. You open Shopify Total Sales and it says you made $25,000 across all channels in the same window. Three numbers, three different stories. Which one is right?
The honest answer: all three are right, in their own coordinate system. Meta is reporting what Meta thinks Meta drove, including 7-day-click + 1-day-view conversions plus modeled (i.e., guessed) post-iOS-14.5 conversions. Your pixel is reporting what your pixel actually saw — strict client-side attribution against the visitor's UTM-tagged path. Shopify is reporting raw revenue across every channel, attributed to nobody in particular. Each number answers a different question:
- Meta-reported: "Of all conversions Meta thinks it drove, how many happened?" Includes view-through, includes modeled, attributed via Meta's internal graph.
- Pixel-tracked: "Of all conversions my pixel saw, which ones can I attribute to which UTM source?" Strict, deterministic, often the most conservative number.
- Shopify-reconciled: "What actually happened to my bank account?" The truth at the order grain, but no per-channel attribution.
Community consensus on the gap: Meta routinely overreports by 26%+ against pixel-side measurement, even before iOS 14.5. 30-60% Meta-vs-Shopify gap is normal for DTC stores spending more than $10K/mo on paid social. iOS 14.5 broke 30-50% of click-attribution — Meta backfills with modeled conversions; your pixel doesn't. The gap isn't a bug. It's structural. The right question isn't "which number is true?" but "what's the gap between them, and what does that gap tell me?"
That's what the Reconciliation Panel is built for.
6 attribution models
The active model determines how Admaxxer's pixel-side numbers split credit across the touches in a visitor's path. Time-decay (H=7d) is the recommended default for DTC stores — it's the GA4 default and matches the typical 5-21 day consideration window. Switch models from the model-pill bar at the top of /marketing-acquisition. The change re-computes the table in <500ms (Tinybird pipe params change, not a re-fetch).
| Model | Formula | Best for | Watch out for | Min orders | When to switch |
|---|---|---|---|---|---|
| Last-click | 100% credit to the touch closest to conversion. | Bottom-funnel optimization, retargeting, brand-search, anything where the last paid touch is the deciding push. | Penalizes top-of-funnel campaigns (TOF prospecting, paid social, video views) that warm the visitor weeks before checkout. | 20+ orders / mo | Switch when paid-social ROAS reads below 1.5x and you suspect the model is undercrediting prospecting. |
| First-click | 100% credit to the first touch in the visitor's journey. | TOF prospecting, audience-building, podcast / influencer campaigns where discovery is the goal. | Penalizes retargeting and brand-search; first-touch can be weeks-old and have nothing to do with the actual purchase. | 30+ orders / mo | Switch when retargeting ROAS reads below 2x and you suspect the model is overcrediting cold reach. |
| Linear (all touches) | Credit split equally across every touch in the path: 1/n per touch. | Long DTC funnels (skincare, supplements, considered-purchase apparel) where 5+ touches over 7-21 days is normal. | Treats a 30-second video view the same as a brand-search click. Position and time information is discarded. | 50+ orders / mo | Use when you want a fairness-as-default baseline before reaching for time-decay or position-based. |
| Linear (paid only) | Credit split equally across paid touches only; organic / direct touches get 0. | Pure paid-traffic operators who want platform comparisons (Meta vs Google) uncontaminated by organic. | Hides organic contribution entirely; useless for measuring brand health or content marketing. | 50+ orders / mo | Switch when comparing paid platforms head-to-head and organic is consistent enough to set aside. |
| Time-decay (H=7d) | Credit weight = pow(0.5, days_since_touch / 7). Touch from yesterday gets ~0.91; touch 7 days back gets 0.5; 14 days back gets 0.25. | Mid-funnel optimization where recency matters but you don't want to discard older awareness touches entirely. | Half-life is fixed at 7 days (GA4 default). Long DTC funnels (subscription, B2B trial) may want 14d or 30d — coming in v1.5. | 75+ orders / mo | The recommended default for DTC stores with 5-21 day consideration windows. Industry standard. |
| Position-based (40/20/40) | First touch + last touch each get 40%; middle touches share 20%. Single-touch = 100%; two-touch = 50/50; 3+ = 40/20/40. | Stores that value both discovery (first touch) AND closing (last touch) and have meaningful middle-funnel campaigns. | Middle 20% can dilute the picture when there are 5+ middle touches; consider time-decay if recency matters more than position. | 100+ orders / mo | Switch when you want to balance prospecting and retargeting credit explicitly without time-weighting. |
The two new models added in this ship (GL#379): time-decay (H=7d) and position-based (40/20/40). The four originals (last-click, first-click, linear-all, linear-paid) remain. Both new models respect the existing attribution_window_days, paid_only, new_customers_only, and platforms filters; the SQL branches in tinybird/pipes/attribution_breakdown.pipe and source_medium_breakdown.pipe use FROM ... FINAL per GL#335 (RMT FINAL discipline). Tooltips on each model pill explain "best for" + "watch out for" inline.
The Reconciliation Panel
This is the marquee feature of Phase 1. The Reconciliation Panel sits right below the AttributionDrilldownCard on /marketing-acquisition and shows you, per channel:
- Pixel-tracked conversions and revenue — what the Admaxxer pixel saw deterministically.
- Platform-reported conversions and revenue — what Meta and Google claim they drove (including iOS 14.5 modeled conversions).
- Shopify-reconciled revenue — what actually hit your bank account, attributed back to the channel via the order's UTM tuple.
- Reasoning — a pre-computed plain-English explanation of the gap.
Pre-computed reasonings include:
- "Platform claims much more than pixel — likely iOS 14.5 view-through." Meta is reporting view-through conversions your pixel can't see. Trust the platform number with a healthy skepticism (it includes modeled).
- "High direct/untagged share — likely UTM hygiene issue." A meaningful chunk of revenue is landing as Direct/None despite paid spend. Fix the UTM tags on the offending campaign — see UTM best practices.
- "Within 10% — reconciliation clean." The three sources agree closely enough that you can trust the model. Move on to optimization.
The panel is built on a single Tinybird pipe — tinybird/pipes/p_attribution_reconciliation.pipe — that does a FULL OUTER JOIN of pixel-side, platform-side, and Shopify-side rows per channel. FULL OUTER (not INNER) is critical: it means a channel that exists in only one source still shows up, with the missing columns marked as a "missing" data-coverage chip instead of silently dropping the row. That's the source-additive principle (GL#256 + GL#313) at the SQL layer.
The card UI is at client/src/components/marketing-acquisition/ReconciliationCard.tsx — premium card chrome matching AttributionDrilldownCard's vocabulary (rounded-2xl border + emerald gradient rail + framer-motion entrance + theme tokens). Mounted right below the AttributionDrilldownCard so it's the second card on the page after the model-pill bar.
The Meta first-conversion lens
One thing the Reconciliation Panel surfaces that nothing else can replicate from pixel alone: Meta's deterministic first-conversion data behind ATT and iOS 14.5. Meta operates the conversion API (CAPI) on the server side and has direct access to the deterministic match between an ad impression and a purchase event — a match that survives Apple's App Tracking Transparency framework because it never leaves Meta's infrastructure.
Concretely, the omni_purchase deduped action type from Meta's /v25.0/<ad-account-id>/insights endpoint is the canonical first-conversion count Meta itself uses internally. It's NOT what the Meta pixel would see if you installed it on your storefront — it's the Meta-internal deduped count that includes server-side matches you have no other way to access. The dailyAdSpendSync cron (GL#378) now captures this column on every level=ad pull and surfaces it in the Reconciliation Panel as the "Meta first-conversion" line within the Platform-reported column.
This is the Hyros-style match-rate signal done right: instead of forcing a 6-month server-side identity-stitching setup like Hyros does, Admaxxer pulls Meta's own first-conversion data via the same insights API every other tool already calls. Free signal, no extra integration, no customer setup work.
Source-additive philosophy
Most attribution tools force you into a binary: either a full setup before you see anything (Hyros: 6-month implementation, server-side identity stitching, custom domain) or a no-platform-data shrug (Datafast: UTM-only, doesn't ingest Meta or Google). Admaxxer is built on a third philosophy: source-additive.
Connect what you have today. See what's possible. Add more sources when ready.
- 1 source (pixel only): UTM-driven attribution + 6 models. Useful from day 1.
- 2 sources (pixel + Shopify): Add pixel-vs-Shopify revenue reconciliation. The two-column view of the Reconciliation Panel.
- 3 sources (pixel + Shopify + Meta): Unlock platform-vs-pixel comparison + Meta first-conversion data (CAPI / omni_purchase deduped match data behind ATT). Three-column reconciliation.
- 4 sources (pixel + Shopify + Meta + Google): Full Reconciliation Panel. Every gap visible, every reasoning surfaced, every channel covered.
Each source you add fills in another column. Each missing source shows a "missing" data-coverage chip instead of breaking the page. This is enforced at the code level by the source-additive guardrail canary (GL#381): every JOIN to a source-attached datasource (ad_spend_daily, shopify_reported_metrics, orders_enriched_mv, visitor_payments) must be a LEFT JOIN (or LEFT ANY / LEFT ASOF / FULL OUTER). INNER JOIN = build violation. The canary walks the pipe SQL and fails the postbuild if anyone accidentally writes INNER JOIN ad_spend_daily; sister rule to GL#368/371 (token-chain canary) and GL#256/313 (source-additive principle).
You can never get into a state where adding more data makes things worse. That's the structural promise.
How to pick your model — 4 steps
- Step 1. Connect at least one source. Open /integrations and connect what you have today. Pixel-only works (UTM-driven). Add Shopify if you have it. Meta + Google unlock platform-side data and first-conversion (Meta's deterministic match data). Source-additive: each source fills in another column in the Reconciliation Panel — missing sources show a chip, not a break.
- Step 2. Open /marketing-acquisition and look at AttributionDrilldownCard. The Channel -> campaign -> adset -> ad drill-down is the first card on /marketing-acquisition. Above it is the model-pill bar with all 6 attribution models. Below it is the Reconciliation Panel showing the 3-way gap.
- Step 3. Pick a model that matches your funnel. Default to time-decay (H=7d) — GA4 default, fits 5-21 day DTC funnels. Switch to last-click for bottom-funnel-only, first-click for TOF-heavy (podcast / influencer), linear (all touches) for long consideration, position-based (40/20/40) when you want to credit discovery + closing equally.
- Step 4. Read the Reconciliation Panel reasoning. The panel surfaces the gap between Meta-reported, pixel-tracked, and Shopify-reconciled conversions per channel. Each row carries a pre-computed 'reasoning' line: 'Platform claims much more than pixel — likely iOS 14.5 view-through', 'High direct/untagged share — likely UTM hygiene issue', or 'Within 10% — reconciliation clean'. Trust the latter; investigate the former two.
FAQ
- Why does Meta show 4x ROAS but my pixel shows 2x?
- Two different coordinate systems, both technically correct. The platform uses modeled conversions plus view-through credit and a 7-day-click + 1-day-view default window — anyone who saw your ad and purchased within 7 days counts, even if the ad wasn't the deciding touch. Pixel relies on click-attribution and iOS 14.5+ ATT broke 30-50% of those signals deterministically. Both numbers are real, both are different lookback choices. Operators on Reddit (r/PPC, r/dtcecommerce) consistently land on the same answer: stop trusting one source and look at all of them — open the Reconciliation Panel on /marketing-acquisition to see platform-reported, pixel-tracked, and Shopify-reconciled side-by-side. The gap itself is the most useful signal: under 10% means your model is clean; over 30% almost always means iOS 14.5 view-through or UTM hygiene drift. Source: BloomAnalytics + EasyInsights research covering DTC-specific Meta attribution gaps.
- What's iOS 14.5 attribution loss and how does Admaxxer handle it?
- Apple's App Tracking Transparency (ATT) framework, shipped in iOS 14.5 (April 2021), prompts every iOS user with 'Allow [App] to track your activity across other companies' apps and websites?' — and ~70-80% of users decline. That decline blocks deterministic click-attribution for ~30-50% of all DTC traffic. The platform's response was to fill the gap with modeled conversions: ML extrapolation from a small consenting cohort to the broader audience. It's controversial because it's unverifiable from the outside, and operators argue it systematically over-credits the platform vs. earned/organic. Admaxxer's first-party pixel sidesteps the third-party-cookie path entirely, click-ID persistence (GL#361, 90-day cookie/localStorage retention) preserves attribution even after the platform's third-party cookies expire, and Meta CAPI _fbc synthesis recovers most of the iOS 14.5 deterministic gap server-side. The Reconciliation Panel still shows you the platform-vs-pixel gap so you can audit transparently — we don't try to hide the loss, we just give you a deterministic floor.
- Hyros markets 'AI tracking' — does Admaxxer have something similar?
- No, and that's intentional. Hyros's 'AI' is widely treated as marketing copy by the operator community — Reddit threads on r/PPC and r/dtcecommerce repeatedly flag the 6-month server-side identity-stitching setup, opaque pricing tiers (~$799/mo entry that climbs with revenue), and the lack of a documented public methodology paper. Admaxxer competes on transparency instead of unverifiable promises: every attribution model has a published formula (see the Models table above), every API endpoint is curl-testable (see the curl examples in our model-specific docs), every Reconciliation Panel row carries a pre-computed 'reasoning' string explaining the gap, and Markov-chain attribution ships natively with the same algorithm Northbeam publishes in their methodology paper. If a competitor needs you to take their 'AI' on faith, that's a feature for them; if you want to verify the math yourself, that's the surface we built.
- Which attribution model should I pick?
- Default to time-decay (H=7d) for most DTC stores — it's the GA4 default and matches the typical 5-21 day consideration window. Switch to last-click if you optimize for bottom-funnel only, first-click if you're a top-of-funnel-heavy operator (podcast, influencer), linear-all-touches if your customers do 5+ touches over weeks, or position-based if you explicitly want to credit both discovery and closing. The Reconciliation Panel works the same way regardless of model — it always shows you the gap between sources, the model just changes how that gap is computed inside the pixel column.
- What if I haven't connected my ad accounts yet?
- Admaxxer's attribution is source-additive — it works with any subset of (pixel, Meta, Google, Shopify). With pixel-only you get UTM-driven attribution and the 6 models against pixel-tracked conversions. Add Shopify and you unlock the pixel-vs-Shopify reconciliation. Add Meta and you unlock the platform-vs-pixel comparison plus Meta's first-conversion data (Meta's deterministic match data behind ATT/iOS 14.5 that no pixel can replicate). Add Google and the same expands to Google Ads. Each source you add fills in another column in the Reconciliation Panel; missing sources show a 'data coverage' chip instead of breaking the page. Datafast skips ad-platform data entirely (UTM-only); Hyros forces a 6-month full setup before you see anything. We ramp with you.
- How does the reconciliation panel handle direct/untagged traffic?
- The panel surfaces direct/untagged traffic as its own row with a pre-computed 'reasoning' column explaining the gap. Common reasonings: 'Platform claims much more than pixel — likely iOS 14.5 view-through' (Meta is reporting view-through conversions your pixel can't see), 'High direct/untagged share — likely UTM hygiene issue' (you're missing UTM tags on a paid surface and the click is landing as direct), 'Within 10% — reconciliation clean' (the three sources agree; trust the model). Direct traffic itself isn't a problem; high direct share when you're spending heavily on paid IS a problem (likely UTM hygiene). The Reconciliation Panel surfaces the latter explicitly.
- Can I see different attribution models side-by-side?
- Yes — the AttributionTab at the top of /marketing-acquisition has a model-pill bar with all 6 models. Click any pill to switch the active model; the table below re-computes in <500ms (Tinybird pipe params change, not a re-fetch from raw rows). To compare two models side-by-side, open /marketing-acquisition in two browser tabs and pick a different model in each. A future enhancement (v1.5) will let you pin two models for in-page comparison; for now the two-tab pattern is the canonical workflow.
- How often does Meta data refresh?
- Once per day. The dailyAdSpendSync cron runs at 04:00 UTC and pulls (a) campaign + adset + ad-level insights via level=ad with the immutable campaign_id / adset_id / ad_id columns, (b) three breakdown calls (age_gender, region, placement), and (c) Meta's omni_purchase deduped first-conversion data (the deterministic match data that survives ATT). Meta itself reports its insights with a ~24-48h lag (Meta-side limitation, not Admaxxer's), so today's spend isn't fully attributable until the day-after-tomorrow's sync. The Reconciliation Panel shows a small 'last sync' timestamp on the Meta column so you know which data freshness window you're working with.
- What's 'time decay' and why H=7?
- Time-decay is an attribution model that weights each touch by how recently it happened, on an exponential curve. The formula is weight = pow(0.5, days_since_touch / H), where H is the half-life — the number of days at which a touch's credit drops to 50%. We use H=7 days because that's GA4's default and matches the 5-21 day DTC consideration window most stores see. A touch from yesterday gets pow(0.5, 1/7) ~= 0.91; a touch from 7 days ago gets exactly 0.5; from 14 days ago, 0.25; from 30 days ago, ~0.05. The key property: the credit decays smoothly with time instead of stair-stepping, so the model never has a sharp 'before/after' edge. v1.5 will expose H as a per-workspace knob for stores with longer (subscription, B2B) or shorter (impulse-buy) consideration windows.
- Does Admaxxer's attribution match TripleWhale's?
- Yes by construction in many cases, no in some. TripleWhale defaults to a 7-day-click attribution window with last-click as the model; Admaxxer's default is also 7-day-click but with time-decay (H=7d) so the two will differ on multi-touch journeys. Set Admaxxer to last-click + 7-day window and the two should match within ~5% (residual differences come from Admaxxer's pixel CV vs TW's pixel CV — different parsers, different UTM normalization). For deterministic apples-to-apples: use last-click + 7-day on Admaxxer, last-click + 7-day on TW, and they'll converge. The reason we default to time-decay is it's the more accurate model for DTC funnels (TW's last-click default is a holdover from pre-iOS-14.5 conventions). The Reconciliation Panel exposes the gap regardless of which tool you compare against — it's an honest 3-way view.