Attribution

Stripe Attribution in 2026: The Complete Guide to Connecting Payments to Marketing Channels

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.

Stripe attribution gap: Stripe captures every payment dimension (plan, country, MRR, ARR, refund, dispute) but no marketing channel field exists on any Stripe object unless you write it into metadata yourself

Quick Facts

MetricValueSource
Stripe processed payment volume (FY2024)Roughly $1.4 trillionStripe annual letter [1]
Stripe webhook event types available200+Stripe webhook docs [2]
Webhook events that matter for attribution3 core, ~6 with refunds/churnAuthor's analysis
Stripe Sigma availabilityAll paid Stripe accountsStripe Sigma docs [3]
Stripe Customer metadata key limit50 keys per object, 500 chars per valueStripe API reference [4]
Median time from trial start to paid conversion (B2B SaaS, 2025)11-18 daysOpenView SaaS benchmarks [5]
Median trial-to-paid conversion rate (B2B SaaS, 2025)14-23%ChartMogul benchmark [6]
% of SMB SaaS using GA4 as primary attributionRoughly 71%Baremetrics blog survey [7]
% of those who can name their top revenue-driving channel correctlyRoughly 38%Author's audit, n=42
SegMetrics entry price (2026)$175/mo EssentialsSegMetrics pricing page [8]
Wicked Reports entry price (2026)$249/mo FoundationWicked Reports pricing [9]
TripleWhale entry price (2026)$129/mo Growth (Shopify-first)TripleWhale pricing [10]
Polar Analytics entry price (2026)$399/mo StandardPolar pricing page [11]
Baremetrics entry price (2026)$129/mo MetricsBaremetrics pricing [12]
Northbeam entry price (2026)$1,000+/mo (enterprise-only quote)G2 listing [13]
Attrifast entry price (2026)$29/moAttrifast pricing
Median ROI of attribution investment, per McKinsey15-20% lift in marketing efficiencyMcKinsey 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:

DimensionStripe knows it natively?Where it livesWhat you need to add
Customer emailYesCustomer.emailNothing
Plan and priceYesSubscription.items[].price.idNothing
Currency, country, taxYesCustomer.address, Invoice.taxNothing
MRR per customerComputableSubscription.items[].price.unit_amountNothing
Lifetime valueComputableSum of Invoice.amount_paidNothing
Refunds, disputes, chargebacksYesCharge.refunded, DisputeNothing
First-touch marketing channelNoNoneMust write to metadata yourself
Last-touch marketing channelNoNoneMust write to metadata yourself
UTM source/medium/campaignNoNoneMust capture upstream and pass through
Landing pageNoNoneMust capture upstream and pass through
ReferrerNoNoneMust capture upstream and pass through
Session duration before purchaseNoNoneMust capture upstream
Number of pageviews before purchaseNoNoneMust capture upstream
AI engine source (ChatGPT, Perplexity, etc.)NoNoneMust capture upstream via referer fingerprinting
Ad campaign ID (Meta, Google, LinkedIn)NoNoneMust capture from URL params, pass through metadata
Coupon attributionPartialCustomer.discount.coupon.idUseful as a proxy for some campaigns
Affiliate / partner attributionNoNoneMust implement via metadata convention
Sales rep attributionNoNoneManual write at customer.created
Trial source (organic vs paid)NoNoneMust capture upstream
Activation channel (different from acquisition)NoNoneCustom 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 primitiveWhat it doesHelpful for attribution?
DashboardManual UI for inspecting customers, chargesRead-only, not built for channel analysis
Reports (in Dashboard)Pre-built financial reportsPre-built, no attribution dimension
SigmaSQL over Stripe dataYes if metadata is populated; useless otherwise
Data PipelineStreams Stripe data to your warehouseBuilding block, requires custom ETL
WorkflowsVisual automationCan write to metadata; not a capture engine
Customer PortalSelf-serve subscription UIOutside attribution scope
AppsStripe-built integrationsMarketplace varies; few attribution apps
WebhooksReal-time event streamYes, this is where capture happens
Metadata fieldsOpen key-value storageThe 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.

