Stripe tells you how much MRR you have. It cannot tell you where it came from. The complete 2026 guide to joining Stripe payments to marketing channels, with webhook code, model trade-offs, and an honest tool comparison.
A founder messaged me in March asking for help with a board deck. He had $112k in MRR through Stripe, a clean Stripe Dashboard, a Sigma report showing his plan mix, and a slide he could not fill in: revenue by acquisition channel. His VP of Marketing had been claiming credit for the ChatGPT pipeline. His ad agency had been claiming credit for the LinkedIn pipeline. His content lead had been claiming credit for the SEO pipeline. The board wanted to know which one was right. Stripe could not answer the question. Neither could GA4 (different reasons, also broken). Neither could his BI tool (correctly joined the wrong fields). The deck shipped with a slide that said "directional, multi-touch" and nobody believed it.
This piece is the long-form playbook for the question that founder could not answer. Where did your Stripe revenue actually come from, and how do you build a measurement layer that survives the next board meeting. The architecture is not exotic and the code is not long. The hard part is the discipline of writing attribution into Stripe metadata at the right lifecycle points, joining the right webhooks at the right time, and picking a model honest enough to defend in a room full of skeptics.
The companion pieces: the ChatGPT referral analytics guide covers the upstream measurement layer for AI traffic specifically, the AEO vs SEO piece frames the strategic split between classic SEO and answer-engine optimization, and does GEO actually drive revenue covers the layered evidence framework for AI-driven channels. This piece is the Stripe-side mechanics: webhooks, models, metadata, joins.
Quick Facts
Metric
Value
Source
Stripe processed payment volume (FY2024)
Roughly $1.4 trillion
Stripe annual letter [1]
Stripe webhook event types available
200+
Stripe webhook docs [2]
Webhook events that matter for attribution
3 core, ~6 with refunds/churn
Author's analysis
Stripe Sigma availability
All paid Stripe accounts
Stripe Sigma docs [3]
Stripe Customer metadata key limit
50 keys per object, 500 chars per value
Stripe API reference [4]
Median time from trial start to paid conversion (B2B SaaS, 2025)
11-18 days
OpenView SaaS benchmarks [5]
Median trial-to-paid conversion rate (B2B SaaS, 2025)
14-23%
ChartMogul benchmark [6]
% of SMB SaaS using GA4 as primary attribution
Roughly 71%
Baremetrics blog survey [7]
% of those who can name their top revenue-driving channel correctly
Roughly 38%
Author's audit, n=42
SegMetrics entry price (2026)
$175/mo Essentials
SegMetrics pricing page [8]
Wicked Reports entry price (2026)
$249/mo Foundation
Wicked Reports pricing [9]
TripleWhale entry price (2026)
$129/mo Growth (Shopify-first)
TripleWhale pricing [10]
Polar Analytics entry price (2026)
$399/mo Standard
Polar pricing page [11]
Baremetrics entry price (2026)
$129/mo Metrics
Baremetrics pricing [12]
Northbeam entry price (2026)
$1,000+/mo (enterprise-only quote)
G2 listing [13]
Attrifast entry price (2026)
$29/mo
Attrifast pricing
Median ROI of attribution investment, per McKinsey
15-20% lift in marketing efficiency
McKinsey on marketing analytics [14]
Two numbers do most of the work. The 200+ Stripe webhook event types means you have full visibility into the financial side of every customer's lifecycle. The 38% of operators who can correctly name their top channel means almost nobody is using that visibility for attribution today. The gap is structural, not technical.
Why Stripe alone cannot tell you where revenue came from
The Stripe object model is exhaustive on the money side and silent on the upstream marketing side, by design. Every primary object (Customer, Subscription, Invoice, Charge, PaymentIntent, Checkout Session) carries a metadata field that is an open key-value bag with a 50-key limit and 500-char-per-value limit [4]. Stripe will store whatever you write there. Stripe will not write attribution data on your behalf because Stripe has no visibility into the upstream session that brought the buyer to the form.
The mental model that sticks for most engineers I work with: Stripe is a perfect ledger of what happened to the money once the buyer reached the form. Attribution is the entirely separate question of how the buyer reached the form, and that question is answered by your analytics layer, not your payments layer.
Here is the side-by-side of what Stripe knows about a typical paying customer versus what you need to know for attribution:
Dimension
Stripe knows it natively?
Where it lives
What you need to add
Customer email
Yes
Customer.email
Nothing
Plan and price
Yes
Subscription.items[].price.id
Nothing
Currency, country, tax
Yes
Customer.address, Invoice.tax
Nothing
MRR per customer
Computable
Subscription.items[].price.unit_amount
Nothing
Lifetime value
Computable
Sum of Invoice.amount_paid
Nothing
Refunds, disputes, chargebacks
Yes
Charge.refunded, Dispute
Nothing
First-touch marketing channel
No
None
Must write to metadata yourself
Last-touch marketing channel
No
None
Must write to metadata yourself
UTM source/medium/campaign
No
None
Must capture upstream and pass through
Landing page
No
None
Must capture upstream and pass through
Referrer
No
None
Must capture upstream and pass through
Session duration before purchase
No
None
Must capture upstream
Number of pageviews before purchase
No
None
Must capture upstream
AI engine source (ChatGPT, Perplexity, etc.)
No
None
Must capture upstream via referer fingerprinting
Ad campaign ID (Meta, Google, LinkedIn)
No
None
Must capture from URL params, pass through metadata
Coupon attribution
Partial
Customer.discount.coupon.id
Useful as a proxy for some campaigns
Affiliate / partner attribution
No
None
Must implement via metadata convention
Sales rep attribution
No
None
Manual write at customer.created
Trial source (organic vs paid)
No
None
Must capture upstream
Activation channel (different from acquisition)
No
None
Custom metric, must derive
The bottom 13 rows are the ones that move budget. Every single one is "must write to metadata yourself" or "must capture upstream and pass through." Stripe does the financial half perfectly and assumes you bring the marketing half. If you do not bring it, your Stripe data has no marketing context and your channel mix is invisible.
A related confusion worth clearing up: Stripe Atlas occasionally publishes content about marketing attribution [15], but the Atlas content is general SMB founder guidance, not a Stripe product feature. There is no "Stripe Attribution" product. The closest official Stripe primitive for this work is Sigma, which is a reporting layer over the data Stripe already has, plus whatever you have written into metadata. Sigma is excellent. Sigma is also not a capture layer, and the capture layer is the work.
Stripe primitive
What it does
Helpful for attribution?
Dashboard
Manual UI for inspecting customers, charges
Read-only, not built for channel analysis
Reports (in Dashboard)
Pre-built financial reports
Pre-built, no attribution dimension
Sigma
SQL over Stripe data
Yes if metadata is populated; useless otherwise
Data Pipeline
Streams Stripe data to your warehouse
Building block, requires custom ETL
Workflows
Visual automation
Can write to metadata; not a capture engine
Customer Portal
Self-serve subscription UI
Outside attribution scope
Apps
Stripe-built integrations
Marketplace varies; few attribution apps
Webhooks
Real-time event stream
Yes, this is where capture happens
Metadata fields
Open key-value storage
The destination for attribution data
The right read of that table: Stripe gives you all the primitives you need to do attribution well. It does not assemble them for you, and the assembly is the work.
The three Stripe webhook events that matter for attribution
Stripe ships 200+ event types [2]. Three of them carry 95% of the load for SaaS and ecommerce attribution. Two more handle the edge cases. The rest you can ignore for attribution purposes (they are still useful for other operations).
The core three
Event 1: checkout.session.completed. Fires when a Stripe Checkout Session converts. This is the moment a buyer transitions from anonymous visitor to identified Customer (or, for an existing Customer, completes a new subscription). The session object carries the metadata field you set at create time, so this is the cleanest moment to read your first-party identifier and write the attribution fields into Customer and Subscription metadata. For ecommerce sites, this is also the moment to record the order-level channel attribution since the conversion event and the order are the same event.
Event 2: customer.subscription.created. Fires when a Customer starts a new Subscription. For SaaS with trials, this often fires later than checkout.session.completed (at trial start), and customer.subscription.updated fires at the trial-to-paid conversion. For non-trial SaaS this fires alongside checkout completion. The reason to handle this separately is that activation-time attribution can differ from signup-time attribution: a buyer who signs up via SEO but activates after an email nurture should arguably have both channels recorded.
Event 3: invoice.payment_succeeded. Fires on every successful payment, including recurring renewals. This is the event you accumulate against to produce MRR-by-channel over time. Without this event, you have first-month revenue attribution; with it, you have the lifetime-revenue attribution that lets you compute LTV by channel.
Event 4: charge.refunded. For ecommerce attribution, refund rates vary enormously by channel. Paid social drives higher refund rates than organic search at most DTC sites; ignoring refunds inflates paid social ROI by 10-30%. Subtracting refunded amount from the original channel's revenue gives a more honest channel ROI.
Event 5: customer.subscription.deleted / customer.subscription.updated. For SaaS, churn-by-channel is the second-most-important question after acquisition-by-channel. The combination of these two events lets you compute net-MRR-by-channel, which is the metric that actually moves budget decisions long-term.
A full webhook event map for attribution
Webhook event
Frequency at typical SMB SaaS
Required for basic attribution?
Required for advanced attribution?
checkout.session.completed
High (every conversion)
Yes
Yes
customer.subscription.created
Medium
Recommended
Yes
invoice.payment_succeeded
Highest (every renewal)
Yes (for MRR over time)
Yes
customer.created
Medium
Optional
Yes (for first-touch lock-in)
customer.subscription.updated
Medium
Optional
Yes (for expansion attribution)
customer.subscription.deleted
Low
Optional
Yes (for churn-by-channel)
charge.refunded
Low-medium
Optional
Yes (for ecommerce net-rev)
invoice.payment_failed
Low-medium
No
Useful (for dunning attribution)
payment_intent.succeeded
Variable
Yes if not using Checkout
Yes
payout.paid
Daily/weekly
No
Useful for cash-basis reconciliation
For a brand-new SaaS shipping attribution from scratch, the right starter set is: checkout.session.completed, customer.subscription.created, invoice.payment_succeeded. Ship those three idempotent handlers, write first-touch and last-touch into Customer metadata, and you have a working revenue-by-channel layer within a week. Add the others when the business reaches the scale where churn and refund segmentation matters.
A worked example: Node.js webhook handler that writes attribution
The code below is the minimal idempotent handler I ship as the starting template at Attrifast customers. Express + Node.js, but the shape ports cleanly to Fastify, Hono, Next.js API routes, FastAPI, or Rails.
// webhooks/stripe.js — minimal Stripe attribution capture handler
import express from 'express'
import Stripe from 'stripe'
import { db } from '../db.js'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET
export const router = express.Router()
router.post(
'/webhooks/stripe',
express.raw({ type: 'application/json' }),
async (req, res) => {
let event
try {
event = stripe.webhooks.constructEvent(
req.body,
req.headers['stripe-signature'],
endpointSecret,
)
} catch (err) {
return res.status(400).send(`Webhook signature failed: ${err.message}`)
}
// Idempotency: skip if we have already processed this event
const alreadyProcessed = await db.processedEvents.findById(event.id)
if (alreadyProcessed) return res.status(200).send('ok-duplicate')
try {
switch (event.type) {
case 'checkout.session.completed':
await handleCheckoutCompleted(event.data.object)
break
case 'customer.subscription.created':
await handleSubscriptionCreated(event.data.object)
break
case 'invoice.payment_succeeded':
await handleInvoicePaid(event.data.object)
break
case 'customer.created':
await handleCustomerCreated(event.data.object)
break
}
await db.processedEvents.insert({ id: event.id, processedAt: new Date() })
res.status(200).send('ok')
} catch (err) {
// Stripe will retry on 5xx, so we surface the failure
console.error('webhook handler error', err)
res.status(500).send('handler-failed')
}
},
)
async function handleCheckoutCompleted(session) {
// 1. Pull the first-party identifier from session metadata (we set it on create)
const firstPartyId = session.metadata?.first_party_id
if (!firstPartyId) return // pre-attribution checkout, no upstream session
// 2. Look up the upstream session row written by the tracker
const upstreamSession = await db.sessions.findFirstPartyId(firstPartyId)
if (!upstreamSession) return
// 3. Compute first-touch and last-non-direct touch
const touches = await db.touches.findByFirstPartyId(firstPartyId)
const firstTouch = touches[0]
const lastNonDirectTouch =
[...touches].reverse().find((t) => t.channel !== 'direct') ?? firstTouch
// 4. Write attribution into Customer metadata (immutable forever)
if (session.customer) {
await stripe.customers.update(session.customer, {
metadata: {
attr_first_channel: firstTouch.channel,
attr_first_source: firstTouch.source ?? '',
attr_first_campaign: firstTouch.campaign ?? '',
attr_first_at: firstTouch.occurredAt.toISOString(),
attr_last_channel: lastNonDirectTouch.channel,
attr_last_source: lastNonDirectTouch.source ?? '',
attr_last_campaign: lastNonDirectTouch.campaign ?? '',
attr_last_at: lastNonDirectTouch.occurredAt.toISOString(),
},
})
}
// 5. Also stamp the conversion row in our own DB for fast joins later
await db.conversions.insert({
stripeCustomerId: session.customer,
stripeSessionId: session.id,
firstPartyId,
firstChannel: firstTouch.channel,
lastNonDirectChannel: lastNonDirectTouch.channel,
amountPaid: session.amount_total,
currency: session.currency,
occurredAt: new Date(session.created * 1000),
})
}
async function handleSubscriptionCreated(subscription) {
// Capture activation-time attribution distinct from signup-time
const customer = await stripe.customers.retrieve(subscription.customer)
const firstChannel = customer.metadata?.attr_first_channel ?? 'unknown'
await stripe.subscriptions.update(subscription.id, {
metadata: {
attr_activation_channel: firstChannel,
attr_activation_at: new Date().toISOString(),
},
})
}
async function handleInvoicePaid(invoice) {
// Accumulate MRR-by-channel for over-time reporting
const customer = await stripe.customers.retrieve(invoice.customer)
const channel = customer.metadata?.attr_last_channel ?? 'unknown'
await db.mrrEvents.insert({
stripeCustomerId: invoice.customer,
stripeInvoiceId: invoice.id,
channel,
amount: invoice.amount_paid,
currency: invoice.currency,
billingReason: invoice.billing_reason,
occurredAt: new Date(invoice.created * 1000),
})
}
async function handleCustomerCreated(customer) {
// No-op for most cases; useful if you want to lock the first-touch immediately
// and not wait for checkout.session.completed
}
A few notes on the code that matter for correctness in production. The handler-correctness checklist I run before any Stripe webhook integration ships to prod:
Check
Why it matters
Common failure
Signature verification on every request
Without it, attribution data is forgeable by anyone with your URL
Skipped during local testing, never re-enabled
Raw body used for signature, not parsed JSON
Stripe signs the raw bytes
Body parsed by Express middleware before reaching verifier
Idempotency table keyed on event.id
Stripe retries on 5xx and timeouts
Handler treats each retry as new event
Handler completes within 30s
Stripe times out and retries at 30s
Long-running sync work in the handler
Returns 200 quickly for unhandled event types
Avoids unnecessary retries
Returns 500 for events you don't care about
Wraps Stripe API calls in retry-on-rate-limit
Stripe API can rate-limit at burst
Single failed update silently drops attribution
Logs the event ID and type on entry
Debugging requires the event ID
Logs without IDs are unparseable
Stores raw event payload for replay
Lets you reprocess after handler bugs
Replay impossible if payload is gone
First-touch is write-once, not overwrite
Customer.metadata is mutable from Stripe's side
Last touch silently replaces first touch on update
Test mode and live mode webhooks routed separately
Mixing modes corrupts attribution
Single endpoint handles both, attribution leaks across
Signature verification is non-negotiable. Stripe's webhook security docs [16] are explicit on this. Without verification, anyone who knows your endpoint URL can forge attribution data into your customer records. Use the raw body, not the JSON-parsed body, for verification.
Idempotency matters because Stripe retries. Stripe sends webhooks at least once, not exactly once. A 500 response triggers retries with exponential backoff for up to 3 days. If your handler is not idempotent, a retry on a partially-processed event will double-write attribution. The pattern in the code (insert into processed_events table after success, check on entry) is the simplest reliable form.
Customer metadata is immutable from your application code's perspective, not Stripe's. Stripe will let you overwrite metadata on every webhook. The discipline of writing first-touch only once (and protecting it on subsequent writes) is your application's job. I check customer.metadata.attr_first_channel before overwriting in production code; the snippet above is simplified.
The 50-key, 500-char limit on metadata is real and you will hit it if you store too much.Stripe's metadata reference [4] is explicit. Store the channel summary in Stripe metadata; keep the full touch history in your own database. Stripe metadata is the join key, not the warehouse.
A worked SQL example: revenue by channel from your Stripe + session join
Once attribution is written into Customer metadata and your session rows are stored, the canonical revenue-by-channel query is a straightforward join. Below in PostgreSQL syntax assuming a stripe_customers mirror table (sourced via Stripe Data Pipeline or your own backfill) and your own sessions and mrr_events tables.
-- Revenue by acquisition channel, current quarter, last-non-direct attribution
SELECT
sc.metadata->>'attr_last_channel' AS channel,
COUNT(DISTINCT sc.id) AS paying_customers,
SUM(me.amount) / 100.0 AS revenue_usd,
SUM(me.amount) / 100.0 / NULLIF(COUNT(DISTINCT sc.id), 0) AS arpu_usd
FROM stripe_customers sc
JOIN mrr_events me ON me.stripe_customer_id = sc.id
WHERE me.occurred_at >= date_trunc('quarter', now())
AND me.occurred_at < date_trunc('quarter', now()) + INTERVAL '3 months'
GROUP BY 1
ORDER BY revenue_usd DESC;
The same shape with first-touch attribution swapped in is a single column change. Sigma syntax differs slightly but the pattern is identical.
The two queries side by side often produce dramatically different channel rankings:
Channel
Last-non-direct revenue
First-touch revenue
Delta
Google organic
$42,180
$31,440
-$10,740
ChatGPT / AI
$14,180
$19,840
+$5,660
Email
$11,820
$4,200
-$7,620
Paid social
$9,310
$8,640
-$670
Direct
$0 (excluded)
$11,180
+$11,180
Referral
$4,210
$3,920
-$290
Affiliate
$2,140
$2,440
+$300
Total
$83,840
$81,660
--
(Totals differ slightly because first-touch attributes some revenue to channels for which the last touch was excluded; last-non-direct re-routes those dollars to the prior non-direct touch.)
The pattern in that table is the canonical SaaS shape: ChatGPT and Direct rank higher on first-touch (they are discovery channels); Email and Google ranks higher on last-non-direct (they are conversion channels). The right channel-budget question is not "which column is right" but "are you trying to optimize for discovery or conversion." Most teams want both columns visible.
A complementary query that often surfaces actionable findings: channel-by-plan-tier, to see whether your acquisition channels map onto your revenue tiers cleanly.
Channel
Starter ($29/mo)
Pro ($79/mo)
Business ($199/mo)
Enterprise (custom)
Weighted ARPU
Google organic
142
64
18
2
$61
ChatGPT
38
24
8
1
$69
Perplexity
6
9
5
1
$98
Email
84
31
7
0
$52
Paid social
71
22
4
0
$46
Referral / partner
12
18
12
3
$112
Affiliate
22
6
1
0
$42
Direct
38
12
4
1
$58
The most actionable finding hides in this table: referrals and Perplexity skew dramatically toward higher-tier plans, while affiliates and paid social skew toward Starter. ARPU-by-channel is a fundamentally different question than revenue-by-channel; a channel that drives high-volume low-ARPU signups can look great in the headline number and terrible in the CAC payback math.
The attribution model question: first-touch vs last-touch for SaaS
The model debate has been running for two decades and the framing has barely changed: every model has a structural blind spot, no single model captures the truth, and the right answer for a given business depends on how long the funnel is and what decision the model is being used to support. The honest short version for SaaS in 2026:
Model
What it credits
Strength
Blind spot
First-touch
The channel that started the journey
Captures discovery; rewards content and SEO
Ignores the channel that actually drove conversion; over-rewards top-of-funnel
Last-touch
The channel of the final session before conversion
Captures conversion; rewards email and paid ads
Over-credits direct re-visits and brand recall; ignores discovery
Last-non-direct touch
The last non-Direct channel before conversion
Best balance for most SaaS; preserves AI/organic signal without over-crediting bookmarks
Still ignores middle-of-funnel touches
Linear
Equal credit to every touch
Honest about multi-touch; no channel over-rewarded
Equal-credit is itself an assumption; rewards channels with high touch count
Time-decay
More credit to recent touches
Captures conversion bias without ignoring discovery entirely
Decay rate is arbitrary; small change shifts rankings
Position-based (U-shaped)
40% first, 40% last, 20% middle
Pragmatic compromise; captures both endpoints
Middle channels under-credited
Data-driven (Markov, Shapley)
Computed by algorithm
Theoretically best; rewards real causality
Requires high data volume; opaque; rankings shift with re-training
For most bootstrapped SaaS in 2026, the practical default I recommend is last-non-direct touch with a first-touch override column kept alongside it. The two columns answer different questions, and a dashboard that pivots on either is more useful than a single number that pretends to be the truth.
The decision tree I use in customer onboarding:
The pattern: there is no universally correct model; there is the model that supports the decision honestly. A single SaaS dashboard usually needs two or three models visible side by side, with the conventions documented so the team is not arguing about whether email is over- or under-counted in any given week.
A worked example from a real Attrifast customer, with the channels and dollars anonymized but the shape preserved:
Channel
Sessions (qtr)
First-touch revenue
Last-touch revenue
Last-non-direct revenue
Linear revenue
Google organic
48,200
$58,400
$34,210
$51,820
$46,180
ChatGPT
9,180
$24,710
$8,420
$19,310
$17,610
Perplexity
2,140
$11,820
$2,140
$9,420
$7,820
Direct
12,400
$4,210
$44,180
$0 (excluded)
$14,210
Email
8,420
$3,840
$28,140
$25,820
$18,420
Paid social
14,200
$14,210
$11,820
$13,640
$12,820
Affiliate
1,840
$2,820
$4,210
$4,180
$3,840
Total
--
$120,010
$133,120
$124,190
$120,900
A few things to read out of that table.
Direct dominates last-touch. $44,180 of $133,120 is 33% of last-touch revenue. Almost all of it is buyers who first encountered the brand elsewhere, then later came back via bookmark or direct URL. Crediting Direct with that revenue is technically accurate and operationally useless; you cannot buy more Direct.
ChatGPT and Perplexity dominate first-touch relative to last-touch. ChatGPT's first-touch ($24,710) is 2.9x its last-touch ($8,420). Perplexity's is 5.5x. This is the canonical AI-channel shape: AI is a discovery surface, not a conversion surface. If you use last-touch and only last-touch, AI looks 3-5x smaller than it actually is.
Email is the inverse pattern. Email's last-touch ($28,140) is 7.3x its first-touch ($3,840). Email rarely starts the journey; it almost always closes one. Last-touch over-credits email; first-touch under-credits it; last-non-direct gives the honest split.
Linear and last-non-direct produce similar totals at this scale. The ranking differs (linear gives more to email and paid social), but the order-of-magnitude is the same. For SMB SaaS at this scale, the operational difference between linear and last-non-direct is usually smaller than the noise in any individual channel's measurement error.
Setting up Stripe-to-attribution in four steps
The architecture below is the one I deploy at every Attrifast customer and the one I built into the product itself. It is also the right architecture if you are rolling your own; the components are commodity and the integration is two days of work for a competent backend engineer.
The four steps mapped to ownership, tools, and gotchas:
Step
Owner
Primary tool
Common gotcha
Establish first-party identifier
Front-end engineer
localStorage / first-party cookie
Cleared by browser extensions, breaks identity
Capture session and touch data
Back-end engineer
Your own API + DB
UTM tag inconsistency across teams pollutes channel data
Pass ID into Checkout Session
Full-stack
Stripe Checkout SDK
ID dropped if checkout opened in new tab without referer state
Webhook handler writes attribution
Back-end engineer
Stripe webhooks
Non-idempotent handler double-writes on retry
Step 1: Establish a first-party identifier on the visitor
A UUID generated on the first visit, stored in localStorage (or a first-party cookie if you need cross-tab persistence with longer-than-session lifetime), and attached to every analytics event from that visitor. This is the join key that everything else hangs off.
// tracker.js — first-party identifier setup
function getOrCreateFirstPartyId() {
let id = localStorage.getItem('_afpid')
if (!id) {
id = crypto.randomUUID()
localStorage.setItem('_afpid', id)
}
return id
}
This is functionally similar to a login session cookie: scoped to your own domain, not a third-party cross-site cookie, and not subject to ITP or ePrivacy cross-site cookie rules. Consent banners are not required for this purpose under most jurisdictions, but verify with your privacy review.
A reference channel-mapping table covering the common URL parameters and referer patterns the inferred_channel column should resolve to:
Signal
Pattern
Inferred channel
Notes
utm_source=google&utm_medium=cpc
UTM
paid-search
Google Ads click
utm_source=google&utm_medium=organic
UTM
organic-search
Manual organic tag (rare)
gclid=...
URL param
paid-search
Google Click ID
fbclid=...
URL param
paid-social
Facebook/Meta Click ID
li_fat_id=...
URL param
paid-social
LinkedIn campaign ID
ttclid=...
URL param
paid-social
TikTok Click ID
msclkid=...
URL param
paid-search
Microsoft Ads Click ID
Referer google.com (no UTM)
Referer
organic-search
Google SERP click
Referer bing.com
Referer
organic-search
Bing organic
Referer chatgpt.com / chat.openai.com
Referer
ai-search
ChatGPT citation
Referer perplexity.ai
Referer
ai-search
Perplexity citation
Referer claude.ai
Referer
ai-search
Claude citation
Referer gemini.google.com
Referer
ai-search
Gemini citation
Referer linkedin.com
Referer
social
LinkedIn organic
Referer t.co / x.com / twitter.com
Referer
social
X / Twitter
Referer reddit.com
Referer
social-community
Reddit
Referer news.ycombinator.com
Referer
social-community
Hacker News
Referer producthunt.com
Referer
social-community
Product Hunt
utm_medium=email
UTM
email
Email campaign
Referer mail.google.com / outlook.live.com
Referer
email (webmail)
Webmail client
No referer, no UTM, root URL
Empty
direct
True direct or bookmark
No referer, deep URL, FAQ-shaped page
Behavioral
suspected-ai
Likely AI citation click
Step 2: Capture session and touch data on your server
Every pageview and every interesting event fires a beacon to your server. The server writes a row to a sessions table (one per first-party id per visit) and to a touches table (one per channel-bearing event). The minimal session schema:
Column
Type
Notes
id
UUID
Primary key
first_party_id
UUID
Join key from the tracker
started_at
timestamptz
First event in session
ended_at
timestamptz
Last event in session (rolling)
utm_source
text
From URL params, nullable
utm_medium
text
From URL params, nullable
utm_campaign
text
From URL params, nullable
utm_content
text
From URL params, nullable
utm_term
text
From URL params, nullable
referer
text
document.referrer or HTTP Referer
referer_host
text
Parsed hostname for fast filtering
landing_page
text
First URL in session
user_agent
text
For bot exclusion
ip_country
text
Geo-IP at session start (no IP stored)
device_type
text
mobile/desktop/tablet
inferred_channel
text
Computed at write time (organic/paid/ai/email/direct)
The inferred_channel column is the precomputed mapping from utm_source, referer_host, and behavioral fingerprinting onto your channel taxonomy. Storing it at write time (rather than computing on read) saves a lot of dashboard latency.
Step 3: Pass the first-party ID into the Stripe Checkout Session
When you create the Stripe Checkout Session, include the first-party identifier in the session metadata. This is the bridge from your tracker world to Stripe's world.
The metadata.first_party_id field is the canonical carrier; client_reference_id is a Stripe-native field that exists for exactly this purpose [17] and shows up in slightly more places in the dashboard, so I write to both for redundancy. Either alone is sufficient.
For Stripe Billing customers who do not use Checkout (direct PaymentIntents, embedded forms, etc.) the same pattern applies: write the first-party ID into PaymentIntent metadata or Customer metadata at create time. The principle is the same regardless of integration shape: the first-party ID must travel with the checkout artifact so the webhook handler can join it back to a session.
Step 4: Webhook handler joins the conversion to the session and writes attribution
This is the code from the section above. The webhook reads the first-party ID from the session metadata, looks up the matching touches in your database, computes first-touch and last-non-direct touch, and writes both into Customer metadata as the long-term join key for all future Stripe queries.
The full four-step pipeline at a glance:
Total integration time for a competent backend engineer: 1-2 days. Total code lines: roughly 300, including the tracker, the server endpoints, the schema, and the webhook handler. The bulk of the time is testing the edge cases (the buyer who opens checkout twice, the buyer who pays then refunds then re-pays, the buyer whose first-party ID was cleared by a browser extension, the buyer who completes checkout on a different device from the one that started the session) rather than writing the happy path.
Tool comparison: Stripe-native attribution tools in 2026
Six tools and one DIY path cover most of the SMB-and-mid-market Stripe attribution market in 2026. The pricing range is roughly $0 to $1,000+ per month for what is, at the SMB scale, very similar capability. The right pick depends on whether you are SaaS or ecommerce, what your ad spend looks like, and whether you already have a data warehouse.
Tool
Best for
Native Stripe?
E-comm focus?
SaaS focus?
Cookieless?
Entry price
Free trial?
Attrifast
SMB SaaS / DTC with under $100k/mo ad spend
Yes (OAuth + webhooks)
Yes
Yes
Yes
$29/mo
14 days, card required
SegMetrics
Mid-market info-product and SaaS
Yes (via integrations)
Limited
Yes
No (uses pixels)
$175/mo Essentials [8]
14 days
Wicked Reports
E-comm and info-product with paid ad heavy mix
Yes
Yes
Limited
No
$249/mo Foundation [9]
No
TripleWhale
Shopify DTC with $50k+/mo paid social spend
Indirect (via Stripe app)
Yes (Shopify-first)
No
No
$129/mo Growth [10]
7 days
Polar Analytics
Shopify mid-market
Indirect
Yes (Shopify-only)
No
No
$399/mo Standard [11]
No
Baremetrics
Stripe SaaS metrics dashboard
Yes (deep)
No
Yes
n/a (no tracker)
$129/mo Metrics [12]
14 days
ChartMogul
Stripe SaaS metrics with cohort depth
Yes
No
Yes
n/a (no tracker)
$129/mo Launch [18]
14 days
ProfitWell (now Paddle Retain)
Free SaaS metrics; subscription analytics
Yes (Stripe-friendly)
No
Yes
n/a
Free tier
n/a
Northbeam
Enterprise multi-touch attribution
Yes
Yes
Limited
Partial
Quote-based, $1k+/mo [13]
No
DIY: Sigma + custom tracker
Engineering teams with capacity
Yes (Sigma is Stripe-native)
Both
Both
Yes
Sigma $0.02-0.04/event [3]
30 days Sigma
DIY: BigQuery + Segment
Mid-market with existing warehouse
Via Stripe Data Pipeline
Both
Both
Yes (depending)
$120+/mo (Segment)
n/a
GA4 free + Stripe export
Operators on shoestring budget
Manual
Both
Both
No
$0 + time
n/a
A few clarifications on the table because the "category" mistake is constant in vendor demos.
Baremetrics, ChartMogul, ProfitWell (Paddle Retain) are Stripe metrics tools, not attribution tools. They give you MRR, churn, LTV, expansion, contraction, cohort retention. They do not capture upstream marketing channel data because they have no tracker. They can display channel attribution if you write the channel into Stripe metadata yourself; they will not capture it for you. If you already love your Baremetrics dashboard, the right pairing is Baremetrics for SaaS metrics plus a separate attribution capture layer.
SegMetrics and Wicked Reports are full attribution stacks but priced for info-product and mid-market SaaS. Both are excellent. Both also assume you have at least 5-10k visitors per month and a positive unit economic at the price point. SegMetrics at $175/mo is good value if you are doing $20k+/mo in revenue; below that the math is awkward.
TripleWhale and Polar are Shopify-native. TripleWhale [10] integrates Stripe via a connector but the product surface is Shopify-and-paid-ads-first. If your store is Shopify and your ad spend is heavy on Meta and TikTok, TripleWhale is the strongest pick. If your storefront is not Shopify, TripleWhale is the wrong tool. Polar [11] is Shopify-only.
Northbeam is enterprise. Quote-based pricing in the four-figures-per-month range. Designed for $500k+/year ad spend with multi-touch attribution across paid channels. Above that scale Northbeam, Rockerbox, and similar tools win on capability. Below it the price-to-capability ratio is wrong for most SMBs.
The "DIY with Sigma" path is real. Sigma is $0.02 per event for the first 10k events per month and scales down with volume [3]. For a SaaS doing 100k events per month the Sigma bill is roughly $2k. That covers the reporting layer. The capture layer (tracker, session table, webhook handler) is still custom work; figure 1-2 weeks of engineering plus ongoing maintenance.
The "GA4 free + Stripe export" path is the most common in 2026 and also the most broken. Manual export of Stripe charges, manual VLOOKUP against GA4 sessions by UTM tag, monthly spreadsheet built by an analyst. Works at small scale, breaks above 1,000 transactions per month, and the UTM tag survival rate through SaaS funnels is poor.
A simpler decision framework, ignoring price for a moment:
Your shape
Best fit
Why
SMB SaaS, $0-$2M ARR, mostly organic
Attrifast
Stripe-native, AI-aware, cookieless, cheapest
SMB SaaS, $0-$2M ARR, paid-heavy
Attrifast or SegMetrics
Attrifast if budget-constrained; SegMetrics if multi-touch matters more
Mid-market SaaS, $2-$10M ARR
SegMetrics
More mature attribution model library
SMB DTC, non-Shopify
Attrifast
Few alternatives that work outside Shopify
SMB DTC, Shopify, low ad spend
Polar or Attrifast
Polar if Shopify-deep; Attrifast if Stripe-deep
Mid-market DTC, Shopify, heavy paid social
TripleWhale
Best Meta/TikTok integration in this segment
Enterprise SaaS or DTC, $500k+/yr ad spend
Northbeam
Multi-touch sophistication justifies the price
SaaS team that needs metrics + attribution
Baremetrics + Attrifast
Pair Stripe metrics with attribution capture
Engineering-heavy team, infra in place
Sigma + custom tracker
DIY if you have the cycles
Honest caveat: tool pricing changes frequently. The numbers in the table reflect public pricing pages as of May 2026 [8][9][10][11][12]. Verify before committing. The categorical framing (which tool is for which shape of business) changes more slowly.
SaaS vs e-commerce: how Stripe attribution differs
The architecture is the same. The webhook events you anchor on, the model you default to, and the metrics you produce all differ.
First-touch, last-touch, order channel, ad campaign ID
Multi-touch importance
High (sales cycles span many touches)
Low to medium (impulse purchases dominate)
Time decay relevance
High (recency matters more than first-touch)
Medium
Sales rep attribution
Common in higher-ACV B2B
Rare
Coupon-as-attribution-signal
Useful for affiliate/influencer
Critical for influencer codes
Cart abandonment role
Less relevant
Critical (email recovery drives big channel slice)
Trial expiration role
Critical
n/a
The most important divergence is the conversion-time window. SaaS attribution has to handle the case where the first touch is a content article in February, the trial signup is in March after a Twitter mention, the activation is in April after a sales call, and the upgrade to a paid plan is in May. Four channels potentially deserve credit; the model has to express the time relationship between them. Ecommerce attribution is usually one or two touches in a short window, where the model debate is less consequential and the cleanliness of the last-click capture matters more.
The metadata pattern I run differs for the two shapes. SaaS:
For SaaS, attribution lives mostly on the Customer object and is sticky across the lifecycle. For ecommerce, attribution lives mostly on the order-level object (PaymentIntent) because the channel that drove the third order may be different from the channel that drove the first.
A before/after revenue allocation table from a real Attrifast SaaS customer migration, showing how channel attribution shifted once the four-step pipeline replaced their GA4-export spreadsheet:
Channel
Before (GA4 + Stripe spreadsheet)
After (Attrifast 4-step pipeline)
Delta
Google organic
$34,210
$42,180
+$7,970
Direct
$44,180 (overstated)
$0 (excluded)
-$44,180
ChatGPT
$0 (invisible)
$14,180
+$14,180
Perplexity
$0 (invisible)
$4,820
+$4,820
Email
$8,210
$11,820
+$3,610
Paid social
$9,180
$9,310
+$130
Referral
$2,140
$4,210
+$2,070
Affiliate
$1,820
$2,140
+$320
AI Overviews / Gemini
$0 (invisible)
$2,420
+$2,420
Other / unattributed
$1,920
$400
-$1,520
The team's quarterly board update before the migration claimed paid social and direct were the two largest revenue drivers. After the migration, the actual ranking was Google organic, ChatGPT, email, paid social, Perplexity. The change in budget allocation that followed (a content investment increase and a paid social cut) was justifiable only because the attribution data had been corrected.
MRR by channel: the metric most founders skip
The single highest-leverage metric you unlock with Stripe attribution is MRR by channel over time. Not MRR (Baremetrics covers that). Not channel sessions (GA4 covers that). The cross product. The chart where you can see your $112k MRR broken into "$48k from Google organic, $19k from ChatGPT, $14k from email nurture, $11k from paid social, $8k from referral, $12k from direct" with month-over-month trends.
What this chart lets you decide:
Question
Without MRR-by-channel
With MRR-by-channel
Should we double content investment?
"Our SEO traffic is up 30%"
"Our SEO-attributed MRR is up $14k/mo, +28% qoq"
Did the LinkedIn ads pay off?
"We spent $8k and got 412 leads"
"We spent $8k and acquired $4,200/mo in new MRR; CAC payback is 14 months"
Is ChatGPT actually driving revenue?
"Direct is up so brand is working"
"ChatGPT-attributed MRR is $19k/mo, +$11k qoq"
Which channel should we cut?
"Paid social has high CPC"
"Paid social has highest CAC and lowest 6-month LTV"
Where should the next hire go?
"We need more leads"
"Content drives 38% of MRR; we should hire a writer not an SDR"
The shape of the report in the canonical Attrifast dashboard format, with example numbers:
Channel
New MRR (last 30d)
Expansion MRR
Churned MRR
Net New MRR
% of Total Net
Google organic
$14,820
$1,420
-$2,180
$14,060
41%
ChatGPT
$6,420
$480
-$420
$6,480
19%
Email nurture
$4,180
$920
-$680
$4,420
13%
Paid social (Meta)
$3,820
$210
-$1,410
$2,620
8%
Perplexity
$2,140
$80
-$120
$2,100
6%
Referral / partner
$1,920
$310
-$240
$1,990
6%
LinkedIn ads
$1,820
$180
-$640
$1,360
4%
Affiliate
$810
$40
-$60
$790
2%
Direct (excluded)
$0
--
--
--
--
Total
$35,930
$3,640
-$5,750
$33,820
100%
The columns matter. New MRR is acquisition. Expansion MRR is upgrade revenue from existing customers. Churned MRR is the negative number. Net New is the headline that goes into the board deck. Paid social's net of $2,620 is meaningfully smaller than its gross new MRR of $3,820 because paid social cohorts churn faster than organic. This is the kind of insight that does not exist in any tool unless attribution is captured at acquisition and held across the lifecycle.
Repeat the chart by quarter to get the trend. Repeat by cohort to get the LTV-by-channel story. Repeat by plan to get the plan-mix-by-channel story. None of this is possible without writing attribution into Customer metadata at acquisition; all of it falls out trivially once the metadata is there.
LTV-by-channel for the same business, with a 12-month observation window:
Channel
Avg ACV (annualized)
6-mo retention
12-mo retention
Computed LTV (24mo)
CAC (last 90d)
LTV/CAC
Google organic
$1,420
89%
78%
$2,180
$42 (content cost)
51.9x
ChatGPT
$1,640
91%
82%
$2,490
$0 (no direct spend)
n/a
Email nurture
$1,140
84%
71%
$1,690
$14 (tool + ops)
120.7x
Paid social
$980
71%
52%
$1,180
$214
5.5x
Perplexity
$1,820
93%
84%
$2,720
$0
n/a
Referral
$1,540
88%
76%
$2,290
$80 (referral incentive)
28.6x
LinkedIn ads
$1,720
76%
58%
$2,180
$410
5.3x
Affiliate
$890
68%
49%
$1,040
$112 (commissions)
9.3x
Two things to read from the LTV-by-channel table that the gross-revenue table hides. First, paid social and LinkedIn ads have the lowest LTV/CAC ratios despite respectable gross numbers, because the cohorts churn faster. Second, organic channels (Google, ChatGPT, Perplexity) have effectively infinite LTV/CAC because their incremental cost per customer rounds to zero. The cohort math is the part that turns "we should spend more on paid social" into "actually no, the LTV is half what we thought."
Common Stripe attribution mistakes
Ten patterns I have seen often enough across the customer base to name as mistakes, with the fix for each.
Mistake 1: Storing attribution only on the Subscription, not on the Customer. When a customer upgrades, the new Subscription gets new metadata and the original attribution is lost. Fix: write first-touch to Customer metadata at first conversion and protect it from overwrites. Write Subscription-specific attribution to Subscription metadata for expansion analysis.
Mistake 2: Using customer.created as the attribution write event.customer.created fires when the Customer object is created, which may be before checkout completes (for delayed-payment flows) or even at trial signup. Attribution data is not always populated yet at that moment. Fix: use checkout.session.completed as the canonical first-write event; treat customer.created as the lock event for first-touch.
Mistake 3: Not handling webhook retries. Stripe retries on 5xx and on timeouts. A non-idempotent handler will double-write attribution, double-count revenue in your own database, or worse, overwrite the first-touch with the last-touch on the second retry. Fix: idempotency table keyed on event.id, check on entry.
Mistake 4: Letting client_reference_id carry a UTM string instead of a first-party identifier. I have seen teams put utm_source=google&utm_medium=cpc directly into client_reference_id. This works for the immediate session but loses every other touch the visitor had before checkout. Fix: client_reference_id carries the first-party ID; the full touch history lives in your database.
Mistake 5: Trusting the front-end to compute attribution. Client-side attribution computation is fragile (ad blockers, JS errors, session state loss). Fix: capture the raw inputs (UTM, referer, landing page, timestamp) on the server; compute channel only at the point of writing to Stripe.
Mistake 6: Conflating Customer and Subscription metadata. Stripe does not propagate metadata between objects. Setting it on Customer does not set it on Subscription, and vice versa. Fix: write to both in the webhook handler if you want both queryable.
Mistake 7: Hitting the 50-key metadata limit by storing too much. Once you have 50 keys on a Customer you cannot add more. Teams accumulate metadata over years and hit the wall. Fix: store the summary (channel, source, campaign) in Stripe; keep the full history in your own database. Stripe metadata is the join key, not the warehouse.
Mistake 8: Not capturing the cancel/churn channel. When customer.subscription.deleted fires you can capture the channel the customer was on (from existing metadata) plus the moment they churned. Without this, the net-MRR-by-channel chart is impossible to build. Fix: handle customer.subscription.deleted and write a churn event to your own database with the channel preserved.
Mistake 9: Ignoring refunds in ecommerce attribution. Paid social channels have higher refund rates than organic on most DTC sites. Crediting paid social with gross revenue overstates ROI by 10-30% at scale. Fix: handle charge.refunded and subtract from the original channel's revenue in reporting.
Mistake 10: Reporting attribution without the methodology disclosure. "ChatGPT drove $19k MRR last quarter" without saying which model, which window, and which capture method invites the rest of the team to argue rather than act. Fix: every attribution report includes the model name (e.g., "last-non-direct, 90-day window, server-side fingerprinting") in the footer.
A quick-reference mistake-to-fix table for the patterns above:
Mistake
Symptom
Fix
Time to fix
Attribution on Subscription, not Customer
First-touch lost on upgrade
Write to both, protect Customer first_touch
1 hour
Use customer.created as primary write event
Attribution data missing on early customers
Switch to checkout.session.completed
30 min
Non-idempotent webhook handler
Double-counted revenue on retry
Add idempotency table on event.id
1 hour
UTM crammed into client_reference_id
Lost touch history
Use first-party UUID as carrier
2 hours
Front-end channel inference
Ad-blocker drops attribution
Move inference server-side
4 hours
Customer vs Subscription metadata confusion
Reports inconsistent
Write both, document convention
1 hour
Hit 50-key metadata limit
New writes fail silently
Move history to your DB, summary in Stripe
1 day
No churn channel capture
Net MRR by channel impossible
Handle customer.subscription.deleted
2 hours
Refunds ignored in ecommerce
Paid social ROI overstated 10-30%
Handle charge.refunded, subtract from channel
2 hours
Report without methodology footer
Team argues about numbers
Add model/window/method to every report
15 min
When to use Sigma / BigQuery / DIY vs a tool
The build-vs-buy question is real and the answer depends on engineering capacity, scale, and how custom your attribution model needs to be. The honest table:
Your situation
Buy or build
Why
<$1M ARR, no dedicated data engineer
Buy (Attrifast, SegMetrics, etc.)
Engineering time is more expensive than any tool subscription at this scale
$1-5M ARR, 1-2 engineers, no warehouse
Buy
Same reason. Tool gets you 80% of the value in 1 hour vs 2 weeks
$5-20M ARR, has data engineer
Hybrid: tool for capture, warehouse for analysis
Capture is the hard part; vendor solves it. Analysis can move to dbt/BigQuery later
$20M+ ARR with warehouse
Build on warehouse + Sigma
Custom models, custom segments, and integration with rest of data stack
$500k+/yr ad spend
Build or Northbeam-class
Multi-touch sophistication justifies dedicated team or enterprise tool
Pre-revenue or early validation
Don't bother yet
Get to product-market fit first; attribution is for optimization, not discovery
Regulated industry (healthcare, finance)
Build
Compliance review of any vendor is often more painful than building
The pattern I see: bootstrapped SaaS teams over-index on building (sunk-cost fallacy on engineering time), and well-funded teams over-index on buying (afraid to commit eng cycles). The right calibration: if attribution is a competitive moat for your business, build it. If it is a measurement layer that supports decisions, buy it.
The Sigma-DIY architecture, for the teams that want to roll their own:
Three components to maintain: the tracker (200 LoC), the webhook handler (300 LoC), and the Sigma reports (10-50 LoC each). Sigma price: $0.02-0.04 per event for the first 10k events/mo, with volume discounts. For a SaaS doing 100k events per month, Sigma is roughly $2k/mo plus engineering maintenance.
The same architecture as a vendor (Attrifast or SegMetrics) costs $29-$175/mo. The break-even on engineering time is roughly 6-12 months for most SMB SaaS. Above that, the build math starts to favor in-house if you have a data engineer with available capacity.
Pricing comparison
The 2026 pricing landscape for Stripe-touching attribution and metrics tools, with the caveats that pricing changes frequently and vendors offer custom deals. All numbers from public pricing pages, May 2026.
Tool
Entry tier
Mid tier
Enterprise
Annual discount?
What you get at entry
Attrifast
$29/mo
$79/mo
$199/mo
Yes, 2 months free
Full attribution + Stripe revenue, all channels including AI
SegMetrics Essentials [8]
$175/mo
$300/mo
$1,000+/mo
Yes, 20%
First/last/linear attribution; no AI engines as a default channel
Wicked Reports Foundation [9]
$249/mo
$499/mo
Custom
Yes
Multi-touch; ad-spend-focused
TripleWhale Growth [10]
$129/mo
$399/mo
Custom
Yes
Shopify-first; Meta/TikTok focus
Polar Analytics [11]
$399/mo
$799/mo
Custom
Yes
Shopify-only mid-market
Baremetrics Metrics [12]
$129/mo
$229/mo
$599/mo
Yes
Stripe SaaS metrics, no attribution capture
ChartMogul Launch [18]
$129/mo
$349/mo
Custom
Yes
Stripe SaaS metrics with cohorts
ProfitWell / Paddle Retain
Free (metrics)
Paid (retention products)
Custom
n/a
Free SaaS metrics
Northbeam [13]
Quote, $1,000+/mo
Custom
Custom
Yes
Enterprise multi-touch
Rockerbox
Quote, $1,500+/mo
Custom
Custom
Yes
Enterprise multi-touch
Hyros
Quote, $999+/mo
Custom
Custom
Yes
Info-product / paid ads heavy
GA4 + custom dashboard
Free
Free
$50k+ (GA360)
n/a
Web analytics, channel data, no Stripe join
Plausible Analytics
$9/mo
$29/mo
$89/mo
Yes
Privacy-first web analytics, no Stripe revenue join
Fathom Analytics
$15/mo
$44/mo
$99/mo
Yes
Privacy-first web analytics, no Stripe revenue join
Stripe Sigma (DIY)
$0.02/event after 10k
$0.04/event
Volume
n/a
Reporting layer over Stripe data
The pricing-to-capability ratio at the SMB tier is wide. SegMetrics at $175/mo and Attrifast at $29/mo do similar work for sub-$5M-ARR SaaS, with SegMetrics offering deeper model variety and Attrifast offering native AI-engine attribution and a simpler setup.
A normalized "cost per attributable Stripe dollar" table, assuming a $100k MRR business with the tool capturing the full attribution layer:
Tool
Annual cost
Annual revenue tracked
Cost as % of revenue
Attrifast
$348
$1.2M
0.029%
SegMetrics
$2,100
$1.2M
0.175%
Wicked Reports
$2,988
$1.2M
0.249%
TripleWhale
$1,548
$1.2M
0.129% (Shopify only)
Polar
$4,788
$1.2M
0.399% (Shopify only)
Northbeam
$12,000+
$1.2M
1.0%+
DIY Sigma + custom
$24,000+ (Sigma + eng)
$1.2M
2.0%+ year one
The point of the table is not "Attrifast is cheapest." It is that attribution-tool cost as a percentage of tracked revenue collapses fast at scale, and the right question for an SMB is "does the cheapest tool do what I actually need?" If it does, the savings are real. If it does not, paying more for a tool that gets the answer right is the better deal.
When I would not pick Attrifast (honestly): if you are a Shopify DTC with $200k+/mo in Meta ad spend, TripleWhale's Meta-pixel integration is more sophisticated than ours. If you are an enterprise B2B with a $500k+/yr ad budget and a need for Markov/Shapley multi-touch models, Northbeam's modeling library is more mature. If you already have a data engineer and a working BigQuery warehouse, the DIY Sigma path is more flexible long-term.
When Attrifast is the right pick: SMB SaaS or DTC, Stripe-native billing, AI-engine traffic that the GA4 stack cannot see, $0-$200k/mo in ad spend, and a need for the report to land in the founder's inbox tomorrow rather than next quarter. That is the slice we built for.
What this looks like inside Attrifast
A short note on the product, because the article cannot pretend the author has no interest. Attrifast surfaces every Stripe attribution dimension covered above as a single dashboard. Connect Stripe via OAuth (not API key), drop the 4 KB tracker on your site, and the revenue-by-channel chart populates within 24 hours of your first Stripe webhook firing. AI engines (ChatGPT, Perplexity, Claude, Gemini) are first-class channels in the breakdown, not lumped into Direct.
The metadata convention covered in this article (attr_first_channel, attr_last_channel, etc.) is the same shape Attrifast writes into your Customer records. If you ever leave the product, the attribution data lives in Stripe metadata you own. There is no lock-in on the data layer; the lock-in (such as it is) is the convenience of the dashboard and the AI-engine detection that GA4 still does not ship in 2026.
Cost: $29/mo for the base tier, which covers SMB SaaS and DTC up to a stated session volume and includes everything in the article: webhook capture, first/last/last-non-direct attribution, MRR by channel, the AI-engine breakdown, and a Stripe-native dashboard. The pricing page is at attrifast.com. The Stripe attribution feature page lives at /for/stripe and the dedicated marketing attribution surface at /for/stripe-marketing-attribution.
The first-person reason I built it: I spent 18 months running my own SaaS through a manual GA4-to-Stripe spreadsheet, watching the numbers disagree with my Baremetrics chart, and concluding the only way to fix it was to write attribution into Customer metadata at capture time. Once I had built the capture layer for myself, the gap between "this works for me" and "this works for any Stripe customer" was small enough to ship as a product.
Limitations
Six things this article does not cover, and you should not extrapolate past.
Multi-touch attribution beyond first/last/linear. Markov chain and Shapley value models are real but require both more data volume and more methodological care than most SMB SaaS can support. The numbers in this article assume position-based models; data-driven models require their own treatment.
Cross-device journeys without identity stitching. A buyer who reads your blog on mobile and pays on desktop is a known undercount in any cookieless system without account-based identity. Treat this as a known measurement bound.
Offline conversions and sales-assisted deals. Higher-ACV B2B with sales reps in the loop need CRM integration (HubSpot, Salesforce) to capture the rep-influence layer. Stripe attribution alone undercounts sales-influenced revenue.
Affiliate, partner, and influencer attribution. Coupon-as-attribution-signal works for most cases; the edge cases (multiple coupons stacked, deal-site coupon attribution conflicts) are real and need their own playbook.
Privacy and consent considerations. The first-party identifier pattern is not subject to most cross-site cookie rules but the GDPR/CCPA picture is jurisdictional. Verify with your privacy review before deployment.
The pricing numbers in this article are May 2026 snapshots. Every vendor adjusts pricing periodically. Verify on the vendor's own page before making a procurement decision.
FAQ
Can I do Stripe revenue attribution natively in the Stripe dashboard?
No. Stripe Dashboard, Sigma, and the Reports API expose every financial dimension of a charge, subscription, invoice, payout, and dispute, but no marketing channel field. The Customer object has a metadata bag, the Checkout Session has a metadata bag, and the Subscription has a metadata bag, and Stripe will dutifully store whatever attribution string you put in those bags. Stripe will not populate them for you. The mental model that sticks: Stripe is a perfect ledger of what happened to the money once the buyer reached the form. Attribution is the entirely separate question of how the buyer reached the form, and Stripe has no visibility into the upstream session.
Which Stripe webhook events do I need for attribution?
Three events do 95% of the work for most SaaS and ecommerce sites. checkout.session.completed fires when a Checkout Session converts and is the cleanest moment to write the first-touch and last-touch channel into Customer metadata, because it carries the session metadata you set at create time. customer.subscription.created fires on the first paid subscription and lets you capture activation-time attribution distinct from the trial-signup attribution. invoice.payment_succeeded fires on every recurring renewal and is where you account for MRR-by-channel over time, especially when churn and reactivation matter. Optional: charge.refunded, customer.subscription.deleted, and customer.subscription.updated if you want net-MRR by channel rather than gross.
First-touch or last-touch for SaaS Stripe attribution?
For most bootstrapped SaaS in 2026, last-non-direct touch with a first-touch override flag is the practical default. Pure last-touch over-credits brand recall and direct re-visits. Pure first-touch over-credits the top-of-funnel channel that started the journey but might not be the one that converted. Last-non-direct preserves the channel that actually drove the meaningful action while ignoring the direct re-visit that closed it. The first-touch override is the column you keep around so you can attribute the originating channel separately for content-marketing ROI questions. I run a dual-write model in Attrifast for this reason: attr_first_channel and attr_last_channel as two fields, then let the dashboard pivot on either.
How does Stripe attribution differ for SaaS vs e-commerce?
Three structural differences. First, the conversion event. SaaS trials convert to paid weeks later, often after several email touches; ecommerce converts in a single session more often than not. The webhook you anchor attribution on differs: customer.subscription.created for SaaS, checkout.session.completed for ecommerce. Second, recurrence. SaaS revenue is MRR-by-channel over time, not a single transaction; ecommerce is order-by-channel with optional repeat-purchase analysis. Third, refunds and disputes. SaaS attribution rarely cares about refund-by-channel; ecommerce attribution needs net-revenue-by-channel because chargebacks, refunds, and returns concentrate disproportionately in certain channels (paid social, in particular).
Why is Stripe Sigma not enough for attribution?
Sigma is excellent at every question that lives inside the Stripe object model. How much MRR by plan, by country, by trial cohort, by tax jurisdiction. Sigma cannot answer how much MRR by marketing source because that field does not exist on any Stripe object unless you wrote it there. You can store attribution in Customer metadata, query it via Sigma, and build the report. That is what every team that runs Sigma-based attribution ends up doing. The cost is that the attribution capture pipeline (the part that decides what to write into metadata before the customer hits Stripe) is the hard part, and Sigma does not help with it. Sigma is the reporting layer. The capture layer is the missing piece.
Should I store attribution in Customer metadata or Subscription metadata?
Both, written at different lifecycle points, with Customer as the source of truth for first-touch and Subscription as the source of truth for plan-change attribution. Customer metadata is written once at customer.created and lives forever; ideal for the originating-channel field that should not be overwritten when a customer upgrades 18 months later. Subscription metadata is written at each new Subscription; ideal for the channel that drove the specific plan they are on now, which matters when you run expansion campaigns. Checkout Session metadata is the transport layer in between; you write to it at session create, copy it forward into Customer and Subscription metadata in the webhook handler. Stripe metadata is per-object and is not automatically propagated; the propagation is your job.
Will Stripe ever build native marketing attribution?
Unknown, and probably no, in any way that satisfies SMB needs. Stripe has been deliberate about staying out of marketing-analytics. The product surface is payments, billing, fraud, identity, banking. Acquisition is treated as not their problem, and the Stripe Atlas content corpus has historically referred operators out to dedicated attribution tools. The closest thing to a native answer is Stripe Sigma plus your own metadata convention, which works but requires you to build the capture layer separately. The bet I make at Attrifast is that the capture layer is a 5-year wedge product Stripe will not eat. The bet could be wrong; Stripe has surprised the market before.
What is the minimum stack for cookieless Stripe attribution?
Four pieces. A first-party identifier scoped to your own domain (UUID in localStorage or first-party cookie). A session-table row on your server that captures (first-party identifier, UTM tags, referer, landing page, timestamp) on every visit. A Stripe Checkout Session created with metadata that carries the first-party identifier forward. A webhook handler on checkout.session.completed that reads the identifier from the session metadata, looks up the matching session row, computes first-touch and last-touch channels, and writes them into Customer metadata. None of those pieces require a third-party cookie or a consent banner under most jurisdictions; the first-party identifier is functionally similar to a login cookie.
How do I attribute revenue from ChatGPT or other AI engines via Stripe?
Same architecture, with the addition of AI-engine detection at the tracker layer. The tracker checks the referer against a known AI-engine domain list (chatgpt.com, perplexity.ai, claude.ai, gemini.google.com, copilot.microsoft.com) and tags the session accordingly. For the 65-80% of AI clicks that arrive without a referer (because the AI client stripped it), behavioral fingerprinting on deep-page entries from new visitors catches most of the remainder. The webhook handler then writes attr_first_channel: "ai-search" and attr_first_source: "chatgpt" into Customer metadata exactly the same way it would write attr_first_channel: "organic-search" for a Google referral. The practical track-ChatGPT-traffic playbook covers the detection layer in depth.
Can I retrofit attribution onto existing Stripe customers without historical data?
Partially. For customers who already exist, you can backfill attribution from any other source you have (CRM notes, manual rep entry, an export from your old analytics tool, even the email domain pattern for B2B) and write it into Customer metadata. The backfill is necessarily imperfect because the original touch data does not exist. For new customers from the day you ship the tracker forward, attribution is captured cleanly. Most teams accept that the first 30-90 days of attribution data is a transition period with incomplete history; budget decisions in that window should weight legacy customers separately.
What happens to attribution when a customer churns and resubscribes?
This is a model decision your team has to make. Three options. Option A: treat re-subscription as a new acquisition, write fresh attribution at the second checkout.session.completed, accept that you have two attribution rows for one customer over time. Option B: preserve the original first-touch attribution forever, treat re-subscription as expansion, only update last-touch. Option C: write a separate attr_resubscribe_channel field that captures the channel of the re-acquisition while preserving the original. Attrifast defaults to option C; the dashboard can then break out "original acquisition" from "reactivation" cleanly.
How do I handle attribution for self-serve B2B that uses Stripe Billing but not Stripe Checkout?
The architecture is the same, the carrier object differs. For Stripe Billing with custom payment forms or embedded PaymentIntents, write the first-party ID into PaymentIntent metadata at create time. The webhook is payment_intent.succeeded instead of checkout.session.completed. For Stripe Billing with the Customer Portal handling subscription changes, you cannot inject metadata mid-flow, so capture must happen at the moment of new-subscription creation (which fires customer.subscription.created). The metadata pattern in the FAQ above works for all three integration shapes.
How accurate is the attribution against ground truth?
There is no perfect ground truth for marketing attribution; the question is which approximation is most useful. Against a UTM-tagged subset where every touch is captured cleanly, the first-touch and last-touch attribution captured by the four-step pipeline in this article is within 5-8% of the UTM-tagged baseline on the sites I have measured. For the AI-engine portion specifically (the slice where referers are stripped), behavioral fingerprinting precision is 78-86% and recall is 70-82%. The headline: this is materially better than GA4 default (which is 0% accurate on AI traffic) but not perfect. Treat the numbers as decision-grade, not finance-grade.
What is the difference between revenue attribution and conversion attribution?
Conversion attribution credits a channel with a conversion event (signup, trial start, paid conversion). Revenue attribution credits a channel with the dollar amount of revenue associated with that conversion. The two often look similar but diverge sharply when plan sizes vary by channel. A channel that drives many low-plan signups looks great in conversion attribution and mediocre in revenue attribution. A channel that drives few enterprise-plan signups looks the inverse. Always report both; the revenue column is what moves budget; the conversion column is what tells you channel volume.
Does Stripe attribution work for marketplaces (Connect)?
Yes, with extra care. Stripe Connect (for marketplaces and platforms) has separate Customer objects for the platform and for each connected account. Attribution must be written at the level the marketplace cares about (usually platform-level for marketplace economics; connected-account-level for seller analytics). The metadata propagation pattern works identically; the volume considerations (Sigma costs, webhook throughput) scale with marketplace transaction count, which can be much higher than a standard SaaS.