Update a coupon
The coupons.update operation: PATCH /api/v1/coupons/{id} in the REST API and the coupons_update tool in the MCP server.
/api/v1/coupons/{id}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.
At a glance
- REST:
PATCH /api/v1/coupons/{id} - MCP tool:
coupons_update - Operation ID:
coupons.update - Scope: Brand
- Permission:
catalog:write - Risk tier:
write - Dry run: Supported (send
Ownrecord-Dry-Run: true) - Idempotency:
Idempotency-Keyoptional - Modes: Test and live
- Authentication: API key with the
catalog:writescope - Since: M5
Request
Path parameters
| Field | Type | Description |
|---|---|---|
id | cpn_… ID |
Body
A JSON object (Content-Type: application/json):
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Shown to customers at checkout and on invoices, e.g. Launch offer. |
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 or null | No |
Response
200 OK with a JSON object:
| Field | Type | Description |
|---|---|---|
id | cpn_… ID | |
object | "coupon" | |
brandId | brd_… ID | |
livemode | boolean | |
name | string | |
percentOffPpm | string or null | Percent off in parts per million (500000 = 50%), or null for an amount-off coupon. |
amountOff | money or null | Amount off per invoice, or null for a percent-off coupon. |
duration | one of once, repeating, forever | |
durationInPeriods | integer or null | |
appliesTo | object or null | The products and prices the coupon is limited to, or null for every line. |
maxRedemptions | integer or null | |
redeemBy | date-time or null | |
timesRedeemed | integer | |
valid | boolean | Whether it can be redeemed now: active, before redeemBy, below its limit. |
used | boolean | A discount references it or a redemption was counted: only name, status and metadata can change. |
active | boolean | |
metadata | object | |
createdAt | date-time | |
updatedAt | date-time |
Examples
Rename a coupon
PATCH /api/v1/coupons/cpn_01m4tx5nj8emwbyf7y9vcf47z0
Authorization: Bearer or_test_…
Content-Type: application/json
{
"name": "Launch week offer"
}
Response (200):
{
"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"
}
Errors
| Code | HTTP status | When | What to do |
|---|---|---|---|
validation_failed | 400 | No field to change was sent, or the new terms break a rule. | Fix the fields listed in details.issues and send the request again. |
coupon_immutable | 409 | The coupon is used and the request changes its terms. | Create a new coupon with the new terms (and new promotion codes for it), then archive this one. Its name and status can still change. |
resource_not_found | 404 | No coupon (or product or price in appliesTo) with this ID is visible. | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
version_conflict | 409 | The coupon changed at the same time (for example a redemption); retry. | Fetch the object again and retry the change. |
permission_denied | 403 | You can't change this brand's catalog. | Ask an organization owner or admin for a role or scope that allows this action. |
It can also return these common errors: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, scope_required, idempotency_key_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.