Stripe, field by field

Stripe has no affiliate feature. Here is what tracks the sale instead

By Jimi Barkway · Published 4 September 2026 · Part of the blog

The short answer

No. There is no affiliate object in the Stripe API and no partner screen in the Dashboard. What Stripe gives you is four parts that affiliate tracking is built from: client_reference_id on the checkout, up to 200 characters, metadata on the objects a checkout creates, promotion codes on the payment, and three webhook events. AffiliateRail's whole tracker is 5,106 bytes, which tells you attribution is the cheap part. The ledger, the holds, the clawbacks, the payouts and the tax forms are the rest.

Does Stripe have an affiliate program feature?

In shortNo. There is no affiliate object in the Stripe API. No partner screen in the Dashboard. Nothing in the docs that pays a commission. The App Marketplace lists other people's apps that do it, which is Stripe pointing outward rather than shipping the thing.

Worth being blunt about, because it is the question behind the question. Search the API reference for a partner, an affiliate or a commission and you get nothing back at all, and there is no guide in the docs, no screen in the Dashboard and no setting anywhere that turns one on. Stripe bills, handles disputes, works out tax and pays out. It does not run programs.

What Stripe gives you instead is better than a half-built feature, and almost nobody explains it. Four small things. Between them they carry a referral from a click to a commission, and back again through a refund.

So the honest answer is no. The useful answer is that Stripe has the parts. Here is each one, and what it costs to put them together.

What does Stripe give you instead?

In shortA reference field on the checkout, metadata on the objects a checkout creates, promotion codes on the payment, and three webhook events. That is the whole toolkit, and every affiliate tool for Stripe is built on it.

The four Stripe features affiliate tracking is built from, and what each does
The partWhat it isWhat it does for a program
client_reference_idA string on a Checkout Session, up to 200 charactersCarries the referral through the payment. Comes back on the webhook
metadataKey and value pairs on the session, and separately on the subscription or payment intent it createsThe server-side path, for a checkout you build yourself
Promotion codesA customer-facing code, referenced in the session's discountsCredits a partner whose audience never clicked a link
Three eventscheckout.session.completed, invoice.paid, charge.refundedBind the customer, book the money, take it back

The reference field carries the weight. Stripe defines it as a unique string for the session. It "can be a customer ID, a cart ID, or similar". The limit is 200 characters.

A unique string to reference the Checkout Session. This can be a customer ID, a cart ID, or similar, and can be used to reconcile the session with your internal systems. The maximum length is 200 characters.

client_reference_id, Create a Checkout Session, Stripe API reference, checked 4 September 2026

Read that again with a program in mind. A click id is a unique string. It fits in 200 characters with room to spare, and it comes back to you on the event that fires when the payment succeeds. Affiliate tracking on Stripe is that one sentence, used for something Stripe did not have in mind.

One trap in the metadata line. A Checkout Session has its own metadata. Then subscription_data and payment_intent_data are separate, and they pass fields to the objects the session makes. So a value on the session is not on the subscription. Put the referral in both, or read it from the session and never from the renewal.

How does the referral get onto the checkout?

In shortEither a script writes it on the page before the buyer clicks through, or your server sets it when you make the session. The script path needs no code from you. The server path is three lines, and the one to use if you build your own checkout.

The no-code path is the interesting one. It has to survive whatever your page is made of. AffiliateRail's tracker stamps the click id onto three Stripe surfaces: a stripe-pricing-table element, a stripe-buy-button element, and any link to buy.stripe.com, where it goes on as a query parameter.

Two details in there are the difference between tracking that works and tracking that mostly works.

The first is timing. A pricing table that draws itself after the page has loaded. A button inside a modal. A plan picker that swaps its links when the buyer flips monthly to yearly. All of those arrive after a script has finished its first pass. AffiliateRail watches the page for new nodes and stamps whatever turns up, whenever it turns up. The scripts captured from elsewhere in this category use a timer instead, and one of them gives up after ten ticks of a half-second interval, which is five seconds and then nothing.

The second is restraint. Say the merchant has already set their own reference on that button, to match the sale to their own records. An affiliate tool must not overwrite it. AffiliateRail does not. One line of code, and it saves somebody's books.

Two warnings from Stripe's own page on Payment Link parameters. A reference can hold letters, digits, dashes and _, up to 200 characters. Anything invalid is "silently dropped" while the payment page carries on working, so a broken value never announces itself. It stops crediting anyone and says nothing. Stripe also warns that a link carrying the parameter "might show up in unexpected places", so the value must hold nothing secret. A click id is safe. A customer email is not.

Which webhook events do you actually need?

In shortThree. checkout.session.completed binds the customer to the partner. invoice.paid is the one that books money, and it fires on every renewal. charge.refunded takes the commission back.

Most people wire the first one and stop there. Then the first renewal lands and nobody is credited, because the session completed once and never again, and the invoice that arrives a month later has no memory of the checkout that started the subscription. Recurring commission lives on invoice.paid. That is why the partner has to be stored against the customer, not against the payment.

The refund event is where a program earns or loses trust, and it has two cases. A commission not yet paid out is voided. One already sent becomes a negative amount, taken off the partner's next payout. AffiliateRail matches the refunded charge to its invoice, or to the payment intent the sale stored when it arrived, then does one or the other. A customer is marked refunded only once no live sale of theirs is left. One refund among several payments is not a lost customer.

