AffiliateRail docs
APIServer-side attribution

Server-side attribution

Attribute sales without Stripe Checkout: read the referral cookie and pass one metadata field.

Three lines of server code attribute a sale for a merchant who never uses Stripe Checkout.

The tracking script stores the referral in a cookie called rail_referral on your registrable domain (acme.com, so app.acme.com and www.acme.com both see it). The cookie's value is a JSON object; the field you need is clickId. Pass that click id to Stripe as metadata.rail_referral when you create the customer, the subscription, or the checkout session, and we bind the customer to the partner when Stripe tells us about it.

We read it from four events, in whichever order they arrive:

Stripe objectEvent we bind onWhere to put it
Customercustomer.createdmetadata.rail_referral
Subscriptioncustomer.subscription.createdmetadata.rail_referral
Checkout Sessioncheckout.session.completedmetadata.rail_referral or client_reference_id
Payment Link, Pricing Table, Buy Buttoncheckout.session.completednothing: the script stamps client_reference_id for you

Binding is idempotent on the Stripe customer id. The first event that carries a click id wins; a later one for the same customer changes nothing. A click id from another program, or one we never issued, binds nothing, and the sale shows on the attribution health panel with that reason.

Pick your stack:

If your framework is not listed, the rule is the same everywhere: URL-decode the cookie, parse the JSON, take clickId. The value is null when the visitor arrived without a partner link, and you should create the customer without the metadata in that case rather than sending an empty string.

rail_referral=%7B%22clickId%22%3A%22clk_01J...%22%2C%22partnerId%22%3A%22part_...%22%2C...%7D

Checking that it works

Stripe test mode works the same way as live. Create a customer with the metadata, pay an invoice, and the sale appears under Insights, Attribution health, with the path shown as "Metadata". If it appears under "Unattributed" instead, the row says what was missing.