UTM parameters and Stripe: 6 steps to revenue by source
UTM tags don't reach Stripe on their own. Store the 3 core tags on your server, write a visitor ID into Stripe metadata, and join each payment to its source.
Muzahid Maruf, FounderUpdated
On this page
Explore with AI
Opens this article inside the chosen assistant with a ready-made prompt.
UTM parameters reach Stripe only when your own code carries them there.
The tags sit in the landing URL, a Stripe Checkout Session is created later on the server, and nothing joins the two unless you save the tags on the first visit and write a visitor ID onto the Stripe objects holding the payment.
Payment Links forward 5 UTM codes to your redirect URL after payment, and Stripe's documentation describes no place where Stripe saves the codes.
Without a bridge, Google Analytics 4 holds the campaign, Stripe holds the amount, and a revenue report files the payments as Direct. A bridge is the saved link between a tag and a payment, and building one takes 6 steps.
First, build the bridge in a sandbox. Finally, check 1 real $49 payment from Google Ads, LinkedIn or Pipedrive in 2026.
Key takeaways
- A Stripe payment carries a UTM tag only when your code puts it there. Payment Links hand 5 codes to a redirect page; other checkouts take metadata or a reference ID.
- Metadata holds 50 pairs, with 40-character keys and 500-character values, and Stripe copies it between objects in just 4 documented cases, so each subscription needs its own.
- Safari lets a JavaScript cookie idle for 7 days at most, so write the attribution cookie in your server's response.
- In the worked example, 10 of 45 paying customers, 22.2% of MRR, have no stored source and sit in Direct.
What happens to UTM parameters in Stripe?
A Payment Link accepts 5 UTM codes of up to 150 characters each, using letters, digits, dashes and _.
With the confirmation behavior set to redirect, Stripe appends the codes once payment completes and silently discards invalid values, per Stripe's Payment Links tracking page. Every other surface takes a field you fill yourself.
A field is a place where Stripe stores a value you wrote, such as metadata or client_reference_id. For example, a Checkout Session holds 50 metadata pairs and a Buy Button holds one 200-character reference.
| Surface | Field you write | Limit |
|---|---|---|
| Checkout Session | metadata, client_reference_id | 50 pairs; keys of 40 characters, values of 500; ID of 200 |
| Subscription or PaymentIntent from Checkout | subscription_data.metadata or payment_intent_data.metadata | Same 50, 40 and 500 |
| Payment Link | client_reference_id in the URL, or metadata on a link per channel | ID of 200 characters, same allowed set as the codes |
| Buy Button or pricing table | The client-reference-id attribute | 200 characters |
Stripe documentation, October 2026.
Which route fits each checkout type?
| Route | What you build | Weak point |
|---|---|---|
| Your own Checkout code, for server-created Sessions | A few lines at session creation and 1 webhook handler | Needs a developer |
| A reference ID on Payment Links, Buy Buttons and pricing tables | 1 URL parameter or attribute per click | A value with a dot or colon is silently dropped |
| A Measurement Protocol call to Google Analytics 4 | A server call with the client_id that gtag stored at signup | 25 events per request, and Google calls it a supplement to tag-based collection |
| A tool that reads Stripe and joins | A restricted API key and 1 tracking script | A monthly fee |
Stripe and Google documentation, October 2026.
A route is the way a tag reaches a Stripe object, and the table lists 4 routes for 2026. I start with the visitor ID: 1 value in client_reference_id and metadata, joined to a visit table you control.
Google's Measurement Protocol overview lists the limits of the Google Analytics 4 route: 25 events per request. A Payment Link route drops values with a dot or colon, and a Checkout code route needs 1 webhook handler.
The guide to connecting Google Analytics to Stripe revenue covers the route.
How does a click become a charge in 6 steps?
A click-to-charge chain is the path from a tagged link to a paid invoice, and a gap at any of the 6 steps leaves a $49 payment in Direct.
Each step below fixes 1 of 6 places where a tag can vanish: the link, the first request, the user row, the Stripe objects, the webhook and the sandbox test in Stripe Checkout.
The steps run in order, and a skipped step shows up as a row of unattributed revenue in the report at the end.
Step 1: How do you tag every link the same way?
Consistent tagging is the first link in the chain. Google's URL builder help says to always include utm_source, utm_medium and utm_campaign, and values are case sensitive, so google and Google count as 2 utm_source values.
The free UTM builder assembles the URL, and the UTM parameters guide covers Google's 9 supported parameters. Keep each channel's spelling lowercase, and store the approved values in 1 shared list in 2026.
Google Analytics 4 groups sessions into 19 default channels by the 3 core tags. Finally, reuse the same campaign name on every link of one send.
| Channel | Source | Medium | Campaign |
|---|---|---|---|
| Newsletter | weekly-digest | launch-2026-10 | |
| Google Ads | cpc | brand-2026-10 | |
| LinkedIn post | social | webinar-2026-11 | |
| Partner link | acme | affiliate | spring-2026-03 |
Example values.
Step 2: Why capture tags on the server?
A server-set cookie is a cookie written in an HTTP response header. Read the tags from the first request your server receives and set the cookie in the response.
Per WebKit's tracking prevention page, Safari Intelligent Tracking Prevention deletes cookies created in JavaScript after 7 days without user interaction and caps them at 24 hours after link decoration, while a cookie set in an HTTP response escapes both rules unless a CNAME or third-party IP address cloaks the cookie.
Write the cookie only when empty, so a later channel cannot replace the first source. A server cookie can keep a 90-day or 365-day lifetime, where Safari caps a JavaScript cookie at 7 days.
The stripped parameters guide has an Express middleware for this.
Step 3: What do you save at signup?
A user row is the database record created at signup. Write the tags and the visitor ID there, so one row holds utm_source, utm_medium, utm_campaign and 1 ID, 4 values in all.
A buyer who confirms an email on a phone has no cookie there, and the email typed at Stripe Checkout can differ from the signup email, while the saved ID still matches. First, write the row once at signup.
Second, never overwrite the row with later tags in 2026. Stripe Checkout, Stripe Billing and the Stripe Dashboard then show the same visitor ID.
Step 4: What goes onto the Stripe objects?
Stripe's metadata guide lists 4 cases where Stripe copies metadata between objects, and Checkout Session to Subscription is not one, so a subscription needs subscription_data.metadata. Each invoice then holds a snapshot in parent.subscription_details.metadata.
The 3 core tags and a visitor ID use 4 of the 50 slots.
A Checkout Session expires 30 minutes to 24 hours after creation (24 by default), so create the Session when the buyer clicks Subscribe if you store last-touch tags.
The Checkout Session, the Subscription and the first Invoice then carry the same 4 keys.
// The tags were saved on the user row at signup (step 3)// Stripe unsets a metadata key whose value is empty, so skip the blanksconst tags = Object.fromEntries( Object.entries({ utm_source: user.utm_source, utm_medium: user.utm_medium, utm_campaign: user.utm_campaign, }) .filter(([, value]) => value) .map(([key, value]) => [key, String(value).trim().toLowerCase().slice(0, 500)]),); const session = await stripe.checkout.sessions.create({ mode: "subscription", customer: user.stripeCustomerId, line_items: [{ price: priceId, quantity: 1 }], client_reference_id: user.visitorId, // up to 200 characters metadata: { vid: user.visitorId }, // on the Checkout Session subscription_data: { metadata: { vid: user.visitorId, ...tags }, // on the Subscription, then each invoice }, success_url: "https://example.com/welcome",});The Stripe metadata attribution setup post has the full key map, and the Payment Links attribution guide covers the per-click ID.
Step 5: Which webhooks return the payment?
Stripe events are the webhook messages announcing a payment. Handle checkout.session.completed for the first order and invoice.paid for renewals. Stripe sends a separate event when a delayed payment method such as ACH Direct Debit succeeds later, so handle checkout.session.async_payment_succeeded too.
Key each row by the object's ID, such as in_1 or cs_1, so a repeated delivery overwrites instead of adding.
For example, an invoice.paid event for month 2 carries the subscription of month 1, so the renewal inherits the first click in 2026. Stripe Webhooks retry failed deliveries, so 1 event can arrive 2 or more times.
Step 6: How do you test in a sandbox?
A Stripe sandbox is a test mode where no real money moves. In a Stripe sandbox, pay with the Visa test card 4242 4242 4242 4242, any future expiry and any 3-digit CVC, per Stripe's testing guide.
For a user with all 3 tags, the Checkout Session should then show 1 metadata key, the Subscription 4, and the first invoice's snapshot the same 4.
Fix any gap before launch in 2026, using test card 4242 4242 4242 4242 and 1 Stripe sandbox.
What does the report look like?
A revenue-by-source report is a table joining each payment to its stored tags. The hypothetical month below has one $49 monthly plan and 45 paying customers, each joined to a stored source.
Three sources have clicks and hold 35 of the 45 customers, or 77.8%, and $1,715 of the $2,205 MRR. One row holds the other 10 customers with no stored source.
| Source | Clicks | Customers | Click to paid | MRR | MRR per click | Share of MRR |
|---|---|---|---|---|---|---|
| newsletter / email | 400 | 12 | 3.0% | $588 | $1.47 | 26.7% |
| google / cpc | 2,000 | 18 | 0.9% | $882 | $0.44 | 40.0% |
| linkedin / social | 900 | 5 | 0.6% | $245 | $0.27 | 11.1% |
| No stored source | n/a | 10 | n/a | $490 | n/a | 22.2% |
| Total | 3,300 | 45 | $2,205 | 100% |
Illustrative figures for a $49 plan, not customer data.
Ranked by clicks, google leads with 2,000 and the newsletter has the fewest at 400.
Ranked by MRR per click the order changes: the newsletter moves from last to first at $1.47 a click, against $0.44 for google and $0.27 for linkedin.
The last row is the one to shrink, since steps 2 and 3 keep a buyer's source from going missing. Each later invoice takes the source of its subscription, so month 12 still credits the click that started it.
First click, last click or an even split is a reporting rule you can change without touching Stripe.
select v.utm_source, v.utm_medium, count(distinct p.subscription_id) as customers, sum(p.amount_cents) / 100.0 as revenuefrom payments pjoin visitors v on v.vid = p.vidwhere p.status = 'paid'group by v.utm_source, v.utm_mediumorder by revenue desc;Does the same pattern work in Pipedrive?
Pipedrive is a CRM storing deals, leads and contacts, and in Pipedrive the tags live in custom fields covering deals, leads, contacts, projects and products, per its custom fields article.
In the deal fields API a varchar field holds 255 characters, enough for any UTM value.
Create 1 field per tag plus 1 for the visitor ID, 4 fields in all, and fill them from the step 3 values at signup. The same ID in Stripe's client_reference_id lets a payment find its deal.
For example, a $49 payment finds its deal by the shared ID in 2026.
Where does the chain leak?
| Leak | What happens | Fix |
|---|---|---|
| Cookie written in JavaScript | Safari drops it after a week idle, so a buyer who returns on day 8 has no source | Set the cookie from the server response |
| Checkout metadata not copied down | A Checkout Session's metadata does not reach its Subscription, so renewals carry no source | Write it onto the Subscription at checkout |
| UTM codes on a Payment Link | Stripe passes the 5 codes on after payment, keeps none on the payment record, and drops a reference ID with any character outside the allowed set | Add a UUID as client_reference_id |
| Return from Stripe's domain | A payment processor's domain can appear as a referral source in Google Analytics 4 | Add it to unwanted referrals, which allow 50 per data stream |
| Partial tags in Google Analytics 4 | With any UTM present and no usable GCLID, every source dimension comes from UTMs, so a missing utm_source shows (not set), per Google's traffic-source documentation | Tag every link with all 3 basics |
Behaviors documented by Stripe, WebKit and Google, October 2026.
What does TrackRev do with the tags?
We built TrackRev, SaaS affiliate software with link tracking and revenue attribution built in. A TrackRev link carries a channel and saved UTM tags.
In our product, the redirect logs the click on the server, appends the tags to the destination, and passes a _vid parameter the pixel stores as a vid cookie on your domain.
On Payment Links, Buy Buttons and pricing tables the pixel adds the ID as the client reference, while a Session you create in code needs the cookie value passed in client_reference_id.
We built the hourly sync to read Stripe with a read-only restricted key, and we check 4 places in order, per the table. First, the charge metadata. Last, the buyer's email in 2026.
The sync covers 6 billing systems: Stripe, Paddle Billing, Polar, Lemon Squeezy, Creem and Dodo Payments.
| Order | Where the sync looks |
|---|---|
| 1 | metadata.vid on the charge |
| 2 | client_reference_id on the Checkout Session |
| 3 | metadata.vid on the Checkout Session |
| 4 | The buyer's email, if identify() ran at signup |
When does a tool earn its place?
A tool earns its place when Stripe is one of several inputs. Renewals inherit the first order's visitor through the subscription ID, and setup is on the Stripe integration page.
In our product, the pixel does not read UTM tags from a page URL, so hand-tagged traffic with no TrackRev link in the path still needs steps 2 to 5.
If one server-created Checkout covers all your sales, those steps cost no subscription; a tool earns its place with Payment Links, several partners and channels, or more than 1 billing provider.
The Free plan covers link tracking only, with 50 links and 1,000 tracked events a month, and revenue figures stay hidden. Revenue attribution needs a paid plan, from Indie at $29 a month (pricing).
Found this useful? Share it.
Frequently asked questions
- Not by itself. Outside Payment Links, Stripe records only what your code puts in metadata or a reference ID.
- Retrieve the Checkout Session, Subscription or Invoice with a secret key and read its metadata. Stripe redacts metadata from publishable-key requests, so run the lookup on a server.
- Yes, up to 5 codes of 150 characters. They reach your site only when the link redirects after payment, so append client_reference_id for a value that stays on the payment record.
- That is a reporting rule: first-touch keeps the earliest source, last-touch keeps the latest, and linear splits credit evenly. Storing both touches costs 7 of the 50 metadata slots, visitor ID included.
- Yes. Google says Google Analytics 4 then uses the auto-tagged values for source and medium, while manual utm_content and utm_term fill the ad content and term dimensions. Keep the 3 core tags on the final URL so your server can store them for Stripe.
- Create custom deal fields for the tags and a visitor ID, then write the saved values when your signup code creates the deal. A varchar field holds 255 characters.

Written by
Founder, TrackRev.io & Contant.io
Muzahid Maruf founded TrackRev.io, SaaS affiliate software with no limit on tracked revenue, and Contant.io. He writes about affiliate programs.
Writes about Marketing attribution · Link tracking · Revenue analytics · SaaS growth
Run your affiliate program without a revenue cap.
Affiliate software for indie hackers and small SaaS teams: $29 a month flat, 0% fees, link tracking and revenue attribution built in. Free to start, no card.