Cancellations matter too, and they are quieter. A subscription that ends stops sending invoices, so there is nothing to undo. What you want is the status on the customer. Then a partner's page shows who is still paying, not who once did.

There is a fourth path worth knowing, and it needs no click at all. A promotion code mapped to a partner attributes the sale on its own. The coupon page covers the mapping and the order of precedence when a buyer arrives with both.

Why does Stripe's connect screen ask for write access?

In shortBecause Stripe's read-only scope is open only to Connect apps registered as extensions. A tool registered as a platform has to ask for read and write, whatever it means to do. Check the key, not the consent screen.

This surprises people, and it is better said out loud than left on a screen. Stripe's OAuth reference says read_only "can only be specified for extensions". The page it links to is plainer: "Only Extensions can use read_only, which ensures that platforms can't read other applications' data." A platform that needs the extension behaviour has to ask Stripe to change its registration. Nothing on either page says the scope was retired.

So a tool connected to your Stripe was almost certainly granted write access, and the promise that it never writes has to rest somewhere else. It rests on the key. AffiliateRail points a restricted key at a merchant's account. It can read charges, payment intents and invoices, and write nothing at all. Stripe enforces the promise, not good intentions.

Stripe agrees, in the plainest words it uses anywhere. On its keys page, a restricted key exists to "Limit the damage to your business that a fraudulent actor could cause if they obtained your key". Of the unrestricted kind it says: "Because you can't limit their permissions, we don't recommend using secret keys for new use cases."

Two questions to ask any tool you are about to connect: which key touches my account, and what can that key do. Any honest answer is short. If the answer is a shrug, or a paragraph about how much the company values your trust, you have learned what you needed to know and it took one question.

Run the affiliate program Stripe leaves you to build

One script tag, a key that can only read, and the ledger and payouts behind it. Fourteen days, no card.

Start free trial

Could you build this yourself?

In shortThe tracking, yes, in a weekend. A few kilobytes of script and one webhook handler. Tracking is not the hard part, and that is the thing worth knowing before you decide.

Here are the numbers, measured on the day this page was written. AffiliateRail's whole tracker is 5,106 bytes built, against a limit that fails the release at 5 KB, and the file served live is the same 5,106 bytes. The two rival trackers captured for this repo weigh 21,235 bytes and 4,256 bytes. The largest is four times the smallest. All three do the same job, so nobody in this category is winning on attribution.

The rest is where the time goes. Start with the ledger. Every commission needs one reason and one amount, and a partner who asks why theirs was twelve dollars and not nineteen should be able to read the answer rather than wait for you to go digging. Then a hold, so nothing is payable before your refund window closes. Clawbacks that survive a refund landing after a payout. Payouts grouped by partner, by currency, by term. Tax forms in first. A portal your partners trust enough to stop emailing you. Fraud rules that know a popular coupon from an abused one.

None of it is Stripe's job. None of it is in those four fields. That is the honest shape of the build-or-buy call: attribution is the cheap part you could write yourself in a weekend, and everything above is the reason almost nobody does.

To see it rather than read about it, the Stripe integration page has the script tag, the server-side snippet and the detail field by field. It behaves the same in test mode, because the event shapes are identical.

Common questions

Where in Stripe would an affiliate feature be, if it existed?

Nowhere, and that is checkable rather than a matter of opinion. There is no affiliate, partner or commission object in the API reference, no such screen in the Dashboard, and no guide in the docs. The App Marketplace lists apps that do it, which is Stripe pointing at other people's software rather than shipping its own.

Does this work with Payment Links, Pricing Tables and Buy Buttons?

All three, and they are the reason the no-code path exists. A link to buy.stripe.com takes the reference as a URL parameter, and the two embedded elements take it as an attribute. The catch is timing: anything that renders after the page loads has to be stamped when it appears, not on a timer that has already stopped.

What happens to the commission when a customer refunds?

It follows the money. A commission not yet paid out is voided, and one already sent becomes a negative amount that comes off the partner's next payout. The customer is only marked refunded once none of their payments are live, because one refund among several is not a lost customer.

Do I need a Stripe API key of my own to run a program?

Not for the tracking. The connection is made through Stripe's own consent screen, and the key that reads your account belongs to the tool. Ask which key it is and what that key can do: a restricted key that reads charges, payment intents and invoices and writes nothing is the answer you want.

Is a webhook enough on its own, with no script on the site?

For the server-side path, yes. If you create your own Checkout Sessions you can set the referral in metadata yourself and never load a script. The script exists for the no-code surfaces, where there is no server of yours in the flow to set anything.

Can I test all of this without taking real money?

Test mode behaves identically, which is the point of proving it there first. The event shapes are the same, so a test click, a test card and a test refund exercise the same path a live sale will take.

Where these facts come from

Every figure above was read off the document named here on the date beside it. If one has moved on, email support@affiliaterail.com and we will correct the figure and move the date.

Start

Stripe has the parts. This is the rest of it

One script tag, a restricted key that can only read, and the ledger, holds, clawbacks and payouts behind it. No card for fourteen days. Full refund within 30 days of your first payment.