Version
DESTRUCTIVE

update_coupon

Change one or more fields of a coupon code. Partial by design — anything omitted keeps its stored value. Emits coupon.updated.

Change an existing coupon without restating it. Only the arguments present in the call are written, the way the REST PATCH behaves, so extending an expiry is one field, not a re-creation. Every field is checked the way create_coupon checks it, and cross-field rules are checked against the coupon's effective values: a percentage cap is judged against the type the coupon will have after the call, an end date against the start it will have.

Synchronous — the row after the change comes back in the response, and coupon.updated emits once with the fields that moved. A call that carries only coupon_id returns the current row and emits nothing.

Omit to keep, send null to clear

Leaving a field out leaves it alone. To remove a limit or a date, send the key with null: max_redemptions (unlimited), minimum_amount (no minimum), starts_at (usable now) and expires_at (no end date) all accept it. max_redemptions_per_user does not — it is always at least 1.

`plan_ids: []` widens the code to every plan

Omitting plan_ids leaves the restriction as it is. Sending an empty list deliberately covers every plan in the project, including ones added later. To narrow a code to "the Premium plan", pass that plan's UUID; resolve it with list_plans first.

Requires the Coupons Addon or a Growth plan on the project owner. When the tier has lapsed the call fails with TEAM_TIER_REQUIRED and nothing is written. The discount still applies to the first payment only; nothing here changes a subscriber's renewal price.

Switching discount_type has a side effect worth knowing: moving to fixed needs a currency_id unless the coupon already stores one, and moving to percentage clears the stored currency.

Requires ability

The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.

The REST endpoint and this tool share one action, so validation, permissions and events are identical.

Fires one event

Delivered to every endpoint subscribed to it once the change is made.

Annotations

DestructiveIdempotentOpen world

A client that honours annotations asks a person before running it. Sending the same arguments twice changes nothing the second time. It reaches beyond Subscriby: a connector, a provider or a member.

Arguments

coupon_id*string

UUID of the coupon to change.

codestringoptional

New code subscribers type at checkout. Letters, numbers and dashes only, 3 to 64 characters. Stored uppercase; must not already exist in the project.

namestringoptional

Internal label, 3 to 255 characters.

discount_typestringoptional

Either "percentage" or "fixed". Switching to fixed needs a currency_id unless the code already has one.

discount_valuenumberoptional

For percentage, 1..100. For fixed, an amount greater than zero in the coupon's currency.

currency_idstringoptional

Currency UUID for a fixed discount. Ignored and cleared for a percentage.

max_redemptionsintegeroptional

Total uses allowed across everyone. Pass null for unlimited.

min1
max_redemptions_per_userintegeroptional

How many times one subscriber may use the code.

min1
minimum_amountnumberoptional

Only apply the code when the plan costs at least this much. Pass null for no minimum.

starts_atstringoptional

ISO 8601 instant the code becomes usable. Pass null to start immediately.

expires_atstringoptional

ISO 8601 instant the code stops working. Pass null for no end date. Must fall after the start.

activebooleanoptional

Whether the code is usable.

plan_idsstring[]optional

Plan UUIDs to restrict the code to. An empty list covers every plan in the project, including ones added later.

What it returns

{  "data": {    "id": "4b9d3e08-a1f6-4275-9c83-7e01d6a2f594",    "project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",    "code": "BLACKFRIDAY",    "name": "Black Friday 2026 (extended)",    "discount_type": "percentage",    "discount_value": "25.0000",    "currency": null,    "duration": "once",    "max_redemptions": 750,    "max_redemptions_per_user": 1,    "minimum_amount": null,    "redemptions": {      "count": 137,      "remaining": 613,      "exhausted": false    },    "starts_at": "2026-11-27T00:00:00+00:00",    "expires_at": "2026-12-08T00:00:00+00:00",    "plan_ids": [],    "active": true,    "redeemable": true,    "created_at": "2026-11-20T09:14:02+00:00"  }}

The whole row comes back, not only the fields sent, so there is no need for a follow-up get_coupon. code is uppercase whatever was sent; discount_value is a decimal string. redemptions.remaining is recomputed against the new cap.

How it fails

TEAM_TIER_REQUIRED

the project owner's tier no longer includes coupons. error.context.reason

VALIDATION_FAILED

one entry per offending field in error.context: code outside 3–64

RESOURCE_NOT_FOUND

unknown coupon_id, a coupon on another team's project, one outside the

AUTHENTICATION_REQUIRED

no authenticated user on the request.

TOKEN_MISSING_ABILITY

token lacks project-coupon:update.

How is this guide?

Last updated on