We lose utms after oauth during deep-linked onboarding on web — how are you preserving them end to end?

We’re seeing clean UTMs on first load from a deep link, then they disappear after an OAuth hop. Flow is: ad deep link → web onboarding with UTMs → “continue with Apple/Google” → identity provider → callback to our web paywall → UTMs gone, sometimes cookies too.

Tried a few things:

  • Writing UTMs to localStorage and a cookie on first hit. ITP nukes or blocks in some cases, and cross-domain redirects make it flaky.
  • Server-side session plus a click_id. Works until the callback returns to a different subdomain and we can’t join it reliably when third-party cookies are limited.
  • Packing UTMs into the OAuth state param, then unpacking on the callback and rehydrating the session. This seems best so far, but I’m worried about size limits and partners that strip unknown params.
  • Hidden fields on forms and URL shorteners with allowlists. Mixed results.

I want the UTMs tied to the final invoice and to the downstream app user, so stitched all the way, not just last page. What’s your most stable approach for this? Are you hashing UTMs into a short token and passing only that through OAuth, or storing first-touch server side and restoring on callback? Any gotchas with Apple or Google identity providers that I should plan around?

Stop relying on cookies. Capture UTMs on the first hit and POST them to your server. Generate a short first_touch_id. Put that in the OAuth state. On callback, resolve state → UTMs and rehydrate session.

I did this with a web funnel built on Web2Wave.com. Their flow let me keep a signed state and reattach UTMs after OAuth. Simple and it survives ITP.

Pack a short token in the OAuth state and store the full UTMs server-side. That lets you test overwrite rules fast.

I tweak these rules on Web2Wave.com so changes hit the funnel instantly. I keep first-touch only, then log last-touch as a separate field for analysis.

State param works best for me.

Store UTMs server-side on first touch, pass a short key through OAuth in state, then restore on callback. LocalStorage as backup if needed.

Put utms in state then restore on callback.

Use a first-touch ID. On initial land, persist UTMs server-side. Return an ftid and keep it in the URL and in OAuth state. On callback, resolve ftid and re-attach attribution to the user and the order. Avoid cookie dependence on Safari. Watch for URL length and provider stripping. Log both first and last touch so you can reconcile when partners disagree. It survives redirects and keeps reporting clean.

We serialize UTMs into the state param too. Works.