Run a referral program
Let customers share a link and earn wallet credit, make partner codes that pay a commission, void a referral, and read what the program has paid.
In the dashboard
A referral starts when a customer of your storefront shares a link with their code and the person who follows it pays a first invoice. The customer who shared the code earns wallet credit. You can also pay the new customer a welcome credit.
The program is off until you turn it on. Your team runs it in the staff console, under Marketing, as Run campaign links and referrals in the staff console describes. The owner and scripts use the API, which this page describes.
Before you begin
Section titled Before you begin- You need the Admin or Owner role in the organization. Members with any other role cannot read or change the program.
- Your storefront must be one that Coritan serves, so the order records the code the visitor arrived with. How customers and orders are attributed explains what an order records.
- The reward is wallet credit that you give. A customer spends it on an invoice like any other credit, and an invoice paid from credit is not part of a payout.
Set it up in your dashboard
Section titled Set it up in your dashboardOwners and admins run the program in the dashboard. Your team can do the same in the staff console, as Marketing campaigns and referrals in the staff console describes. A member with another role does not see the page, and its address opens Overview.
- In the dashboard, open the organization, then Settings, then Referrals.
- Under Program, turn on Run the referral program. While it is off the page says so, and nothing is made or paid.
- Set "Reward for the referrer", "Welcome credit for the new customer", "First invoice must reach", "Days before a reward is paid" and "Most rewards for one customer in 30 days". Each hint shows the range the API allows. Amounts are in US dollars, the unit of a customer's wallet, and the first invoice is counted without tax. Turn Refuse orders that came from a paid ad on or off.
- Select Save changes. The page says "Referrals turned on." when the switch was the only change, and "Referral settings saved." otherwise. A value the API refuses shows its sentence under the input. A new amount applies to referrals that start after you save it, and a referral already waiting keeps the amounts it started with.
- Read the tiles under the card: Qualified, Credited and Void, and Pending when a referral is in that state. Each shows how many referrals are in it, with what is owed or paid out. Select a tile to filter the list below it, and select it again to clear the filter.
Referrals lists each referred customer, newest first, with who referred them, the code that was used, their first invoice, the reward and the welcome credit, and the status. A qualified referral says when it pays, such as "Pays in 2 days". A void one says why: "The same customer", "Both addresses reach one mailbox", "Signed up from the same network", "Signed up on the same device", "The order came from a paid ad", "An account is closed, suspended or banned", "The referrer reached the monthly cap", "The first invoice was refunded", "The first invoice was charged back", or the reason a person gave. Search by either customer's email, or choose a status. The search, the status and the page stay in the address.
To void a referral that has not been paid, open its actions menu and select Void referral…. Type the reason, 1 to 120 characters, and select Void referral. The reason is kept with the referral. A referral that is paid or void cannot be voided, and money already paid is not taken back. You can void 60 an hour.
Codes lists every code with its owner, whether it is a customer's own or a partner's, what it pays, how many referrals it started and whether it is Active or Stopped. Select New partner code…, type the customer's "Customer email", an optional "Code" and "Label", and the "Share of the first invoice" as a percent from 0.01 to 50, then select Create code. The page turns the share into basis points for the API. You can make 30 codes an hour. In a partner code's actions menu, Edit… changes its share and label, Stop code… stops it from starting new referrals and Restart code starts it again. Referrals a stopped code already started still pay.
How a referral works
Section titled How a referral worksEvery amount on this page is in wallet dollars, the unit a customer's wallet is kept in (US dollars), whatever currency the customer is billed in.
Codes and links
Section titled Codes and links- A customer's own code is made the first time they open their referral page, and only while the program is on. It is 8 characters, capital letters and digits without the look-alikes
I,O,0and1. It never changes. - The link is your storefront's address followed by
/?ref=and the code, such ashttps://shop.example.com/?ref=K7M2Q9XA. - The storefront keeps the code for 30 days in the visitor's browser, in a cookie named
_ct_ref, and every order placed from that browser records it. A storefront you build yourself sets that cookie. A code is at most 40 letters, digits, hyphens and underscores. - A code is found without regard to capitals, and it belongs to one organization. A code of another organization never starts a referral on yours.
- A partner code is a code that you make for a customer, as Partner codes describes. It pays a share of the first invoice instead of a fixed reward.
What starts a referral
Section titled What starts a referralA referral starts when an invoice is paid and every one of these is true:
- The program is on.
- The order recorded a code of your organization, and the code is not stopped.
- It is the paying customer's first paid invoice with a total above 0. A free invoice, an unpaid one or a later one does not count as an earlier paid invoice, but a refunded one does.
- Money passed through a payment gateway. An invoice that a wallet paid in full, or that your team marked as paid, never starts a referral.
- The invoice total without tax, converted to wallet dollars, is at least
threshold_amount. - None of the rules in What stops a referral applies.
The customer is referred once. A second code, or a second invoice, starts nothing, and a referral that was voided is not started again. A paid ad order is refused only while block_paid_ads is on.
A referral that is refused before it starts leaves no row, so it does not appear in the list. We write the reason to our logs. Troubleshooting lists the reasons.
The hold
Section titled The holdA new referral is qualified and waits hold_days days before it is paid. A refund or a lost chargeback on the invoice during the hold voids it. You can void it by hand as well.
How it is paid
Section titled How it is paidOnce an hour we pay every qualified referral whose hold has passed, at most 200 each time, oldest first. We check every rule again first, so a referral that no longer passes is voided instead of paid. One organization's referrals are paid by one worker at a time.
For each referral we pay the customer who shared the code reward_amount, or for a partner code its commission, and the referred customer referee_reward_amount when it is above 0. Both credits are written together or not at all. Each is a credit_add entry in the customer's wallet history with no gateway and the reference referral:<referral id>:<customer id>, so a second run pays nothing. The referral becomes credited.
After the credit is saved, we email each customer who was paid. These are service emails, so they go to a customer who has not agreed to marketing, and they skip an address on your suppression list. If an email fails, the credit stays.
What stops a referral
Section titled What stops a referralThese rules are checked when the referral starts and again when it is due. A referral that fails one when it is due is void, and void_reason holds the word.
void_reason |
The rule |
|---|---|
self |
The customer who shared the code is the customer who paid. |
same_mailbox |
The two email addresses reach one mailbox. We ignore capitals, cut +tag off the part before the @, and for gmail.com and googlemail.com also ignore dots and treat the two domains as one. |
same_network |
Both accounts were made from the same network address, within 30 days of each other. |
same_device |
Both accounts were made from the same browser fingerprint. |
paid_ad |
The order's last channel is paid_search, paid_social or display, and block_paid_ads is on. |
closed |
Either account is closed, banned or suspended. |
cap |
The customer who shared the code already has monthly_cap referrals paid in the last 30 days. |
refunded |
The invoice is no longer paid in full: it was refunded, or part of it was. |
chargeback |
A chargeback on the invoice was lost. |
A referral that staff void by hand keeps the words they gave as void_reason.
Turn the program on
Section titled Turn the program onIn the staff console, under Marketing, turn the program on and set its amounts. With the API, send only the settings you want to change:
curl -X PUT "https://api.coritan.com/api/v1/orgs/acme/referrals/settings" \
-H "Authorization: Bearer $CORITAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": true, "reward_amount": "5.00", "hold_days": 7}'
The answer holds every setting, the wallet currency, the range of each number and changed, the keys that differ from before:
{
"enabled": true,
"reward_amount": "5.00",
"referee_reward_amount": "0.00",
"threshold_amount": "5.00",
"hold_days": 7,
"monthly_cap": 20,
"block_paid_ads": true,
"currency": "USD",
"limits": {"hold_days": {"min": 1, "max": 90}},
"changed": ["enabled", "hold_days"]
}
limits lists every number's range. This example shows one.
| Setting | Default | What it does |
|---|---|---|
enabled |
false |
Turns the program on. While it is off nothing starts and no code is made. |
reward_amount |
"5.00" |
What a customer's own code pays the customer who shared it. An amount from 0 to 1000 with at most 2 decimals. |
referee_reward_amount |
"0.00" |
A welcome credit for the referred customer. An amount from 0 to 1000. 0 pays none. |
threshold_amount |
"5.00" |
What the referred customer's first paid invoice must reach, without tax. An amount from 0 to 10000. |
hold_days |
14 |
Days a referral waits before it is paid, from 1 to 90. |
monthly_cap |
20 |
The most referrals one customer is paid for in 30 days, from 1 to 500. |
block_paid_ads |
true |
Refuses a referral whose order came from a paid ad. |
Amounts are decimal strings. A value that does not fit, or a name that is not a setting, answers 422 with detail, the sentence to show, and field, the setting it belongs to, and nothing changes. A change is recorded in the audit log as referrals.settings_updated, with the settings before and after. A save that changes nothing is not recorded.
A new amount applies to referrals that start after the change. A referral that is already waiting keeps the amounts it started with. The new hold_days also applies only to referrals that start after the change.
Partner codes
Section titled Partner codesA partner code pays a customer a share of every first invoice it brings, instead of the fixed reward. Use it for a streamer, a newsletter author or a community owner who already holds an account on your storefront. Your team makes the code in the staff console, under Marketing.
With the API, name the owner by customer_id or email, and give commission_bp, the share in basis points. 100 is 1 percent, and the range is 1 to 5000. The share is taken from the invoice total without tax, in wallet dollars, and kept to four decimal places. The owner must be an active customer of this organization.
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/referrals/codes" \
-H "Authorization: Bearer $CORITAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email": "pat@example.com", "code": "PAT-LAUNCH", "commission_bp": 1250, "label": "Pat, launch week"}'
{
"id": 41,
"code": "PAT-LAUNCH",
"kind": "partner",
"customer": {"id": 12, "email": "pat@example.com", "name": "Pat Tester"},
"commission_bp": 1250,
"label": "Pat, launch week",
"active": true,
"referrals": {"total": 0, "credited": 0, "earned": "0.00"},
"created_at": "2026-10-11T12:00:00"
}
codeis optional: 3 to 40 letters, digits, hyphens or underscores. Left out, we make an 8 character one. A code that is in use answers409, and two codes that differ only in capitals are one code.labelis a note for your team, at most 120 characters.- You can make 30 codes an hour for each member.
PATCH /referrals/codes/{code_id}changesactive,commission_bp(a partner code only) andlabel. The code never changes.active: falsestops the code from starting new referrals, and referrals it already started still pay.GET /referrals/codeslists customer codes and partner codes, newest first, withkind,active,q(part of the code, its label or the owner's email) andlimit(at most 200). Each code showsreferrals: how many it started, how many were paid and what they paid the owner.
The code is recorded as referral_code.created, and a change as referral_code.updated, with the values before and after.
Read the referrals
Section titled Read the referralsGET /referrals lists the referrals of your organization, newest first. Each row names the referrer and the referred customer, the code, the invoice that started it, the rewards, the status and the dates. Filter by status (pending, qualified, credited or void), and by q, which matches either customer's email address without regard to capitals. limit is at most 200.
curl "https://api.coritan.com/api/v1/orgs/acme/referrals?status=qualified&limit=20" \
-H "Authorization: Bearer $CORITAN_TOKEN"
{
"referrals": [
{
"id": 118,
"status": "qualified",
"referrer": {"id": 7, "email": "ada@example.com", "name": "Ada Tester"},
"referee": {"id": 9, "email": "sam@example.com", "name": "Sam Tester"},
"code": {"id": 4, "code": "K7M2Q9XA", "kind": "customer"},
"invoice": {"id": 310, "number": "INV-000310"},
"reward_amount": "5.00",
"referee_reward_amount": "0.00",
"currency": "USD",
"void_reason": null,
"qualified_at": "2026-10-11T12:00:00",
"hold_until": "2026-10-25T12:00:00",
"credited_at": null,
"created_at": "2026-10-11T12:00:00"
}
],
"count": 1,
"limit": 20,
"offset": 0,
"totals": {"currency": "USD", "pending": 0, "qualified": 1, "credited": 0, "void": 0, "credited_amount": "0.00", "owed_amount": "5.00"}
}
count is how many referrals match the filters. totals covers the whole program and ignores them: the number of referrals in each status, credited_amount paid out so far and owed_amount for the open ones.
Void a referral
Section titled Void a referralA referral that you do not want to pay can be voided while it is pending or qualified. A referral that is already credited or void answers 409, and money already paid is not taken back.
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/referrals/118/void" \
-H "Authorization: Bearer $CORITAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reason": "Same household"}'
reason is required, 1 to 120 characters. The answer is {"id": 118, "status": "void", "void_reason": "Same household"}. You can void 60 referrals an hour for each member. The void is recorded in the audit log as referral.voided, with the reason.
What customers see
Section titled What customers seeA signed-in customer reads their own page with GET /api/v1/orgs/{org_slug}/portal/referrals. It answers:
{
"enabled": true,
"code": "K7M2Q9XA",
"link": "https://shop.example.com/?ref=K7M2Q9XA",
"reward_amount": "5.00",
"referee_reward_amount": "0.00",
"threshold_amount": "5.00",
"currency": "USD",
"hold_days": 14,
"stats": {"pending": 0, "qualified": 1, "credited": 2, "earned": "10.00"},
"referrals": [
{"id": 118, "status": "qualified", "referee": "Sam", "reward_amount": "5.00", "currency": "USD",
"qualified_at": "2026-10-11T12:00:00", "hold_until": "2026-10-25T12:00:00", "credited_at": null,
"created_at": "2026-10-11T12:00:00"}
]
}
- While the program is off,
enabledisfalseand there is nocodeorlink. No code is made. - The customer sees the last 50 of their referrals. A referred customer shows as their first name, or
Someonewhen there is none. The customer never sees an email address. - A code that you stopped is not shown.
- A support session that signed in as the customer reads the page but does not make a code.
What your customers see on the storefront
Section titled What your customers see on the storefrontA storefront that Coritan serves draws the panel and keeps the link's code for you. This section says what a visitor reads. A storefront you build yourself calls GET /portal/referrals and writes its own words.
The "Refer a friend" panel
Section titled The "Refer a friend" panelThe panel sits in the customer's account, near their wallet or billing settings. It draws nothing while the program is off, and nothing while the customer has no link, so a storefront with the program off shows no gap. When the program is on it shows:
- The words "Share your link. When a friend pays a first invoice of $10.00 or more, you get $5.00 in wallet credit. We add it after a 14 day hold." The amounts and the days come from your settings, in wallet dollars. When
referee_reward_amountis above 0 it adds "Your friend gets $2.00 in wallet credit too." The panel never mentions reviews. - The customer's link and a "Copy link" button. The page says "Link copied." and, when the browser refuses, "Could not copy. Select the link and copy it."
- Three counts: "Waiting" (referrals that are pending or qualified), "Credited" and "Earned".
- "Your referrals": the first five, then "Show all" for up to 50 and "Show fewer". Each row has a first name or "Someone", a status and an amount. The status reads "Waiting for the hold to end" with "Credit on" and the date, "Credited" with the date, or "Not credited" with a dash for the amount. The panel never says why a referral is void.
- Before any referral exists, "No referrals yet. Share your link to get started."
The ?ref= link
Section titled The ?ref= linkA link that ends in ?ref=<code>, on any page of the storefront, keeps the code for 30 days when the program is on:
- The cookie is
_ct_ref, with the path/, a maximum age of 2,592,000 seconds,SameSite=LaxandSecureon https. - A code is 1 to 40 letters, digits,
_and-, and its case is kept. A value that does not fit sets nothing. A long value is dropped whole, never cut. - A newer valid code replaces the older one.
- While the program is off the storefront sets nothing, and the page does not load the code that would.
- The order records the cookie when the customer pays, and that is how a referral finds its code. See How customers and orders are attributed.
The cookie is the only thing the storefront keeps for the program. It is set when a visitor opens a referral link, whatever they chose in the cookie banner, so name _ct_ref in the cookie list of your privacy policy.
Limits
Section titled Limits- A referred customer has one referral, whoever referred them.
- A pass pays at most 200 referrals. The rest wait for the next hour.
- A list returns at most 200 rows.
offsetpages through the rest. - A member can make 30 partner codes and void 60 referrals an hour. Past that, the API answers
429.
Troubleshooting
Section titled Troubleshooting- A referral did not start
- Check the program is on, the order carried the code (the
_ct_refcookie was set when the customer paid), it was the customer's first paid invoice, a gateway took the money, and the total without tax reachedthreshold_amount. A referral that is refused before it starts leaves no row. The reason is in our logs as one ofdisabled,unknown_code,inactive_code,not_first_invoice,not_paid_by_gateway,below_threshold,no_exchange_rate,no_reward, or a word from What stops a referral. - A referral is
voidand nobody voided it - Read
void_reason. It is one of the words in What stops a referral. Money is never paid for a voided referral. - A referral is still
qualifiedafter its hold - We pay once an hour, at most 200 at a time, and an organization that is not active waits. Check again in an hour.
409when voiding- The referral is already
creditedorvoid. Read the list for its status. 422on a save- The answer's
fieldnames the setting or input, anddetailsays what fits.
Related
Section titled Related- How customers and orders are attributed: where the code on an order comes from.
- Make campaign links and read their results: links with UTM words and a coupon.
- Customers: add credit to a wallet by hand.
- Audit log: every change to the program is recorded there.