Optional. For products with their own marketing landing page: which first-touch visit led to a checkout.
A one-line script on your marketing site records the first visit (referrer, UTM, click ids) under a random visitor id. When that same id is passed into Stripe Checkout as client_reference_id or into Paddle Checkout as customData.ek_visitor_id, Erdi links the checkout to that first touch. Nothing is required in your product or app binary — only the marketing page and your own checkout wiring.
In the app: project Settings → API → create a token with purpose Attribution snippet, set Allowed origin to the exact marketing-site origin (scheme + host, no path, e.g. https://peeranimo.com), then paste the one-line script Erdi shows after create:
<script src="https://erdiknows.com/api/v1/attribution/ek_…" async></script>
The script stores the visitor id in localStorage under ek_vid and posts a first-touch visit once. The token is write-only for visits; it cannot write deploy events, metrics, or tickets. FAQ background: Do I have to install a script on my site?
Erdi does not create Checkout Sessions for you. When your backend (or edge) creates a Stripe Checkout Session, set client_reference_id to the same id the snippet stored in the browser. Read it on the page that starts checkout and send it to your server with the rest of the checkout request.
In the browser (marketing or checkout page):
const visitorId = localStorage.getItem('ek_vid')
// Send visitorId to your server when starting checkout.When creating the session (server):
const session = await stripe.checkout.sessions.create({
mode: 'payment', // or subscription
// …
client_reference_id: visitorId || undefined,
})If ek_vid is missing (direct traffic, no snippet, different browser), omit client_reference_id. The webhook still succeeds; Erdi simply writes no attribution marker.
Erdi does not open Paddle Checkout for you. When you call Paddle.Checkout.open, put the same ek_vid in customData.ek_visitor_id. That key is required — Erdi looks for exactly ek_visitor_id on transaction.completed.
const visitorId = localStorage.getItem('ek_vid')
Paddle.Checkout.open({
items: [/* … */],
...(visitorId
? { customData: { ek_visitor_id: visitorId } }
: {}),
})If ek_vid is missing, omit customData (or the key). The Paddle webhook still writes the usual payment timeline marker; Erdi simply writes no attribution marker.
Stripe: Settings → Webhooks → Stripe Checkout attribution. In Stripe, subscribe to checkout.session.completed only and paste the whsec_… signing secret. Detail: Stripe Checkout attribution.
Paddle: the existing Paddle webhook destination (same as timeline payments) is enough — no second endpoint. On transaction.completed, Erdi also reads data.custom_data.ek_visitor_id. Detail: Paddle.
In the app, open the project Acquisition tab. If an attribution snippet token is active for the project, a Web attribution section shows conversion counts for the selected range (today / yesterday / 7 / 30 / 90 / custom), broken down by first-touch utm_source or referrer. Counts are checkouts with a known first touch — not revenue.
Stripe Checkout (client_reference_id) and Paddle Billing (customData.ek_visitor_id) are both supported. Both call the same conversion helper after a matching visit.