Commission rules
Define what partners earn per payment with versioned rules: rates, recurring windows, holds, overrides, conditions, tiers, bounties, sub-affiliates, partner groups and promotions.
A commission rule tells Referalli what a partner earns when a referred customer pays. Rules are versioned per program: publishing a rule creates the next version, and every payment uses the latest version at the time it is processed. Lines earned under an older version keep a reference to the version that produced them.
Publish a rule
curl -X POST https://referalli.com/v1/commission-rules \
-H "Authorization: Bearer $KEY" \
-H "content-type: application/json" \
-d '{
"programId": "prg_...",
"config": {
"base": { "kind": "percent", "bps": 2500 },
"appliesTo": "net_of_tax",
"recurring": { "type": "months", "months": 12 },
"holdDays": 30
}
}'
# { "id": "crv_...", "programId": "prg_...", "version": 2, "config": { ... } }The response echoes config with defaults filled in. GET /v1/commission-rules?programId=… lists every version, newest first. Publishing requires the owner or admin role.
A program with no rule records conversions but earns no commissions.
Rule config reference
base (required)
What a payment earns before any modifiers.
Shape | Meaning |
|---|---|
| A percentage in basis points, 0 to 10000. 2000 = 20%. |
| A flat amount per qualifying payment. |
Percentages are computed in integer minor units with half-even rounding.
appliesTo
The amount a percentage is applied to. Default gross.
Value | Basis |
|---|---|
|
|
|
|
|
|
recurring
Which payments from a customer qualify. Default { "type": "lifetime" }.
Value | Qualifying payments |
|---|---|
| Only the customer's first payment. |
| Every payment inside the referral's window. |
| Payments in the first N calendar months after the first payment (1 to 120). |
All of these are also limited by the program's signupToConversionDays.
"First payment" means the customer's first payment of any kind, including payments made before the referral existed. If associate runs late (queued after an outage, or a backfill) and the customer already paid, their next payment is not their first, so a one_time rule pays nothing for it and a months window counts from that earlier payment.
holdDays
Days a new line stays pending before it can be approved, 0 to 365. Default 30. The hold gives refunds and chargebacks time to arrive before money is committed.
rounding
How percentage lines round to whole minor units: half_even (the default, banker's rounding) or floor (always down). For 5% of 3333, half_even gives 167 and floor gives 166. It applies to the base rate, tiers, new-vs-expansion rates and sub-affiliate shares.
overrides
Different rates for specific partners, links or products. Each override has a match and its own base:
"overrides": [
{ "match": { "partnerId": "ptr_vip" }, "base": { "kind": "percent", "bps": 3000 } },
{ "match": { "productRef": "pro_enterprise" }, "base": { "kind": "fixed", "amountMinor": 50000, "currency": "usd" } }
]When several match, the most specific wins: partner and link together, then link, then partner, then product. An override can also carry countries and metadata conditions (see conditions); it then applies only to payments that meet them, and beats an otherwise equally specific override without conditions. An override with only conditions applies to every partner when its conditions hold:
"overrides": [
{ "match": { "countries": ["BR", "MX"] }, "base": { "kind": "percent", "bps": 1000 } },
{ "match": { "metadata": [{ "key": "interval", "equals": "year" }] }, "base": { "kind": "percent", "bps": 3000 } }
]Which rate applies
For each payment, the first of these that applies sets the rate:
A matching override (partner and link, link, partner, product, or a conditional override).
An active promotion that replaces the rate.
The partner's group rate.
The rule's
base(withtiersornewVsExpansion, which only adjust the rule's own base).
A promotion that adds percentage points (boost) is added on top of whichever percent rate step 2 to 4 chose, capped at 100%.
conditions
The rule pays only for payments that meet every condition given:
"conditions": {
"countries": ["US", "CA"],
"excludeCountries": ["DE"],
"metadata": [
{ "key": "plan", "in": ["pro", "team"] },
{ "key": "channel", "equals": "web" }
]
}Field | Meaning |
|---|---|
| ISO 3166-1 alpha-2 codes. The payment's customer must be in one of them. A payment whose country is unknown does not match. |
| The customer must not be in any of them. An unknown country passes. |
| Each entry names a billing-event metadata |
The customer's country comes from the billing event's metadata.country (or metadata.customer_country), otherwise from the geo of the click the referral was attributed to. With the generic connection, send it as metadata.country.
tiers
Raise the percentage as a partner brings in more paying customers in a calendar month:
"tiers": [
{ "minCustomers": 10, "bps": 2500 },
{ "minCustomers": 50, "bps": 3000 }
]The partner gets the rate of the highest tier whose minCustomers they have reached this month, counting distinct referred customers with a payment. Tiers apply to percentage bases only.
newVsExpansion
Separate rates for new revenue and expansion revenue. When set on a percentage rule, it takes precedence over tiers and the base percentage:
"newVsExpansion": { "newBps": 2000, "expansionBps": 1000 }A payment counts as expansion when its billing event metadata has expansion: true (for Stripe, invoice metadata expansion set to "true").
bounties
Fixed amounts for events other than payments:
"bounties": [
{ "event": "signup", "amountMinor": 500, "currency": "usd" },
{ "event": "trial", "amountMinor": 1000, "currency": "usd" }
]
| When it pays |
|---|---|
| Once per referral, when the referral is created with status |
| Once per referral, on the customer's first trial start: a billing |
| activation or a custom name | Once per referral, when you report that event for the customer with POST /v1/track/events (below). Custom names use lowercase letters, digits, _, . and -, up to 64 characters, for example first_project. |
Each bounty line is held for the rule's holdDays (or the partner's group's), counted from the signup, trial start or event time, like any other line. Its memo is the event name followed by bounty, for example activation bounty. A rule may list each event once; listing one twice fails with 400.
Report a product event
curl -X POST https://referalli.com/v1/track/events \
-H "Authorization: Bearer $KEY" -H "content-type: application/json" \
-d '{
"type": "event",
"programId": "prg_...",
"customerRef": "cus_123",
"event": "activation",
"occurredAt": "2026-11-10T12:00:00Z",
"idempotencyKey": "activation:usr_8812"
}'{
"event": "activation",
"recorded": true,
"referralId": "ref_...",
"duplicate": false,
"commissionId": "cme_...",
"amountMinor": 1500,
"currency": "usd"
}The event is recorded once per referral per name. Repeating it, or repeating the
idempotencyKey, returnsduplicate: trueand pays nothing.recorded: falsewithreferralId: nullmeans no referral in the program has that customer: an organic customer, not an error.A referral that isn't
active(paused, held for review) records the event but earns no bounty, as does a partner who can't earn.occurredAtdefaults to now; more than 5 minutes in the future is400. The bounty emitscommission.earnedlike any other line.
subAffiliate
Pay the partners who recruited the earning partner. levels (1 to 5) is how far up the chain to pay, and ratesBps gives one rate per level, applied to the same basis as the main commission:
"subAffiliate": { "levels": 2, "ratesBps": [500, 200] }Each upline line is recorded with its level (1, 2, …). ratesBps must have at least levels entries or the rule is rejected with 400.
Partner groups
A group gives a set of a program's partners their own terms. Create one with POST /v1/programs/{id}/groups, change it with PATCH /v1/partner-groups/{id} (archived: true archives it), and list them with GET /v1/programs/{id}/groups:
{
"name": "VIP",
"base": { "kind": "percent", "bps": 2500 },
"holdDays": 7,
"minThresholdMinor": 2000,
"payoutCurrency": "eur",
"onboardingMessage": "Welcome to the VIP program!",
"onboardingChecklist": ["Add your channel", "Share your first link"]
}Field | Effect on members |
|---|---|
| Their commission rate, instead of the rule's |
| How long their lines are held, instead of the rule's. |
| Their payout threshold and currency in payout runs, instead of their payout profile's. A partner in groups in several programs gets the lowest group threshold, and the groups' currency when the groups agree on one. |
| Shown on their partner portal home. |
Put a partner in a group with PUT /v1/programs/{id}/partners/{partnerId}/group and { "groupId": "grp_..." } (null removes them). A partner is in at most one group per program and must be enrolled in it. Creative assets created with a groupId are shown in the portal to that group's members only. Partners see their groups at GET /v1/partner/groups.
Promotions
A promotion changes the commission for payments inside a time window, for every partner or one group. Create one with POST /v1/programs/{id}/promotions:
{
"name": "Black Friday",
"groupId": null,
"effect": { "kind": "boost", "bps": 1000 },
"startsAt": "2026-11-27T00:00:00Z",
"endsAt": "2026-12-01T00:00:00Z"
}effectis{ "kind": "boost", "bps": … }to add percentage points to the partner's percent rate, or{ "kind": "replace", "base": … }to use another rate (percent or fixed) instead.It applies to payments whose time falls in
[startsAt, endsAt), judged by the payment's own time, so a late webhook for a payment made during the promotion still gets it.With
groupId, only that group's members get it; a group promotion wins over an all-partner one running at the same time.POST /v1/promotions/{id}/endends it now.GET /v1/programs/{id}/promotionslists them withstatus(scheduled,activeorended). The commission line's memo names the promotion.
Rewards for the referred customer
Double-sided programs also reward the customer who was referred. Set it per program; it is not part of the rule and never creates a partner ledger line:
curl -X PUT https://referalli.com/v1/programs/prg_.../referee-reward \
-H "Authorization: Bearer $KEY" \
-H "content-type: application/json" \
-d '{"kind": "store_credit", "bps": 200, "capMinor": 500, "on": "first_payment"}'Field | Meaning |
|---|---|
| How you deliver it: |
| A flat reward, in minor units of |
| A percent reward instead: basis points of the customer's first payment, in that payment's currency. Can't be combined with |
| Upper bound for a percent reward. |
|
|
|
|
|
|
A referral earns at most one reward. Each one is listed by GET /v1/rewards and emits referee_reward.earned with customerRef, amountMinor, currency and a code, so you can credit the customer. Nothing is issued while the referring partner can't earn.
Partners who can't earn
What a partner earns depends on their status in your org:
Status | New lines and bounties | Payouts |
|---|---|---|
| Recorded. | Paid. |
| Recorded as usual, so nothing is lost. | Held: payout runs skip them with |
| Not recorded, and nothing is recorded later to make up for it. | Never paid while the status or flag stands. |
A banned status applies only in your org, and you can lift it with PATCH /v1/partners/{id} ({ "status": "active" }). A flag is set on the partner's Referalli account by a platform admin, never by a merchant, and blocks them with every merchant until the platform lifts it.
When the referring partner is banned or flagged, a payment by their referral records the conversion but no lines at all, so sub-affiliate partners above them get no override either. When only an upline partner is banned or flagged, just that partner's share is left out. What a blocked partner earned before stays in the ledger and is held from payouts (see Payouts).
Holdout groups
PUT /v1/programs/{id}/incrementality with { "holdoutBps": 500 } (0 to 5000) puts a stable share of a program's referrals, here 5%, into a control group. Their payments are recorded as conversions but earn no commission, which lets you measure how much partner-driven revenue is truly incremental.