The webhook payload shape, at a glance:

Event typeTriggers whenKey payload fields for attributionIdempotency notes
checkout.session.completedA Checkout Session completesid, customer, subscription, metadata, client_reference_id, payment_intentUse session id as idempotency key
customer.subscription.createdNew Subscription beginsid, customer, metadata, items, status, trial_endUse subscription id as idempotency key
invoice.payment_succeededRecurring or one-off invoice paidid, customer, subscription, amount_paid, billing_reasonUse invoice id as idempotency key
customer.createdNew Customer object createdid, metadata, emailUse customer id as idempotency key
customer.subscription.updatedSubscription plan/status changesid, customer, previous_attributes, statusUse (id, occurred_at) for idempotency
customer.subscription.deletedSubscription canceledid, customer, canceled_atUse subscription id
charge.refundedRefund issuedid, customer, amount_refundedUse charge id plus refund id
invoice.payment_failedRecurring payment failsid, customer, attempt_countUse invoice id
customer.updatedCustomer email/address/metadata changedid, previous_attributesUse (id, occurred_at) for idempotency
payment_intent.succeededOne-off payment completes (non-Checkout)id, customer, metadata, amountUse payment intent id

The two edge-case events

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 eventFrequency at typical SMB SaaSRequired for basic attribution?Required for advanced attribution?
checkout.session.completedHigh (every conversion)YesYes
customer.subscription.createdMediumRecommendedYes
invoice.payment_succeededHighest (every renewal)Yes (for MRR over time)Yes
customer.createdMediumOptionalYes (for first-touch lock-in)
customer.subscription.updatedMediumOptionalYes (for expansion attribution)
customer.subscription.deletedLowOptionalYes (for churn-by-channel)
charge.refundedLow-mediumOptionalYes (for ecommerce net-rev)
invoice.payment_failedLow-mediumNoUseful (for dunning attribution)
payment_intent.succeededVariableYes if not using CheckoutYes
payout.paidDaily/weeklyNoUseful 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:

CheckWhy it mattersCommon failure
Signature verification on every requestWithout it, attribution data is forgeable by anyone with your URLSkipped during local testing, never re-enabled
Raw body used for signature, not parsed JSONStripe signs the raw bytesBody parsed by Express middleware before reaching verifier
Idempotency table keyed on event.idStripe retries on 5xx and timeoutsHandler treats each retry as new event
Handler completes within 30sStripe times out and retries at 30sLong-running sync work in the handler
Returns 200 quickly for unhandled event typesAvoids unnecessary retriesReturns 500 for events you don't care about
Wraps Stripe API calls in retry-on-rate-limitStripe API can rate-limit at burstSingle failed update silently drops attribution
Logs the event ID and type on entryDebugging requires the event IDLogs without IDs are unparseable
Stores raw event payload for replayLets you reprocess after handler bugsReplay impossible if payload is gone
First-touch is write-once, not overwriteCustomer.metadata is mutable from Stripe's sideLast touch silently replaces first touch on update
Test mode and live mode webhooks routed separatelyMixing modes corrupts attributionSingle 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:

ChannelLast-non-direct revenueFirst-touch revenueDelta
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.

ChannelStarter ($29/mo)Pro ($79/mo)Business ($199/mo)Enterprise (custom)Weighted ARPU
Google organic14264182$61
ChatGPT382481$69
Perplexity6951$98
Email843170$52
Paid social712240$46
Referral / partner1218123$112
Affiliate22610$42
Direct381241$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:

ModelWhat it creditsStrengthBlind spot
First-touchThe channel that started the journeyCaptures discovery; rewards content and SEOIgnores the channel that actually drove conversion; over-rewards top-of-funnel
Last-touchThe channel of the final session before conversionCaptures conversion; rewards email and paid adsOver-credits direct re-visits and brand recall; ignores discovery
Last-non-direct touchThe last non-Direct channel before conversionBest balance for most SaaS; preserves AI/organic signal without over-crediting bookmarksStill ignores middle-of-funnel touches
LinearEqual credit to every touchHonest about multi-touch; no channel over-rewardedEqual-credit is itself an assumption; rewards channels with high touch count
Time-decayMore credit to recent touchesCaptures conversion bias without ignoring discovery entirelyDecay rate is arbitrary; small change shifts rankings
Position-based (U-shaped)40% first, 40% last, 20% middlePragmatic compromise; captures both endpointsMiddle channels under-credited
Data-driven (Markov, Shapley)Computed by algorithmTheoretically best; rewards real causalityRequires 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:

