> ## Documentation Index
> Fetch the complete documentation index at: https://docs.clinkbill.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Discounts and Promotion Codes

> Creating coupons and codes, and applying them across every integration path.

Discounts in Clink have two layers. Once those are clear, the rest of the configuration follows.

## The two layers

**A Coupon is the discount rule** — how much comes off, which items it applies to, how many periods it lasts, and the total redemption cap.

**A Promotion Code is the string the customer types** — or one you attach in advance.

<Warning>
  Creating a Coupon does not give customers anything to enter. **You must create at least one Promotion Code under that Coupon** before anyone can redeem it.
</Warning>

Everywhere `promotionCode` appears below, it is the **customer-facing code string** such as `WELCOME20` — never a `couponId` or `promotionCodeId`.

## Creating them

One call can create a Coupon together with its codes:

```json theme={null}
{
  "couponName": "Welcome offer",
  "discountType": "percentage",
  "percentage": 20,
  "applyType": "product",
  "applicableProducts": ["prd_xxxxx"],
  "durationType": "repeating",
  "durationMonths": 3,
  "promotionCodes": [
    {
      "code": "WELCOME20",
      "firstOrderOnly": true,
      "maxRedemptionLimit": 100
    }
  ]
}
```

Field rules:

* `discountType` is `percentage` or `fixed_amount`. A percentage must be above 0 and no more than 100
* Fixed amounts use `fixedAmounts` keyed by currency, still in the **major currency unit** (19.99 is `19.99`)
* `applyType` is `none`, `product`, or `price`. The last two require the matching IDs
* `durationType` is `once`, `repeating`, or `forever`
* A promotion code is 1 to 32 letters or digits. Omit `code` and the platform can generate one
* Validity uses 13-digit Unix millisecond timestamps. A code's end time never exceeds its Coupon's end time

<Warning>
  **The name `durationMonths` is misleading.** It counts **billing periods**, not calendar months. On a monthly subscription `3` means three months; on a weekly one it means three weeks.
</Warning>

To add codes to an existing Coupon, use [`POST /promotion-code/{couponId}`](/api-reference/endpoint/create-promotion-code). Full fields are in [Create coupon](/api-reference/endpoint/create-coupon).

## Applying them per integration path

| Situation                                 | Configuration or call                                                                                                               | What the customer sees                                                                                |
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| Hosted Checkout, customer enters the code | Send `allowPromotionCodes: true` when creating the Session                                                                          | The input is shown by default; the customer can apply or remove a code                                |
| Pre-applied, not editable                 | `allowPromotionCodes: true` + `showPromotionCode: false` + `promotionCode: "WELCOME20"`                                             | Validated at Session creation; checkout shows the discount but no editable input                      |
| Elements                                  | Enable `allowPromotionCodes` on the Session; the frontend calls `promoCodeChange({ type: "apply", code })` or `({ type: "clear" })` | You build the input yourself, updating totals from `amount-change` and errors from `promo-code-error` |
| Direct payment                            | Send `promotionCode` on `POST /payment`                                                                                             | The backend applies the discount before charging                                                      |
| Direct subscription                       | Send `promotionCode` on `POST /subscription`                                                                                        | The discount lands on the first invoice; later periods follow the Coupon duration                     |
| Plan changes                              | Send **the same** `promotionCode` on both preview and confirm                                                                       | Preview returns the discount and proration; confirm applies it                                        |

The full Elements event code lives in [Elements](/elements) — it is not duplicated here.

## Validation rules

These catch people out most often:

**Inline items only work with unrestricted Coupons.** Items built from `priceDataList` have no `productId` or `priceId`, so they can only use a Coupon with `applyType: none`. A Coupon restricted to a product or price fails validation.

**A fixed-amount Coupon must cover the order currency.** If the order's original currency has no amount in `fixedAmounts`, the Coupon does not apply. A discount larger than the order reduces it to 0 rather than going negative.

**Percentage discounts round down to the currency's maximum decimal places.** Do not recompute on the frontend — display the amount returned by the Session, the preview endpoint, or the Elements `amount-change` event.

**`firstOrderOnly` is judged by whether that Customer has a successful Order.** Not by browser, not by email text, and not by visits to your own pages.

**`minimumSpend` is checked against the order's original amount and currency.** If that currency is not configured, the condition is not met.

**Errors surface at different moments.** The hidden-code mode fails **when the Session is created**. The visible-input mode shows the error **when the customer clicks apply**.

**You do not redeem codes yourself.** The system reserves a redemption when the order is created, confirms it on successful payment, and releases it on failure.

## How long a subscription discount lasts

| `durationType` | Actual behaviour                                                                                                 |
| -------------- | ---------------------------------------------------------------------------------------------------------------- |
| `once`         | Covers **one actual paid period**. With a free trial, the trial does not consume it — the first paid period does |
| `repeating`    | Covers `durationMonths` consecutive **billing periods**. The unit is periods, not calendar months                |
| `forever`      | Keeps applying to later renewals that match the product, price, and currency                                     |

## Discounts produce no webhooks of their own

Discounts do **not** emit a separate "payment succeeded" event. Confirming money still works the same way:

* One-time payments: read the Order and Session result
* Subscriptions: read the Subscription and Invoice result

You can store `couponId`, `promotionCode`, the original price, the discount, and the amount paid locally for display and reconciliation. But **do not recalculate the discount yourself after a webhook arrives** — the amounts Clink returns are authoritative.

## Two things to verify yourself

<Warning>
  These two differ between the implementation and the documented behaviour. Do not depend on them yet:

  **Do not send an empty `restrictedCustomerIds` array.** The field description says an empty array means unrestricted, but the current implementation runs a containment check on any non-null array, which can leave nobody able to redeem. **Omit the field entirely** when there is no restriction.

  **`perCustomerRedemptionLimit` enforcement is unconfirmed.** The field is stored and returned, but no per-customer counting or blocking was found in the current validation path. Verify it yourself before relying on it for a per-customer cap.
</Warning>

## Next

<CardGroup cols={2}>
  <Card title="Subscriptions" icon="repeat" href="/subscriptions">
    Recurring prices, subscription status, and renewals.
  </Card>

  <Card title="Coupon concepts" icon="ticket" href="/guides/resources/coupon">
    Resource definitions and dashboard management.
  </Card>
</CardGroup>
