# Coupons tools

> MCP tools for Coupons: coupons_archive, coupons_create, coupons_get, coupons_list, coupons_update.

- `coupons_archive`: Archive a coupon
- `coupons_create`: Create a coupon
- `coupons_get`: Get a coupon
- `coupons_list`: List coupons
- `coupons_update`: Update a coupon

## `coupons_archive`

**Archive a coupon.** Operation [`coupons.archive`](https://ownrecord.io/docs/reference/coupons-archive.md) · Brand level · permission `catalog:write` · risk tier `write` · since M5

Archives a coupon: it can't be redeemed any more, directly or through its promotion codes. Discounts already
applied with it keep running until they end. Archiving an archived coupon changes nothing. Reactivate it with
`coupons.update` and `active: true`.

`reason` is recorded in the audit log.

Annotations: `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument          | Type         | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`cpn_…`) | yes      | –                                                                                                                                                                                                        |
| `reason`          | string       | no       | (1–500 characters)                                                                                                                                                                                       |
| `entity_id`       | ID (`ent_…`) | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                                                                                 |
| `brand_id`        | ID (`brd_…`) | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.                                                                 |
| `idempotency_key` | string       | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean      | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No coupon with this ID is visible to the request.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The coupon changed at the same time; retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't change this brand's catalog.

### Example

**End a launch offer**

Arguments:

```json
{
  "id": "cpn_01m4tx5nj8emwbyf7y9vcf47z0",
  "reason": "Launch period over",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "cpn_01m4tx5nj8emwbyf7y9vcf47z0",
  "object": "coupon",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "name": "Launch offer",
  "percentOffPpm": "500000",
  "amountOff": null,
  "duration": "repeating",
  "durationInPeriods": 3,
  "appliesTo": {
    "productIds": [],
    "priceIds": ["price_01m4tx5nj8emwbyf7y9vcf47y2"]
  },
  "maxRedemptions": 500,
  "redeemBy": "2027-01-01T00:00:00.000Z",
  "timesRedeemed": 0,
  "valid": false,
  "used": false,
  "active": false,
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z"
}
```

## `coupons_create`

**Create a coupon.** Operation [`coupons.create`](https://ownrecord.io/docs/reference/coupons-create.md) · Brand level · permission `catalog:write` · risk tier `write` · since M5

Creates a coupon in the brand's catalog, in the request's mode: a percent off (`percentOffPpm`, parts per
million: 500000 = 50%) or an amount off (`amountOff`, whole minor units, only for invoices in its currency),
for `once` (one billing period), `repeating` (`durationInPeriods` periods) or `forever`.

Optionally limit it to products or prices (`appliesTo`) and cap redemptions with `maxRedemptions` and
`redeemBy`. Customers redeem coupons through promotion codes (`promotion_codes.create`); operators apply them
to subscriptions directly. Once a coupon is used, only its name, status and metadata can change.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Arguments

| Argument            | Type                                  | Required | Description                                                                                                                                                                                              |
| ------------------- | ------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`              | string                                | yes      | Shown to customers at checkout and on invoices, e.g. Launch offer. (1–120 characters)                                                                                                                    |
| `percentOffPpm`     | string                                | no       | Percent off in parts per million as a string: 500000 = 50%, 1000000 = 100%. Exactly one of this and amountOff.                                                                                           |
| `amountOff`         | object                                | no       | An amount off per invoice, in whole minor units of its currency; applies only to invoices in that currency. Exactly one of this and percentOffPpm.                                                       |
| `duration`          | one of `once`, `repeating`, `forever` | yes      | `once` (one billing period), `repeating` (`durationInPeriods` periods) or `forever`. Periods count from the first period boundary at or after the discount starts.                                       |
| `durationInPeriods` | integer                               | no       | Repeating coupons only: how many billing periods the discount lasts (1–120). (-9007199254740991–9007199254740991)                                                                                        |
| `appliesTo`         | object                                | no       | Limit the coupon to lines of these products or prices (a line matches either). Absent: every line.                                                                                                       |
| `maxRedemptions`    | integer                               | no       | How many times it can be redeemed in total (counted on successful payment). (-9007199254740991–9007199254740991)                                                                                         |
| `redeemBy`          | date-time (ISO 8601)                  | no       | No new redemptions from this instant on (must lie ahead).                                                                                                                                                |
| `metadata`          | object                                | no       | Your own key-value pairs (at most 50 keys). On updates the whole set is replaced.                                                                                                                        |
| `entity_id`         | ID (`ent_…`)                          | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                                                                                 |
| `brand_id`          | ID (`brd_…`)                          | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.                                                                 |
| `idempotency_key`   | string                                | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`           | boolean                               | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): Not exactly one of percentOffPpm and amountOff, a value out of bounds, a duration without (or with an unexpected) number of periods, or redeemBy in the past.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): A product or price in `appliesTo` isn't in this brand and mode.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't change this brand's catalog.

### Examples

**50% off the monthly price for three months**

Arguments:

```json
{
  "name": "Launch offer",
  "percentOffPpm": "500000",
  "duration": "repeating",
  "durationInPeriods": 3,
  "appliesTo": {
    "priceIds": ["price_01m4tx5nj8emwbyf7y9vcf47y2"]
  },
  "maxRedemptions": 500,
  "redeemBy": "2027-01-01T00:00:00.000Z",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "cpn_01m4tx5nj8emwbyf7y9vcf47z0",
  "object": "coupon",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "name": "Launch offer",
  "percentOffPpm": "500000",
  "amountOff": null,
  "duration": "repeating",
  "durationInPeriods": 3,
  "appliesTo": {
    "productIds": [],
    "priceIds": ["price_01m4tx5nj8emwbyf7y9vcf47y2"]
  },
  "maxRedemptions": 500,
  "redeemBy": "2027-01-01T00:00:00.000Z",
  "timesRedeemed": 0,
  "valid": true,
  "used": false,
  "active": true,
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z"
}
```

**€10 off one invoice**

Arguments:

```json
{
  "name": "€10 welcome credit",
  "amountOff": {
    "amount": "10000000",
    "currency": "EUR"
  },
  "duration": "once",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "cpn_01m4tx5nj8emwbyf7y9vcf47z1",
  "object": "coupon",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "name": "€10 welcome credit",
  "percentOffPpm": null,
  "amountOff": {
    "amount": "10000000",
    "currency": "EUR"
  },
  "duration": "once",
  "durationInPeriods": null,
  "appliesTo": null,
  "maxRedemptions": null,
  "redeemBy": null,
  "timesRedeemed": 0,
  "valid": true,
  "used": false,
  "active": true,
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z"
}
```

## `coupons_get`

**Get a coupon.** Operation [`coupons.get`](https://ownrecord.io/docs/reference/coupons-get.md) · Brand level · permission `catalog:read` · risk tier `read` · since M5

Returns one coupon of the brand in the request's mode, archived or not: its discount, how long it lasts (`duration`: `once`, `repeating` for `durationInPeriods` billing periods, or `forever`), how often it was redeemed and whether it can be redeemed now (`valid`).

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument    | Type         | Required | Description                                                                                                                              |
| ----------- | ------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `id`        | ID (`cpn_…`) | yes      | –                                                                                                                                        |
| `entity_id` | ID (`ent_…`) | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                 |
| `brand_id`  | ID (`brd_…`) | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No coupon with this ID is visible to the request.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see this brand's catalog.

### Example

**A launch coupon**

Arguments:

```json
{
  "id": "cpn_01m4tx5nj8emwbyf7y9vcf47z0",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
```

Result `output`:

```json
{
  "id": "cpn_01m4tx5nj8emwbyf7y9vcf47z0",
  "object": "coupon",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "name": "Launch offer",
  "percentOffPpm": "500000",
  "amountOff": null,
  "duration": "repeating",
  "durationInPeriods": 3,
  "appliesTo": {
    "productIds": [],
    "priceIds": ["price_01m4tx5nj8emwbyf7y9vcf47y2"]
  },
  "maxRedemptions": 500,
  "redeemBy": "2027-01-01T00:00:00.000Z",
  "timesRedeemed": 0,
  "valid": true,
  "used": false,
  "active": true,
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z"
}
```

## `coupons_list`

**List coupons.** Operation [`coupons.list`](https://ownrecord.io/docs/reference/coupons-list.md) · Brand level · permission `catalog:read` · risk tier `read` · since M5

Lists the brand's coupons in the request's mode, newest first, with their name, discount and duration: look
one up by its name here. Filter with `active=true` (or `false` for archived ones).

Paginate with `limit` (1–100, default 10) and `starting_after` (the `next_cursor` of the previous page).

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument         | Type         | Required | Description                                                                                                                              |
| ---------------- | ------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `active`         | string       | no       | Only active (true) or archived (false) coupons.                                                                                          |
| `limit`          | integer      | no       | (1–100)                                                                                                                                  |
| `starting_after` | ID (`cpn_…`) | no       | –                                                                                                                                        |
| `entity_id`      | ID (`ent_…`) | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                 |
| `brand_id`       | ID (`brd_…`) | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A filter or pagination value is malformed.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see this brand's catalog.

### Example

**Active coupons**

Arguments:

```json
{
  "active": "true",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "id": "cpn_01m4tx5nj8emwbyf7y9vcf47z0",
      "object": "coupon",
      "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
      "livemode": false,
      "name": "Launch offer",
      "percentOffPpm": "500000",
      "amountOff": null,
      "duration": "repeating",
      "durationInPeriods": 3,
      "appliesTo": {
        "productIds": [],
        "priceIds": ["price_01m4tx5nj8emwbyf7y9vcf47y2"]
      },
      "maxRedemptions": 500,
      "redeemBy": "2027-01-01T00:00:00.000Z",
      "timesRedeemed": 0,
      "valid": true,
      "used": false,
      "active": true,
      "metadata": {},
      "createdAt": "2026-10-13T12:00:00.000Z",
      "updatedAt": "2026-10-13T12:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## `coupons_update`

**Update a coupon.** Operation [`coupons.update`](https://ownrecord.io/docs/reference/coupons-update.md) · Brand level · permission `catalog:write` · risk tier `write` · since M5

Changes a coupon. Send only the fields to change; `null` clears an optional field.

The name, `active` (false archives, true reactivates) and metadata can always change. The terms (percent or
amount off, duration, `appliesTo`, `maxRedemptions`, `redeemBy`) change only while the coupon is unused: once a
discount references it or a redemption is counted they are fixed (`coupon_immutable`), so customers keep what
they were promised. Create a new coupon for new terms.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument            | Type                                  | Required | Description                                                                                                                                                                                              |
| ------------------- | ------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                | ID (`cpn_…`)                          | yes      | –                                                                                                                                                                                                        |
| `name`              | string                                | no       | Shown to customers at checkout and on invoices, e.g. Launch offer. (1–120 characters)                                                                                                                    |
| `active`            | boolean                               | no       | false archives the coupon, true reactivates it.                                                                                                                                                          |
| `metadata`          | object                                | no       | Your own key-value pairs (at most 50 keys). On updates the whole set is replaced.                                                                                                                        |
| `percentOffPpm`     | string or null                        | no       | –                                                                                                                                                                                                        |
| `amountOff`         | object or null                        | no       | –                                                                                                                                                                                                        |
| `duration`          | one of `once`, `repeating`, `forever` | no       | `once` (one billing period), `repeating` (`durationInPeriods` periods) or `forever`. Periods count from the first period boundary at or after the discount starts.                                       |
| `durationInPeriods` | integer or null                       | no       | –                                                                                                                                                                                                        |
| `appliesTo`         | object or null                        | no       | –                                                                                                                                                                                                        |
| `maxRedemptions`    | integer or null                       | no       | –                                                                                                                                                                                                        |
| `redeemBy`          | date-time (ISO 8601) or null          | no       | –                                                                                                                                                                                                        |
| `entity_id`         | ID (`ent_…`)                          | no       | Optional: the brand's legal entity (ent\_…); `brand_id` alone is enough.                                                                                                                                 |
| `brand_id`          | ID (`brd_…`)                          | no       | The brand to act on (brd\_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.                                                                 |
| `idempotency_key`   | string                                | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`           | boolean                               | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Brand:** pass `brand_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): No field to change was sent, or the new terms break a rule.
- [`coupon_immutable`](https://ownrecord.io/docs/reference/errors.md#coupon_immutable): The coupon is used and the request changes its terms.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No coupon (or product or price in `appliesTo`) with this ID is visible.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The coupon changed at the same time (for example a redemption); retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't change this brand's catalog.

### Example

**Rename a coupon**

Arguments:

```json
{
  "id": "cpn_01m4tx5nj8emwbyf7y9vcf47z0",
  "name": "Launch week offer",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "cpn_01m4tx5nj8emwbyf7y9vcf47z0",
  "object": "coupon",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "name": "Launch week offer",
  "percentOffPpm": "500000",
  "amountOff": null,
  "duration": "repeating",
  "durationInPeriods": 3,
  "appliesTo": {
    "productIds": [],
    "priceIds": ["price_01m4tx5nj8emwbyf7y9vcf47y2"]
  },
  "maxRedemptions": 500,
  "redeemBy": "2027-01-01T00:00:00.000Z",
  "timesRedeemed": 0,
  "valid": true,
  "used": false,
  "active": true,
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z"
}
```
