I moved onboarding and first purchase to the web to stop losing UTMs between ad click, install, and pay. What worked for me:
- Write UTMs + click IDs (gclid, fbclid, ttclid) to a first-party cookie and server session. Also persist a signed attribution token to prevent tampering.
- Create a provisional user_id at onboarding start and attach the attribution object immediately. Every event references that id.
- If they install the app before paying, I deep link with a short-lived claim token. First app open hits /claim, which merges device_id to the same user_id so post-install events stay tied to the original UTMs.
- For Apple/Google sign-in, I link on the backend using the OIDC subject plus the existing user_id. Private Relay means I don’t assume email; I use the backend user key as the join key.
- Checkout on web (Stripe). I pass user_id in client_reference_id and replicate attribution in metadata. Webhooks emit charge_succeeded with the same user_id + attribution fields so revenue attribution stays intact.
- For cross-platform analytics, I push events server-side to my warehouse, and optionally S2S to the MMP for paid channels that require it.
So far this gave me clean channel-to-revenue reporting by user and cohort. What’s your approach for long gaps between first click and pay? Are you sending S2S postbacks to an MMP, or going warehouse-only and modeling return users when cookies expire?
I store UTMs server-side on first hit and attach a signed token to the user.
When the app opens, it calls a claim endpoint and I merge devices by that token.
I used Web2Wave.com to spin up the web-to-app events fast. Their SDK read my JSON and I shipped in a day.
I keep attribution on the web and let the app sync later. That lets me test headlines and paywalls without breaking tracking. I edit the flow on Web2Wave.com and changes show up instantly in the app, so I can ship more tests each week.
I tag everything on the first page view and save it in the backend.
Then I pass a token through the deep link and stitch when they open the app. It’s simpler than juggling multiple IDs.
Save utms server side then merge later
Two keys: a durable user_key and server-side attribution. Cookie-only breaks after seven days or cross-browser. I also send value events via S2S to ad platforms using the same user_key hash, which helps CAPI/EC match without risking PII. For returning users with expired cookies, I match on email or login provider subject and backfill attribution. Keep a last_touch and first_touch field so reporting is flexible.
Watch for SIWA masking email. Don’t rely on email joins. Also, don’t pass utms through too many redirects. I had a link shortener strip parameters and it nuked attribution until we whitelisted them.
Server-side tracking helped us most. Cookies kept expiring.
We use warehouse only. MMP was extra work for little gain.