docs
Platform guides

Attribution and tracking

How the script tracks referrals, proving your install, the four attribution paths, and fixing unattributed sales.

Attribution means working out which partner sent each paying customer. Partners only promote what they trust to count, so this part has to be right. This page covers:

  • how the tag tracks a visitor
  • proving your install with Check my install
  • the four ways a sale gets credited to a partner
  • fixing a sale that wasn't credited

Here because a sale didn't track? Jump to Attribution health.

How tracking works

The script is one tag:

<script async src="https://app.affiliaterail.com/rail/v1/rail.js" data-rail="prg_xxxx"></script>

A visitor lands with a referral parameter in the URL, like ?ref=alice. The script records a click and remembers who sent them.

  • It stores the referral in a small file the browser keeps for your site: a first-party cookie named rail_referral.
  • The cookie sits on your main domain, so acme.com, www.acme.com and app.acme.com all see it. The browser's own storage (localStorage) holds a backup copy.
  • The click also stores how long the credit lasts, the attribution window. It's 60 days by default, set under Settings, Attribution.
  • Inside that window, the first touch wins. A second partner's link doesn't take over a referral that's already running.

The parameters the script reads

The script also reads the link parameters other affiliate tools use, so old links keep paying the right partner. Here's the full list, in order. The first one present wins:

rail, ref, aff, via, fpr, tap_s, lmref, tolt, afmc, ali, f2f-ref, fp_ref, affonso, atp, _from, _by, _go, _get, deal

The first is ours and the second is your default. You can set your own under Settings, Attribution.

Because the order is the precedence order, your own parameter always beats a leftover one on an old link. Say you already use one of these names for something else, like a ?deal= promo code. A visitor arriving on it is reported once and gets nothing back, because the value doesn't match a partner. We store nothing and credit nobody.

When the query string carries no referral, the script reads the part of the URL after #. It accepts #rail=jimi, the short form #rail-jimi, and the same parameter list as above.

A fragment survives things a query string often doesn't: link shorteners, email clients that strip tracking parameters, and chains of redirects. The query still wins, so ?rail=a#rail=b credits a.

Two domains in one funnel

Say your marketing site is acme.com and checkout is on getacme.io. List the second domain in the tag's data-domains attribute. The script carries the referral across in the link, then strips its parameter from the address bar.

Two guards on every click

  • No iframes. The script won't track inside an iframe, a page framed inside someone else's page. That stops anyone framing your pricing page to make fake clicks.
  • Signs of a bot. Each click carries whether the page was visible and whether the browser was automated. That feeds the risk checks under Reports, Risk.

We read nothing from the visitor's device beyond what attribution already uses. We don't fingerprint browsers, as a matter of policy.

Advertising click identifiers

An ad platform adds its own id to the links it sends, like gclid from Google or fbclid from Meta. When a landing URL carries one, we store it on the click. The same goes for msclkid, ttclid and the rest, the five utm_ values, and sub1 to sub5.

Here's why that helps. Let's say every conversion from one partner carries a gclid. That partner is buying search ads, and if those ads land on your own brand name, you're paying commission on customers you'd have won anyway. The identifiers feed the same risk checks. With consent mode on, the ones that identify a person only travel after consent.

By default the script treats the referral cookie as part of running your affiliate program. Do you run a cookie banner for EU or UK visitors, and want attribution to wait for it? Add one attribute:

<script async src="https://app.affiliaterail.com/rail/v1/rail.js"
        data-rail="prg_xxxx" data-consent="auto"></script>

With consent mode on, we only track a visitor from a referral link after they say yes. Until then, the referral sits in the page's memory and nowhere else. If the visitor leaves without saying yes, it's gone.

The script listens for the yes on its own. It understands:

  • Google Consent Mode v2 (a grant of ad_storage or analytics_storage)
  • the IAB Transparency and Consent Framework v2.2 (Purpose 1)
  • the consent objects Cookiebot and OneTrust put on the page

Most banner tools can send Google Consent Mode, and turning that on is the most reliable route. If yours sends none of these, call this from its accept callback:

window.rail.consent();

Calling it twice changes nothing. Nor does calling it after the script already spotted consent. On consent, the script sends the held referral and writes the cookie, and if an earlier visit already stored a referral, it reuses that one instead of counting it again.