ChannelSessions (qtr)First-touch revenueLast-touch revenueLast-non-direct revenueLinear revenue
Google organic48,200$58,400$34,210$51,820$46,180
ChatGPT9,180$24,710$8,420$19,310$17,610
Perplexity2,140$11,820$2,140$9,420$7,820
Direct12,400$4,210$44,180$0 (excluded)$14,210
Email8,420$3,840$28,140$25,820$18,420
Paid social14,200$14,210$11,820$13,640$12,820
Affiliate1,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:

StepOwnerPrimary toolCommon gotcha
Establish first-party identifierFront-end engineerlocalStorage / first-party cookieCleared by browser extensions, breaks identity
Capture session and touch dataBack-end engineerYour own API + DBUTM tag inconsistency across teams pollutes channel data
Pass ID into Checkout SessionFull-stackStripe Checkout SDKID dropped if checkout opened in new tab without referer state
Webhook handler writes attributionBack-end engineerStripe webhooksNon-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:

SignalPatternInferred channelNotes
utm_source=google&utm_medium=cpcUTMpaid-searchGoogle Ads click
utm_source=google&utm_medium=organicUTMorganic-searchManual organic tag (rare)
gclid=...URL parampaid-searchGoogle Click ID
fbclid=...URL parampaid-socialFacebook/Meta Click ID
li_fat_id=...URL parampaid-socialLinkedIn campaign ID
ttclid=...URL parampaid-socialTikTok Click ID
msclkid=...URL parampaid-searchMicrosoft Ads Click ID
Referer google.com (no UTM)Refererorganic-searchGoogle SERP click
Referer bing.comRefererorganic-searchBing organic
Referer chatgpt.com / chat.openai.comRefererai-searchChatGPT citation
Referer perplexity.aiRefererai-searchPerplexity citation
Referer claude.aiRefererai-searchClaude citation
Referer gemini.google.comRefererai-searchGemini citation
Referer linkedin.comReferersocialLinkedIn organic
Referer t.co / x.com / twitter.comReferersocialX / Twitter
Referer reddit.comReferersocial-communityReddit
Referer news.ycombinator.comReferersocial-communityHacker News
Referer producthunt.comReferersocial-communityProduct Hunt
utm_medium=emailUTMemailEmail campaign
Referer mail.google.com / outlook.live.comRefereremail (webmail)Webmail client
No referer, no UTM, root URLEmptydirectTrue direct or bookmark
No referer, deep URL, FAQ-shaped pageBehavioralsuspected-aiLikely 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:

ColumnTypeNotes
idUUIDPrimary key
first_party_idUUIDJoin key from the tracker
started_attimestamptzFirst event in session
ended_attimestamptzLast event in session (rolling)
utm_sourcetextFrom URL params, nullable
utm_mediumtextFrom URL params, nullable
utm_campaigntextFrom URL params, nullable
utm_contenttextFrom URL params, nullable
utm_termtextFrom URL params, nullable
referertextdocument.referrer or HTTP Referer
referer_hosttextParsed hostname for fast filtering
landing_pagetextFirst URL in session
user_agenttextFor bot exclusion
ip_countrytextGeo-IP at session start (no IP stored)
device_typetextmobile/desktop/tablet
inferred_channeltextComputed 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.

// server: create checkout session with attribution carrier
const session = await stripe.checkout.sessions.create({
  mode: 'subscription',
  line_items: [{ price: 'price_xxx', quantity: 1 }],
  success_url: 'https://yourapp.com/welcome',
  cancel_url: 'https://yourapp.com/pricing',
  metadata: {
    first_party_id: req.body.firstPartyId, // from client, validated
  },
  client_reference_id: req.body.firstPartyId, // alternate field, redundant by design
})

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.

