# Reward rules

Rates per product or plan, groups and levels, bonuses and ceilings, and why every commission is the amount it is.

A referral program pays by **rules**. Each rule says when it applies, what it
gives, and to whom. A program can pay a different rate per product, price, plan
or billing interval, more to a group or level of partners, its own rate to one
partner, a bonus when a partner crosses a milestone, and stop at a ceiling per
customer or per month. Every commission records which rule paid it and why, and
says so in one sentence (`basis_text`).

The rules live under
[`/v1/referrals/programs/{id}/rewards`](https://boltclaw.ai/docs/api/referrals#get-v1referralsprogramsidrewards)
and the groups and levels under `/v1/referrals/programs/{id}/groups`, with
`referrals:read` to read them and `referrals:write` to change them. The dashboard
edits the same rules on a program's **Rewards** tab.

## What a rule is

| Field | |
|---|---|
| `event` | `lead` (a signup), `sale` (a payment) or `bonus` (a milestone). |
| `scope` | Who it pays: `program` (everyone), `group` (a group or level, with `group_id`) or `partner` (one partner's own rate, with `partner_id`). |
| `kind` | `percent` (`percent_bps`: 2000 is 20%) or `flat` (`amount_minor`, in the program's currency). A signup and a bonus are always flat. |
| `duration` | `once` per customer, `months` (with `months`, 1 to 120) or `lifetime`: for as long as the customer pays. |
| `conditions` | When it applies, below. None at all makes it the **default** for its event and audience. |
| `cap_minor` | The most it pays for one customer, in total. The commission that reaches it is cut to what is left, and says so. |
| `priority` | Breaks a tie between two equally specific rules. Higher wins. |
| `label` | Your own words for when it applies ("Yearly plans"). Partners see the label, never your product or price ids. |

Each audience may have **one default rule per event**: a second answers
`409 CONFLICT`. Rules with conditions are as many as you need, up to 200 in a
program.

## Conditions

| Key | Matches when |
|---|---|
| `products` | the line's product is one of these: your own SKUs, or Stripe's `prod_…` |
| `prices` | the line's price is one of these: your own plan ids, or Stripe's `price_…` |
| `plans` | the line's plan name is one of these, without regard to case |
| `intervals` | the line is billed `month`, `year` or `one_time` |
| `purchase` | the sale is the customer's `first` payment, or a `renewal` |
| `currencies` | the sale is in one of these (ISO 4217) |
| `min_amount_minor` | the sale is at least this |

Every key a rule names must match. A sale reported **without** `items` matches
no rule that names `products`, `prices`, `plans` or `intervals`; it is matched as
a whole, by the rules that do not. "First payment" is the first sale Bolt Claw
records for that customer in the program, decided when the sale is recorded, so
two payments arriving at once cannot both be first.

## Which rule pays

For each line of a sale (or the whole sale, when it carries no lines):

1. The partner's own rules are asked first, then their group's or level's, then
   the program's.
2. The first of those that has a rule **matching** the line wins.
3. Inside it, the rule matching the most condition keys wins, then the higher
   `priority`, then the older rule.
4. Lines won by the same rule are added together into one commission for it.

So "Gold partners get 25%" and "yearly plans pay 30% for everyone" combine the
way you would expect: a Gold partner's monthly line pays the Gold rate, and
their yearly line pays 30% only if Gold has no rule of its own for it.

The duration of a rule is counted per rule and per customer, exactly as it
always was: a `once` rule pays a customer's first commissioned sale under it, a
12-month rule pays for twelve months from that sale.

## Sales with lines

Report a sale's lines with `items`, each with its share of the amount. They
must add up to the sale's `amount` (`400` otherwise):

```json
{
  "program_id": "6f0e3b9a-4c21-4d8e-9a57-0b3c2d1e4f60",
  "event": "sale",
  "event_id": "inv_2026_000183",
  "customer": { "id": "cus_acme_0042" },
  "referral": { "code": "jane" },
  "amount": 9800,
  "currency": "USD",
  "items": [
    { "product": "prod_basic", "price": "price_basic_monthly", "interval": "month", "amount": 4900 },
    { "product": "prod_pro", "price": "price_pro_yearly", "plan": "Pro", "interval": "year", "amount": 4900 }
  ]
}
```

With "20% on monthly" and "30% on Yearly plans", this earns two commissions,
980 and 1470, and the answer lists both under `commissions`, each with its
`basis_text`. From a connected Stripe account, an invoice's lines are read for
you.

## Groups and levels

A **group** is a set of partners you place by hand: "Agencies at 35%". A
**level** is a group with a `rank` (1 is the lowest) and, above the first, a
`minimum`:

```json
{ "name": "Gold", "rank": 2, "minimum": { "metric": "revenue", "amount_minor": 500000, "window_days": 90 } }
```

`metric` is `revenue` (in `amount_minor`, the program's currency), `sales` or
`customers` (in `count`), counted **net of refunds** over the last `window_days`
(30 to 365), or all time when it is null. Only the first level (`rank` 1) may
have no minimum. A sale held as fraud, or confirmed as fraud, counts for
nothing. Once a day, each approved partner who
is in no group or on a level moves to the highest level they qualify for. With
`keep_earned` (on by default) nobody is moved down. A partner you place in a
plain group, or pin (`PATCH /v1/referrals/partners/{id}` with `pinned: true`),
stays where you put them. One group may be `is_default`: a newly approved
partner starts there.

Give a group or level its own rates with a rule whose `scope` is `group`. A
partner sees their own group's or level's name, the next level's name and what
is left to reach it; never another partner's level, the list of groups, or how
many people are in one.

## Bonuses

A rule with `event` `bonus` pays a flat amount once when a partner crosses a
milestone:

```json
{
  "event": "bonus",
  "kind": "flat",
  "amount_minor": 5000,
  "label": "5 customers",
  "conditions": { "milestone": { "metric": "customers", "count": 5, "window": "lifetime" } }
}
```

`window` is `lifetime`, or `calendar_month` (UTC) for a bonus that can be earned
again each month. It is paid at the sale that crosses the line, once per window
whatever is redelivered or arrives at the same moment. Like any commission it
waits out the program's hold period: a refund or a dispute in that time that
takes the partner back under the line takes the bonus back
(`referral.commission.reversed`), and crossing again pays it again. Once it is
approved it stays. A partner already past the line when you add the rule is
not paid it: only a crossing pays. A sale held or confirmed as fraud crosses nothing, and a bonus
crossed by the partner's own purchase is held for review like any commission.
It raises `referral.bonus.created`.

A flat fee or a one-off bonus **by hand** ("200.00 for the launch video") is
`POST /v1/referrals/partners/{id}/bonuses`, with a reason the partner reads. It
needs the permission to manage payouts, is recorded in the activity log, and an
AI assistant always has to ask a person first.

## Ceilings

- **Per customer:** a rule's `cap_minor` is the most it pays for one customer,
  net of refunds. Past it, the rule pays that customer nothing more.
- **Per partner per month:** the program's `partner_monthly_cap_minor` is the
  most one partner earns from it in a calendar month (UTC), milestone bonuses
  included. The part over it is recorded on the commission as `capped_minor`,
  so you can see what the ceiling held back. A bonus you pay by hand is outside
  it: you chose to pay it.
- Both ceilings are in the program's currency and hold commissions in it. A
  percentage of a sale paid in another currency is not compared with them.

## A template for each kind of business

`POST /v1/referrals/programs` with `kind` starts a program with settings and
rules made for it, in one step; anything you send yourself wins. Amounts are in
the program's currency, rounded to an equivalent for currencies without cents
(a 50.00 minimum is 5,000 in JPY).

| `kind` | Starts with |
|---|---|
| `affiliate` | 30% of each payment for 12 months, no signup reward, a 60-day cookie, last click, a 30-day hold, a 50.00 minimum, monthly payouts, approval by hand |
| `refer_a_friend` | 10.00 once for each referred customer who pays; approved automatically, no minimum |
| `creator` | 20% of each payment for 12 months, a 30-day cookie, and a discount-code prefix from the program's name |
| `ambassador` | 10.00 per paying customer and a 50.00 bonus at 5 customers, first click |
| `reseller` | levels Silver (everyone), Gold from 5,000.00 and Platinum from 25,000.00 in referred sales in 90 days, at 20%, 25% and 30% for as long as the customer pays; invite only, a 60-day hold, a 100.00 minimum |
| `shop` | 10% of the first order; approved automatically, a 25.00 minimum |

Two examples of what those rules then do:

- **A reseller** on Gold sells a 2,000.00 yearly plan: 25% is 500.00, and the
  commission says "25% of 2,000.00 (Gold level)". A partner who drops below
  Gold's line keeps Gold, because levels keep what was earned.
- **An ambassador's** fifth paying customer earns them 10.00 for the sale and
  the 50.00 bonus, two commissions on one sale; a sixth customer earns 10.00.

## What never changes

What a partner already earned is a stored amount: editing or deleting a rule,
moving a partner, or changing a level changes what is earned **from then on**.
A program created before rules had conditions keeps its one rule per event and
pays exactly what it paid.

<!-- https://boltclaw.ai/docs/rewards as markdown. Generated from openapi.yaml; API version 1.22.0. -->
<!-- Every /docs page answers as markdown: add .md to its path, or send Accept: text/markdown. -->
