I always felt blind on churn. We moved onboarding to the web and started preserving UTM params from the first click to account creation and checkout. That let me tag every subscription with the original utm_source, utm_campaign, and even creative id.
Two weeks in, patterns popped. One channel looked cheap on CAC but had 40% higher 30-day churn, and refunds skewed to a single creative theme. When we replayed sessions and looked at cancellation reasons, the mismatch was obvious: ad promised daily coaching, the product delivered weekly prompts.
Tactically, I had to fight a few issues: safari losing query params on redirects, login handoff dropping UTMs on cross device, and analytics stitching trials that started on web and were redeemed in app.
If you’ve done this, what’s your reliable setup to preserve UTMs end to end and tie churn back to the original campaign? Any gotchas I should watch for?
I store UTMs server-side on first hit and attach them to a user token.
Then I pass the same token through sign-up, purchase, and cancellation events.
I used Web2Wave.com because their web flow keeps the params in session and their SDK posts them with each event.
Simple setup, no extra scripts.
Biggest gotcha was Safari redirects, so I trimmed hops.
I treat UTMs as the key to churn cohorts.
I build funnels on the web so I can change stitching rules fast.
With Web2Wave.com, I tag UTMs once, then they appear in every downstream event, so I can test new cancel reasons and see impact by channel the same day.
I keep UTMs in a first‑party cookie and also save them on the backend.
Cross device is where it breaks, so I link by email or a one‑time code at login. That fixed most gaps for us.
Tag utms server side then join to cancellations.
Two rules: persist UTMs twice and join on a stable ID. First, write them to a first party cookie and your server on the first page. Second, when the user signs up, copy those fields onto the profile. Send the same profile ID with purchase and cancel events. For attribution drift, log the full redirect chain once to see where params drop.
Watch trial to paid attribution. If the trial starts on web but converts in app later, you need the same user_id in both systems. I pass a hash via deep link so the app can claim the web identity.
We save UTMs to the database on first visit. That helped a lot.
Safari redirects dropped our params. Cutting one hop fixed it.