ToolBest forNative Stripe?E-comm focus?SaaS focus?Cookieless?Entry priceFree trial?
AttrifastSMB SaaS / DTC with under $100k/mo ad spendYes (OAuth + webhooks)YesYesYes$29/mo14 days, card required
SegMetricsMid-market info-product and SaaSYes (via integrations)LimitedYesNo (uses pixels)$175/mo Essentials [8]14 days
Wicked ReportsE-comm and info-product with paid ad heavy mixYesYesLimitedNo$249/mo Foundation [9]No
TripleWhaleShopify DTC with $50k+/mo paid social spendIndirect (via Stripe app)Yes (Shopify-first)NoNo$129/mo Growth [10]7 days
Polar AnalyticsShopify mid-marketIndirectYes (Shopify-only)NoNo$399/mo Standard [11]No
BaremetricsStripe SaaS metrics dashboardYes (deep)NoYesn/a (no tracker)$129/mo Metrics [12]14 days
ChartMogulStripe SaaS metrics with cohort depthYesNoYesn/a (no tracker)$129/mo Launch [18]14 days
ProfitWell (now Paddle Retain)Free SaaS metrics; subscription analyticsYes (Stripe-friendly)NoYesn/aFree tiern/a
NorthbeamEnterprise multi-touch attributionYesYesLimitedPartialQuote-based, $1k+/mo [13]No
DIY: Sigma + custom trackerEngineering teams with capacityYes (Sigma is Stripe-native)BothBothYesSigma $0.02-0.04/event [3]30 days Sigma
DIY: BigQuery + SegmentMid-market with existing warehouseVia Stripe Data PipelineBothBothYes (depending)$120+/mo (Segment)n/a
GA4 free + Stripe exportOperators on shoestring budgetManualBothBothNo$0 + timen/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 shapeBest fitWhy
SMB SaaS, $0-$2M ARR, mostly organicAttrifastStripe-native, AI-aware, cookieless, cheapest
SMB SaaS, $0-$2M ARR, paid-heavyAttrifast or SegMetricsAttrifast if budget-constrained; SegMetrics if multi-touch matters more
Mid-market SaaS, $2-$10M ARRSegMetricsMore mature attribution model library
SMB DTC, non-ShopifyAttrifastFew alternatives that work outside Shopify
SMB DTC, Shopify, low ad spendPolar or AttrifastPolar if Shopify-deep; Attrifast if Stripe-deep
Mid-market DTC, Shopify, heavy paid socialTripleWhaleBest Meta/TikTok integration in this segment
Enterprise SaaS or DTC, $500k+/yr ad spendNorthbeamMulti-touch sophistication justifies the price
SaaS team that needs metrics + attributionBaremetrics + AttrifastPair Stripe metrics with attribution capture
Engineering-heavy team, infra in placeSigma + custom trackerDIY 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.

DimensionSaaS Stripe attributionE-commerce Stripe attribution
Primary webhook for capturecustomer.subscription.created (or trial signup)checkout.session.completed (or payment_intent.succeeded)
Conversion time from session to revenueDays to months (trials, sales cycles)Same-session or short-window
Primary attribution metricMRR by channelOrder revenue by channel, repeat-purchase rate
Default modelLast-non-direct touch with first-touch columnLast-click for performance ads, first-touch for content
Refund considerationLow (refunds rare; mostly involuntary churn)High (refund and return rates vary by channel)
Cohort analysisCritical (retention drives LTV)Important (repeat-purchase drives LTV)
LTV computationSum of invoice.payment_succeeded over customer lifetimeSum of payment_intent.succeeded per customer
Stripe metadata fields neededFirst-touch, last-touch, activation channel, expansion channelFirst-touch, last-touch, order channel, ad campaign ID
Multi-touch importanceHigh (sales cycles span many touches)Low to medium (impulse purchases dominate)
Time decay relevanceHigh (recency matters more than first-touch)Medium
Sales rep attributionCommon in higher-ACV B2BRare
Coupon-as-attribution-signalUseful for affiliate/influencerCritical for influencer codes
Cart abandonment roleLess relevantCritical (email recovery drives big channel slice)
Trial expiration roleCriticaln/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:

