Coupons tools
MCP tools for Coupons: coupons_archive, coupons_create, coupons_get, coupons_list, coupons_update.
coupons_archive: Archive a couponcoupons_create: Create a couponcoupons_get: Get a couponcoupons_list: List couponscoupons_update: Update a coupon
coupons_archive
Archive a coupon. Operation coupons.archive · 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_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, 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: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
resource_not_found: No coupon with this ID is visible to the request.version_conflict: The coupon changed at the same time; retry.permission_denied: You can't change this brand's catalog.
Example
End a launch offer
Arguments:
{
"id": "cpn_01m4tx5nj8emwbyf7y9vcf47z0",
"reason": "Launch period over",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"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 · 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_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, 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: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
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: A product or price inappliesToisn't in this brand and mode.permission_denied: You can't change this brand's catalog.
Examples
50% off the monthly price for three months
Arguments:
{
"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:
{
"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:
{
"name": "€10 welcome credit",
"amountOff": {
"amount": "10000000",
"currency": "EUR"
},
"duration": "once",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"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 · 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_idwith an organization or entity key; a brand key acts on its own brand. - Read-only: changes nothing;
idempotency_keyis ignored.
Errors
resource_not_found: No coupon with this ID is visible to the request.permission_denied: You can't see this brand's catalog.
Example
A launch coupon
Arguments:
{
"id": "cpn_01m4tx5nj8emwbyf7y9vcf47z0",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
Result output:
{
"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 · 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_idwith an organization or entity key; a brand key acts on its own brand. - Read-only: changes nothing;
idempotency_keyis ignored.
Errors
validation_failed: A filter or pagination value is malformed.permission_denied: You can't see this brand's catalog.
Example
Active coupons
Arguments:
{
"active": "true",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
Result output:
{
"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 · 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_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, 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: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
validation_failed: No field to change was sent, or the new terms break a rule.coupon_immutable: The coupon is used and the request changes its terms.resource_not_found: No coupon (or product or price inappliesTo) with this ID is visible.version_conflict: The coupon changed at the same time (for example a redemption); retry.permission_denied: You can't change this brand's catalog.
Example
Rename a coupon
Arguments:
{
"id": "cpn_01m4tx5nj8emwbyf7y9vcf47z0",
"name": "Launch week offer",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"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"
}