Attribution
How Supapulse links a payment to the visit that first brought the customer, how renewals are handled, and what it doesn't cover.
How revenue is linked to a visit
Every revenue event is credited to a first touch and a last touch. A touch is a pageview that carries a utm_source or a referrer. The first touch is the earliest such pageview, the last touch is the latest. When Supapulse finds none, both read direct.
To find the visit, Supapulse tries these in order and uses the first that matches:
- Visitor fingerprint. Sent on the event, or stored when the customer was identified.
- Session ID. Sent on the event, matched to pageviews with the same
session_id. - The customer's stored touch. Saved the first time this customer was attributed. This is what renewals use.
- None. The payment counts in totals and shows as
direct.
Visit from google.com ──► pageviews /pricing (referrer google.com)
│
Signup ─┴─► supapulse.identify("user_123") ──► customer user_123 gets first touch google.com
│
Stripe invoice.paid (renewal, months later) ─────────────┘ credited to google.comLinking a payment to a visit
identify
Call identify() in the browser once the user is known. It sends your user ID and the current session ID with the public token. Supapulse builds the visitor fingerprint on the server from the request's IP address and user agent, the same way it does for pageviews, and looks up that visitor's pageviews.
supapulse.identify(user.id);Use the same value as customer_id on revenue events. With Stripe, the customer key is the Stripe customer ID (cus_…); pass your own user ID in Stripe metadata as supapulse_customer_id and Supapulse links the two. See Stripe.
Checkout metadata
If a visitor can pay before you identify them, read the fingerprint and session ID when checkout starts and put them in the payment's metadata. This is the strongest link because it doesn't depend on a later identify().
const visitor_fingerprint = await supapulse.getVisitorFingerprint(); // string | null
const session_id = supapulse.getSessionId();For Stripe, set these metadata keys: supapulse_visitor_fingerprint, supapulse_session_id and supapulse_customer_id. For other processors, pass the same values as visitor_fingerprint and session_id on the revenue event.
Renewals
The first payment from a customer saves its touch on the customer record. Every later payment from the same customer_id that can't be matched to a visit directly reuses that stored touch, so a renewal in month six is credited to the source that won the customer.
The breakdowns
| Breakdown | Revenue is credited to |
|---|---|
| Sources | The first or last touch source of the payment. Use the toggle to switch. |
| Pages, entry pages, exit pages | Pageviews of the matched session |
| Referrers | The referrer of the matched session |
| UTM campaigns | The first pageview with a utm_campaign in the matched session |
| Countries, devices, browsers, OS | The matched session's visitor details |
Every breakdown except Sources joins revenue to the pageviews of the matched session, inside the selected date range. A payment with no matched session still counts in totals and in Sources.
Refunds and MRR
A refund is stored as a negative amount, so revenue in the dashboard is net of refunds and the Refunds tile shows what was refunded. MRR is the running sum of mrr_impact across subscription starts, upgrades, downgrades and churn. Stripe events set it for you. For other processors, send mrr_impact yourself. See API reference.
Events with a subscription_id are reconciled per subscription. A renewal counts its mrr_impact only when Supapulse has no MRR recorded for that subscription yet, so subscriptions that started before you connected get a baseline from their first renewal. A churn removes exactly the MRR recorded for the subscription and ignores the value sent, so discounts don't leave a remainder and churn of a subscription Supapulse never saw doesn't go below zero.
Limits
- Lookback. Supapulse looks at pageviews from the last few days, following the site's salt rotation window: 1 to 30 days. A visit older than that isn't found by fingerprint. Identify the user while the visit is recent.
- Cross-device journeys. A phone visit and a laptop purchase are different visitors.
identify()on the laptop can only link what the laptop did. - Session IDs. A session ID covers one page load. On a multi-page site each full page load has a new one.
- Currencies. Reports add amounts without converting them.
Getting good attribution
- Call
identify()right after login or signup, on the same device as the first visit. - Pass
supapulse_customer_idin Stripe metadata so Stripe's customer ID and your user ID are linked. - Keep UTM parameters on your landing URLs; they're read from the pageview URL.
- Use the checkout metadata path when payment can happen before an account exists.