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.

Embed
Preview of the card
<a href="https://referalli.com/docs/commission-rules"><img src="https://referalli.com/docs/_api/public/orgs/07f432f4-34b9-46a7-aaad-c0e395d2e4f9/spaces/2bf4f614-3630-4fbf-85f9-5ecafa6b7d97/pages/434727ae-9542-4878-a2e7-67be39bfa5fc/social-card.svg?v=3e7ae33eec8b" alt="Commission rules" width="600" height="315"></a>

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

{ "kind": "percent", "bps": 2000 }

A percentage in basis points, 0 to 10000. 2000 = 20%.

{ "kind": "fixed", "amountMinor": 1500, "currency": "usd" }

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

gross

amountGross

net_of_tax

amountGross - tax

net_of_fees

amountGross - tax - fees

recurring

Which payments from a customer qualify. Default { "type": "lifetime" }.

Value

Qualifying payments

{ "type": "one_time" }

Only the customer's first payment.

{ "type": "lifetime" }

Every payment inside the referral's window.

{ "type": "months", "months": 12 }

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:

  1. A matching override (partner and link, link, partner, product, or a conditional override).

  2. An active promotion that replaces the rate.

  3. The partner's group rate.

  4. The rule's base (with tiers or newVsExpansion, 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

countries

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.

excludeCountries

The customer must not be in any of them. An unknown country passes.

metadata

Each entry names a billing-event metadata key and either equals one value or is in a list. Values compare as strings, so "1" and 1 match.

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" }
]

event

When it pays

signup

Once per referral, when the referral is created with status active. A referral created as pending_review records its signup conversion but earns no signup bounty.

trial

Once per referral, on the customer's first trial start: a billing sub_created event marked as a trial (Stripe, Paddle, Lemon Squeezy and Chargebee set this from the subscription's trialing status; for the generic API send metadata.trial: true). The referral must be active. Later trial starts for the same referral record a trial conversion but no second bounty.

| 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, returns duplicate: true and pays nothing.

  • recorded: false with referralId: null means 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.

  • occurredAt defaults to now; more than 5 minutes in the future is 400. The bounty emits commission.earned like 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

base

Their commission rate, instead of the rule's base (overrides and promotions still win; see which rate applies). null keeps the rule's.

holdDays

How long their lines are held, instead of the rule's.

minThresholdMinor, payoutCurrency

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.

onboardingMessage, onboardingChecklist

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"
}
  • effect is { "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}/end ends it now. GET /v1/programs/{id}/promotions lists them with status (scheduled, active or ended). 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

kind

How you deliver it: store_credit, coupon or gift_card. Default store_credit.

amountMinor

A flat reward, in minor units of currency. Default 1000 when bps isn't sent.

bps

A percent reward instead: basis points of the customer's first payment, in that payment's currency. Can't be combined with amountMinor.

capMinor

Upper bound for a percent reward.

on

first_payment (the customer's first payment, counting payments made before the referral) or first_commission (the referral's first commission line of any kind, including a signup bounty). Default first_payment for a percent reward, first_commission for a flat one. A percent reward requires first_payment.

rounding

half_even (default) or floor, for a percent reward.

enabled

false turns the reward off.

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

active

Recorded.

Paid.

suspended

Recorded as usual, so nothing is lost.

Held: payout runs skip them with partner_inactive until you set them back to active, then the next run pays everything approved, including what they earned while suspended.

banned or archived, or account flagged

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.

Updated