What your data protection officer will want to know

  • Before consent the script makes no network request, writes no cookie, reads and writes no localStorage, and keeps the referral out of the URL and browser history. Nothing leaves the page's memory.
  • Every click row stores whether it was captured under consent mode. So you can check consent per click, from a stored field.
  • Ad click identifiers that identify a person (such as gclid and fbclid) follow the same rule. With consent mode on, nothing is sent before consent, so they aren't either.
  • The script sets no third-party cookies and collects no fingerprint. The referral cookie is first party, on your domain, with a lifetime you set under Settings, Attribution.

Consent mode is off by default, because most programs outside the EU don't run banners. It's on every plan, because compliance shouldn't cost extra.

Why the click count is the soft number and commissions aren't

The script stores the referral in the cookie and in localStorage before it calls us. It sends that call with keepalive, so it survives the visitor leaving mid-request.

So a slow, blocked or dropped click request loses a row in the click count, and keeps the attribution. The sale carries the stored referral whenever it arrives, minutes or weeks later.

Keep that in mind when you compare two dashboards. Your own analytics counts page views in the browser. Ours counts clicks that reached our server. Ad blockers, privacy extensions, patchy mobile signal and people who close the tab in the first second all sit between the two.

A few percent of difference in the click column is normal. The commission column is exact, and it doesn't depend on that request.

Getting the sale attributed

Four paths tie a paying customer to a partner. Use whichever fits how you charge. They can run side by side.

  1. Stripe Checkout, Payment Links, Pricing Tables and Buy Buttons. Nothing to build. The script fills in the checkout's client_reference_id for you, including on buttons added after the page loads. It never overwrites a value you set yourself.
  2. Server-side metadata. For Stripe Elements, or checkouts your own server code creates. Read the rail_referral cookie and pass its click id as metadata.rail_referral on the customer, subscription or checkout session. It's three lines of code, and Server-side attribution has them in Node, Next.js and Python.
  3. Coupons. Give a partner a Stripe promotion code. Sales that use it are credited to them, even with no click. Podcast and video audiences often buy this way.
  4. Leads and the API. Approved leads and customers created over the API are tied to their partner directly. Your commission rules price them like everything else.

Billing with Paddle? The Paddle guide covers its version of these paths.

The first event that carries a referral wins for that Stripe customer. Later events change nothing, however many arrive.

Prove the install

  1. Open Reports, then the Attribution tab.
  2. Press Check my install.
  3. Enter a page URL, or leave it blank to use your homepage.

You get three checks back. Each one passes or fails, with the fix written out.

  1. Tag found. We fetch and read the page. A failure says which: no tag, a tag without data-rail, another program's id, or an unexpected host. A tag-manager install passes as yes, from your traffic. Verifying your install explains that case.
  2. Click recorded. We send a practice click through the real click endpoint, from your page's address. It follows the same path your visitors take. We delete the practice click and everything it made afterwards.
  3. Cookie domain. Which domain the referral cookie lands on, and what sets it.

About that cookie. Safari keeps a cookie written by a script for seven days at most. With a tracker domain of your own, our server sets the cookie in the click response, which clears that cap. Safari's cloaking defence then applies its own seven-day cap anyway, because the tracker domain is a CNAME, a DNS record pointing your name at ours. That's why a commission depends on the billing reference rather than the cookie.

Attribution health

The same page tracks how attribution is going over 7, 30 or 90 days:

  • Sales seen, Attributed, Unattributed and the Attribution rate
  • a split by path: link, coupon, lead and metadata

Every sale we couldn't credit is listed with what was missing, in plain words:

  • No click id on the checkout
  • A click id we never issued
  • A click from another program
  • The click's window had closed
  • No checkout session or customer event seen

Each one comes with the likely fix. To credit a sale by hand, press Attribute on its row and pick the partner. That also picks up every other missed invoice from that customer.

Test mode

Everything above works the same in Stripe test mode.

  1. Connect a test account.
  2. Click your own partner link.
  3. Pay with the test card.
  4. Watch the sale land as attributed.

The quickstart runs this loop end to end.

Attribution is the same in test mode. Money isn't, on purpose. Stripe tells us whether a payment came from test or live, and we keep that on the sale and on its commission.

  • A test sale carries a Test chip in the commissions queue, so you can see your setup working.
  • It never adds to a partner's balance or enters a payout batch.
  • It's left out of every figure that states money: your revenue, your commissions and what you owe.

So you can rehearse on your real program, with your real partners and rules, and nobody is owed a penny for a payment that never happened.