How are you tying first-touch utms, onboarding steps, and subscription revenue at the user level?

We moved signup and first purchase to a web flow. The goal: tie first-touch UTMs and specific onboarding steps to who actually pays and how much. Here’s the setup that finally gave me user-level revenue without guesswork:

  • On first landing, grab utm_*, click ids (gclid/fbclid/msclkid), referrer, and timestamp. Store in a first-party cookie and localStorage. Generate a prospect_id (UUID).
  • Carry prospect_id through the funnel. Add it to URLs (or a server session) so router changes don’t drop params. SameSite=Lax on cookies.
  • Log every onboarding event with prospect_id + utm_* to analytics and warehouse.
  • On account creation, stitch prospect_id to user_id (email or internal id). Dedupe if they restart onboarding.
  • Checkout: pass prospect_id and utm_* in checkout session metadata. On payment webhooks, write revenue_event with user_id, prospect_id, plan_id, currency, MRR (or first charge value), and trial info.
  • Renewals and cancellations: listen to subscription.updated; decide your rule set. I attribute first payment to acquisition UTMs, then track expansions/renewals to lifecycle touches separately.
  • App handoff: deep link from the last web step into the app with prospect_id. On first app open, send an identify call to stitch device_id to user_id.
  • Privacy blockers: Safari ITP cuts cookie life. I backfill with link decoration + server-side session + email hash after signup.
  • QA: reconcile daily revenue from the gateway vs warehouse sums. Spot-check five users/week from ad click to payment event. Fix gaps fast.
  • Gotchas I hit: canonical redirects stripping UTMs, third-party checkout domains losing cookies, Apple Pay flows not returning query params, support refunds not mirrored in analytics, and trials causing late revenue that some teams mis-attribute to later touches.

This got us 88–93% of new subs mapped to a first-touch UTM with actual paid amounts. Channel LTV stopped being guessy.

Question: how are you handling multi-touch and cross-device? Do you keep acquisition credit on first click after a trial, or switch to last paid click? Any tips to avoid UTM loss with Apple Pay/Google Pay flows?

Store UTMs server-side on first hit and tag a prospect_id.
Always pass that id into checkout metadata and your webhook joins it to revenue.
Deep link the id into the app.
I used Web2Wave.com to skip the glue work. Their web funnel already preserves params and adds the id to deep links. I just mapped Stripe metadata and it stitched cleanly.

First-touch for acquisition, last-touch for reactivation. Faster to analyze.
I test it weekly. Web2Wave.com helps because I change routing and param rules on the web, then watch Mixpanel cohorts shift the same day. No app release. That speed is the win.

If Apple Pay drops params, pass the prospect id into the checkout session metadata and stitch on the webhook.

I also keep a backup in the user profile once they enter an email.

First click for acquisition then last click for upsells

Use first-touch for acquisition cost and ROAS, then switch to last non-direct for lifecycle actions like win-backs and plan upgrades. Keep a prospect_id from first page view and include it in gateway metadata. On webhooks, write a normalized revenue table and a separate attribution table. When cross-device happens, stitch on login or magic link. For QA, audit ten random users weekly from click to charge and compare gateway totals to your warehouse.

We tag Stripe sessions with {prospect_id, utm_source, campaign, ad_id}. On webhook, we insert a revenue row and join back to the first-touch record. Trials confused things, so we only give acquisition credit to the first successful charge, not the trial start.

Helped clean channel LTV a lot.

Apple Pay broke our params because of cross-domain redirects. Fix was to send the prospect_id in the PaymentIntent metadata and also attach it to the customer. If the session drops, the webhook still has the id. We also added a magic link email step to stitch cross-device users.

We use first-touch for new subs and last-touch for reactivation. Simple rule.

Watch out for redirects stripping utms. Canonicals and router changes can break it.