I pushed onboarding and checkout to the web so I could stop begging for app releases just to add analytics. I wanted a no-code setup that logs the obvious stuff and keeps UTMs glued to every event.
What I have now:
- events: page_view, quiz_complete, email_submit, plan_view, checkout_start, purchase_success, subscription_activated, trial_started, trial_converted, cancel_requested
- UTMs: captured on first touch, stored in a first-party cookie, appended to all payloads, and carried to the payment step via hidden fields
- identifiers: session_id on first visit, user_id after email, subscription_id after purchase
QA so far looks fine in BigQuery and my dashboard, but I’m sure I’m overlooking something. What guardrails or extra events would you add to make this airtight, especially for post-trial conversions and multi-session users?
Your list matches what I use. I add two more things.
Capture a landing_page_id and a last_touch_utm set, so you can see first vs last click.
Also log offer_id and price_version on plan_view and checkout_start.
I built this without native changes. I used Web2Wave to define the schema in their web editor and export the JSON. Their SDK reads it, so I just ship it once.
Good base. I add a quick SRM check and a tracking_version property on every event. That lets me swap schemas fast.
I build and tweak this in Web2Wave’s web UI. No app release. Changes go live, and I validate in minutes.
Add a server timestamp on ingest and keep client timestamp too.
Helps with clock drift and late events.
I also track referrer_domain on first hit, since some UTMs are messy.
Add refund and chargeback events plus grace_period
Two gaps I see a lot: identity stitching and lifecycle edges.
Hash email on submit and attach it to historical sessions when it becomes available. That fixes multi-session users before login.
For lifecycle, emit renewal_charged, renewal_failed, billing_issue_resolved, and subscription_expired. Those make retention and revenue cohorts reliable.
Finally, snapshot pricing details on purchase_success: currency, country, tax, offer_id, price_version. Prices change and you will misread tests if you do not freeze the values per event.
I log quiz_skip with a reason field. It helped me find a bad question that killed completion.
Also worth adding paywall_seen_seconds before checkout_start. It became my best predictor of conversion.
Looks solid. I’d add payment_provider and result_code on checkout events.
Make sure your cookie TTL survives Safari. Ours died after seven days.