The lifecycle in events
| Lifecycle moment | Event | Origin | Properties |
|---|---|---|---|
| Account created | sign_up | browser (hybrid) | method |
| Trial started | start_trial | server (billing webhook) | plan, trial_days |
| First paid subscription | subscribe | server (billing webhook) | plan, interval, value, currency, transaction_id |
| Renewal invoice paid | purchase with props.renewal: true | server | value, currency, transaction_id |
| Upgrade / expansion | purchase with props.expansion: true | server | delta value |
| Cancellation or refund | refund | server | original transaction_id, negative value |
| Login | login | browser | — |
The billing system is the authority for everything with money in it. The browser may show a "subscription confirmed" page, but the event that reaches ad platforms comes from the webhook, with the billing system's ids.
Why renewals must not be subscribe
Advertising platforms optimise toward the conversion you send. If every monthly renewal arrives as a new subscription with value, the platform learns that existing customers convert well and bids to reach them again. Keep the acquisition event (subscribe, once per customer) separate from retention (purchase with the renewal flag) and map only the acquisition event to advertising conversion actions. Renewals still flow to analytics and to your own reporting.
Values for ad platforms
subscribe: the first invoice amount is defensible and simple. Sending a lifetime-value estimate is allowed only as a documented constant per plan in the destination mapping, visible in the audit log; never a per-user guess.start_trial: no value, or a documented expected value per plan.- Expansion: the delta, not the new total.
refund: negative value of the refunded amount; GA4 processes refunds by transaction id, Google Ads by conversion adjustments, most others ignore it — the connector applies what the vendor supports.
Identity across devices
A SaaS customer signs up on desktop, pays on mobile, logs in everywhere. Call identify with your user id and, under marketing consent, the hashed e-mail; the server events carry the same user id and hash. This is what lets platforms match a server-side subscribe to the ad click that happened on another device days earlier, within their windows.
Trials and consent
The billing webhook carries the consent state your system recorded at sign-up, together with the user id. The router applies that state: a user who refused marketing at sign-up produces analytics events only, and a webhook that carries no consent information reaches no advertising destination at all. Do not re-derive consent from the fact that someone became a customer; being a customer is not consent to advertising measurement.
Dedup across billing retries
Billing systems retry webhooks. Use the invoice or subscription id as transaction_id; the ingest stage marks a second subscribe or purchase with the same transaction id as a duplicate conversion, and platforms that dedupe on order id merge anything that got through.
Reporting that stays honest
- Acquisition:
subscribecount and value by campaign, from the ad platforms and from your own events. - Trial conversion:
start_trial→subscribeper plan, from your own events only. - Net revenue:
purchase(all flags) minusrefund, from billing, reconciled monthly against the events.
If those three numbers ever disagree with the billing system's own reports, the events page's breakdown by source shows which path is missing what.