# Discounts

> Coupons, promotion codes and the discounts they become. A coupon is a percent or amount off for one, several or every billing period; promotion codes are what customers type; a discount is a coupon applied to a customer or subscription, with dates.

Discounts have three parts, all per brand and mode:

- a **coupon** says what is taken off and for how long, such as "50% off the monthly price for 3 billing periods";
- a **promotion code** is what a customer types at checkout to get a coupon, such as `LAUNCH50`, with its own restrictions;
- a **discount** is a coupon applied to a customer or to one subscription, with the dates it applies between.

Tax is always computed on the discounted amount.

## Coupons

A coupon has exactly one of:

- a **percent off**, as `percentOffPpm` in parts per million: `"500000"` is 50%, `"1000000"` is 100%;
- an **amount off**, such as €10.00 per invoice, in whole minor units of its currency. It applies only to invoices in that currency.

Its **duration** is `once`, `repeating` (for `durationInPeriods` billing periods, 1 to 120) or `forever`. Periods count on the subscription's billing schedule, from the first renewal date at or after the discount starts: a `once` coupon applied when a subscription starts discounts its first invoice; applied in the middle of a period, it discounts the next renewal.

Optionally, a coupon is limited to some **products** or **prices** (`appliesTo`): only invoice lines of those products or prices are discounted. Limiting it to a price lets an offer cover the monthly fee of a plan but not its yearly price or its usage.

Limits on new redemptions:

- `maxRedemptions`: how many times it can be redeemed in total;
- `redeemBy`: no new redemptions from this date on.

Discounts already applied keep running when a coupon reaches a limit or is archived.

### Fixed once used

Once a discount uses a coupon, or a redemption is counted, what it gives can't change: its percent or amount, duration, product and price limits, `maxRedemptions` and `redeemBy` stay as they were, so every customer keeps what they were promised. The coupon's view says `used: true`. Its name, metadata and status can still change. To offer different terms, create a new coupon and archive the old one.

Coupons are never deleted. **Archiving** one stops new redemptions, directly or through its promotion codes. The view's `valid` says whether it can be redeemed right now: active, before `redeemBy` and below `maxRedemptions`.

## Promotion codes

A promotion code is a customer-facing string for a coupon: 3 to 50 letters, digits, `-` or `_`. Codes are **case-insensitive** (`launch50` and `LAUNCH50` are the same code), unique in the brand and mode, and never reused: an archived code keeps its code, so an old code can't suddenly give a different offer.

A code can have **restrictions**:

| Restriction      | The code works only when…                                                       |
| ---------------- | ------------------------------------------------------------------------------- |
| `firstTimeOnly`  | the customer has no earlier successful payment or paid invoice in this brand.   |
| `minimumAmount`  | the purchase before discounts reaches this amount, in the same currency.        |
| `customerId`     | the customer is this customer.                                                  |
| `expiresAt`      | it is used before this moment.                                                  |
| `maxRedemptions` | it was redeemed fewer times than this (the coupon's own limit applies as well). |

When a fact isn't known, the code is refused: a code with a minimum needs the purchase amount, and a customer's code or a first-time code needs the customer. A refused code returns `promotion_code_not_redeemable` with `details.reason`, so checkout can tell the customer why. A code and its restrictions can't change; archive it and create another.

To look a code up the way a customer typed it, list promotion codes with `code`, for example `GET /api/v1/promotion_codes?code=launch50`.

## Discounts

Applying a coupon (directly, or through a promotion code at checkout) creates a **discount**: the coupon, the code used, the customer, the subscription if any, and its window from `start` to `end`. It applies to an invoice dated from `start` up to, but not including, `end`; a `forever` discount has no end.

- A subscription's own discounts apply to its invoices. When it has none, the customer's **customer-level** discounts apply instead.
- Several discounts can stack: amount-off discounts first, then percent-off, and a line never goes below zero.
- The same coupon can't be applied twice to one subscription (or customer) while its discount is running.

**Ending** a discount (`DELETE /api/v1/discounts/{id}`) stops it applying from now on; a discount scheduled to start later never starts. Invoices already issued keep their discount.

### Counting redemptions

A redemption is counted when the payment it applies to succeeds, or straight away for free and trial subscriptions. Counting is exact under load: two customers redeeming the last available use at the same moment never both get it. Each discount is counted once, even if a payment notification arrives twice.

## Events

| Event                                              | When                                                    |
| -------------------------------------------------- | ------------------------------------------------------- |
| `coupon.created`, `coupon.updated`                 | A coupon was created, renamed, archived or reactivated. |
| `promotion_code.created`, `promotion_code.updated` | A code was created, archived or reactivated.            |
| `discount.created`, `discount.deleted`             | A coupon was applied, or a discount was ended early.    |

Counting a redemption doesn't send an event; `timesRedeemed` shows it.

## Coupons in the console

Open a brand, choose **Catalog**, then **Coupons**. Owners and admins can change coupons, and developers can in test mode; finance, tax managers, support and viewers can see them.

- **Coupons**: every coupon, filtered by status, with its discount and redemptions.
- A coupon's page shows its terms and limits and its promotion codes with their restrictions. From there you create codes, archive or reactivate codes, and rename, archive or reactivate the coupon.

Every page names the operation behind it, so you can do the same through the API or an MCP client: see [Create a coupon](https://ownrecord.io/docs/reference/coupons-create.md), [Create a promotion code](https://ownrecord.io/docs/reference/promotion-codes-create.md) and [List discounts](https://ownrecord.io/docs/reference/discounts-list.md).