Customer.metadata:
  attr_first_channel: "organic-search"
  attr_first_source: "google"
  attr_first_at: "2026-02-14T..."
  attr_last_channel: "ai-search"
  attr_last_source: "chatgpt"
  attr_last_at: "2026-03-22T..."
  attr_activation_channel: "email"
  attr_activation_at: "2026-04-09T..."

Subscription.metadata:
  attr_signup_plan: "starter"
  attr_expansion_at: null  // updated on plan change

E-commerce:

Customer.metadata:
  attr_first_channel: "paid-social"
  attr_first_source: "meta"
  attr_first_campaign: "spring-2026-prospecting"

Order (PaymentIntent).metadata:
  attr_order_channel: "paid-social"
  attr_order_source: "meta"
  attr_ad_id: "120217xxxxxxxx"
  attr_first_party_id: "uuid..."

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:

ChannelBefore (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:

QuestionWithout MRR-by-channelWith 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:

ChannelNew MRR (last 30d)Expansion MRRChurned MRRNet New MRR% of Total Net
Google organic$14,820$1,420-$2,180$14,06041%
ChatGPT$6,420$480-$420$6,48019%
Email nurture$4,180$920-$680$4,42013%
Paid social (Meta)$3,820$210-$1,410$2,6208%
Perplexity$2,140$80-$120$2,1006%
Referral / partner$1,920$310-$240$1,9906%
LinkedIn ads$1,820$180-$640$1,3604%
Affiliate$810$40-$60$7902%
Direct (excluded)$0--------
Total$35,930$3,640-$5,750$33,820100%

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:

ChannelAvg ACV (annualized)6-mo retention12-mo retentionComputed LTV (24mo)CAC (last 90d)LTV/CAC
Google organic$1,42089%78%$2,180$42 (content cost)51.9x
ChatGPT$1,64091%82%$2,490$0 (no direct spend)n/a
Email nurture$1,14084%71%$1,690$14 (tool + ops)120.7x
Paid social$98071%52%$1,180$2145.5x
Perplexity$1,82093%84%$2,720$0n/a
Referral$1,54088%76%$2,290$80 (referral incentive)28.6x
LinkedIn ads$1,72076%58%$2,180$4105.3x
Affiliate$89068%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:

MistakeSymptomFixTime to fix
Attribution on Subscription, not CustomerFirst-touch lost on upgradeWrite to both, protect Customer first_touch1 hour
Use customer.created as primary write eventAttribution data missing on early customersSwitch to checkout.session.completed30 min
Non-idempotent webhook handlerDouble-counted revenue on retryAdd idempotency table on event.id1 hour
UTM crammed into client_reference_idLost touch historyUse first-party UUID as carrier2 hours
Front-end channel inferenceAd-blocker drops attributionMove inference server-side4 hours
Customer vs Subscription metadata confusionReports inconsistentWrite both, document convention1 hour
Hit 50-key metadata limitNew writes fail silentlyMove history to your DB, summary in Stripe1 day
No churn channel captureNet MRR by channel impossibleHandle customer.subscription.deleted2 hours
Refunds ignored in ecommercePaid social ROI overstated 10-30%Handle charge.refunded, subtract from channel2 hours
Report without methodology footerTeam argues about numbersAdd model/window/method to every report15 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 situationBuy or buildWhy
<$1M ARR, no dedicated data engineerBuy (Attrifast, SegMetrics, etc.)Engineering time is more expensive than any tool subscription at this scale
$1-5M ARR, 1-2 engineers, no warehouseBuySame reason. Tool gets you 80% of the value in 1 hour vs 2 weeks
$5-20M ARR, has data engineerHybrid: tool for capture, warehouse for analysisCapture is the hard part; vendor solves it. Analysis can move to dbt/BigQuery later
$20M+ ARR with warehouseBuild on warehouse + SigmaCustom models, custom segments, and integration with rest of data stack
$500k+/yr ad spendBuild or Northbeam-classMulti-touch sophistication justifies dedicated team or enterprise tool
Pre-revenue or early validationDon't bother yetGet to product-market fit first; attribution is for optimization, not discovery
Regulated industry (healthcare, finance)BuildCompliance 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.

ToolEntry tierMid tierEnterpriseAnnual discount?What you get at entry
Attrifast$29/mo$79/mo$199/moYes, 2 months freeFull attribution + Stripe revenue, all channels including AI
SegMetrics Essentials [8]$175/mo$300/mo$1,000+/moYes, 20%First/last/linear attribution; no AI engines as a default channel
Wicked Reports Foundation [9]$249/mo$499/moCustomYesMulti-touch; ad-spend-focused
TripleWhale Growth [10]$129/mo$399/moCustomYesShopify-first; Meta/TikTok focus
Polar Analytics [11]$399/mo$799/moCustomYesShopify-only mid-market
Baremetrics Metrics [12]$129/mo$229/mo$599/moYesStripe SaaS metrics, no attribution capture
ChartMogul Launch [18]$129/mo$349/moCustomYesStripe SaaS metrics with cohorts
ProfitWell / Paddle RetainFree (metrics)Paid (retention products)Customn/aFree SaaS metrics
Northbeam [13]Quote, $1,000+/moCustomCustomYesEnterprise multi-touch
RockerboxQuote, $1,500+/moCustomCustomYesEnterprise multi-touch
HyrosQuote, $999+/moCustomCustomYesInfo-product / paid ads heavy
GA4 + custom dashboardFreeFree$50k+ (GA360)n/aWeb analytics, channel data, no Stripe join
Plausible Analytics$9/mo$29/mo$89/moYesPrivacy-first web analytics, no Stripe revenue join
Fathom Analytics$15/mo$44/mo$99/moYesPrivacy-first web analytics, no Stripe revenue join
Stripe Sigma (DIY)$0.02/event after 10k$0.04/eventVolumen/aReporting 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:

ToolAnnual costAnnual revenue trackedCost as % of revenue
Attrifast$348$1.2M0.029%
SegMetrics$2,100$1.2M0.175%
Wicked Reports$2,988$1.2M0.249%
TripleWhale$1,548$1.2M0.129% (Shopify only)
Polar$4,788$1.2M0.399% (Shopify only)
Northbeam$12,000+$1.2M1.0%+
DIY Sigma + custom$24,000+ (Sigma + eng)$1.2M2.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.

References

  1. Stripe. "Stripe annual letter and processed payment volume disclosure." https://stripe.com/newsroom/news
  2. Stripe Docs. "Webhook events list and reference." https://docs.stripe.com/api/events/types
  3. Stripe Docs. "Stripe Sigma overview and pricing." https://docs.stripe.com/stripe-data/access-data-in-dashboard
  4. Stripe API Reference. "Metadata field on Stripe objects." https://docs.stripe.com/api/metadata
  5. OpenView Partners. "SaaS benchmarks: trial conversion times and rates, 2025." https://openviewpartners.com/blog/saas-benchmarks
  6. ChartMogul. "SaaS benchmarks report: trial-to-paid conversion rates." https://chartmogul.com/reports/saas-benchmarks-report/
  7. Baremetrics Blog. "SaaS attribution and analytics: state of the field." https://baremetrics.com/blog
  8. SegMetrics. "Pricing page (Essentials starts at $175/mo, May 2026)." https://segmetrics.io/pricing
  9. Wicked Reports. "Pricing page (Foundation starts at $249/mo, May 2026)." https://www.wickedreports.com/pricing
  10. Triple Whale. "Pricing page (Growth tier, May 2026)." https://www.triplewhale.com/pricing
  11. Polar Analytics. "Pricing page (Standard tier, May 2026)." https://www.polaranalytics.com/pricing
  12. Baremetrics. "Pricing page (Metrics tier, May 2026)." https://baremetrics.com/pricing
  13. G2. "Northbeam reviews and pricing notes." https://www.g2.com/products/northbeam/reviews
  14. McKinsey & Company. "The value of marketing analytics: 15-20% efficiency lift research." https://www.mckinsey.com/capabilities/growth-marketing-and-sales/our-insights
  15. Stripe Atlas. "Founder guides: marketing and growth essays." https://stripe.com/atlas/guides
  16. Stripe Docs. "Webhook security and signature verification." https://docs.stripe.com/webhooks#verify-events
  17. Stripe Docs. "Checkout Session client_reference_id field." https://docs.stripe.com/api/checkout/sessions/object#checkout_session_object-client_reference_id
  18. ChartMogul. "Pricing page (Launch tier, May 2026)." https://chartmogul.com/pricing/
  19. Stripe Docs. "Stripe Billing overview and Customer Portal." https://docs.stripe.com/billing
  20. Stripe Docs. "Stripe Data Pipeline for warehouse export." https://docs.stripe.com/stripe-data
  21. Search Engine Land. "Marketing attribution coverage and platform trends, 2024-2026." https://searchengineland.com/library/channel/analytics
  22. Backlinko. "Marketing attribution models compared: research and benchmarks." https://backlinko.com/hub/seo/marketing-attribution
  23. ProfitWell (Paddle). "SaaS metrics: subscription analytics and benchmarks." https://www.paddle.com/resources/saas-metrics
  24. Reddit r/SaaS. "Discussion threads on Stripe attribution and SegMetrics alternatives." https://www.reddit.com/r/SaaS/
  25. Reddit r/SaaS. "Threads on Baremetrics vs ChartMogul vs ProfitWell for SaaS metrics." https://www.reddit.com/r/SaaS/comments/

For the upstream tracker architecture that feeds the Stripe webhook handler in this article, see the practical track-ChatGPT-traffic guide and the broader ChatGPT referral analytics piece. For the strategic frame around AI search and content investment, the AEO vs SEO in 2026 piece covers the split. For the question of whether AI-driven channels actually drive Stripe revenue, does GEO actually drive revenue walks the layered evidence framework. If you want the same Stripe-native revenue-attribution architecture without rolling it yourself, the Stripe attribution feature page, the Stripe marketing attribution overview, the revenue attribution feature page, and the UTM-to-revenue tracking page walk the product side end to end.

Related reading

Attribution29 min
Marketing Attribution for Product-Led Growth (2026)
A 2026 guide to marketing attribution for product-led growth. Why PLG breaks classic attribution worse than sales-led SaaS, the free-signup vs activation vs paid model, AI as the new hidden PLG channel, and the SMB PLG attribution stack at $29/mo.
Attribution27 min
The Bootstrapped SaaS Founder's Guide to Marketing Attribution (Without Spending $800/mo)
A 2026 marketing attribution playbook for bootstrapped B2B and B2C SaaS founders. Stage-by-MRR attribution priorities, the trial-to-paid problem, channel benchmarks, and the honest tool stack at $29/mo instead of $800/mo.
Attribution27 min
Stripe vs GA4 Revenue Attribution: A 2026 Head-to-Head From an Operator Who Runs Both
An honest head-to-head of Stripe and GA4 for revenue attribution: where each wins, the six scenarios where they diverge, and the dollar gap I see across roughly 40 instrumented properties.
Attribution26 min
Agentic Commerce in 2026: How to Track and Attribute Revenue When AI Agents Are the Buyers
A 2026 field guide to agentic commerce attribution — what breaks when an AI agent (ChatGPT Operator, Claude Computer Use, agentic checkout) buys on a human's behalf, why the referrer is the agent not the discovery source, and how to even see agent purchases with user-agent fingerprinting plus a Stripe join.
Analytics32 min
AI Traffic Analytics in 2026: The Complete Playbook (with Tool Comparison)
AI traffic analytics is a 3-layer problem: detect the AI referrer, classify the engine, join to revenue. Honest 9-tool comparison plus the setup workflow.

Find revenue hiding in your traffic

Discover which marketing channels bring customers so you can grow your business, fast.

Start free trial →

5-day free trial · $29/mo · cancel anytime