Skip to content

Create a coupon

The coupons.create operation: POST /api/v1/coupons in the REST API and the coupons_create tool in the MCP server.

POST/api/v1/coupons

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.

At a glance

  • REST: POST /api/v1/coupons
  • MCP tool: coupons_create
  • Operation ID: coupons.create
  • Scope: Brand
  • Permission: catalog:write
  • Risk tier: write
  • Dry run: Supported (send Ownrecord-Dry-Run: true)
  • Idempotency: Idempotency-Key required
  • Modes: Test and live
  • Authentication: API key with the catalog:write scope
  • Since: M5

Request

Body

A JSON object (Content-Type: application/json):

FieldTypeRequiredDescription
namestringYesShown to customers at checkout and on invoices, e.g. Launch offer.
percentOffPpmstringNoPercent off in parts per million as a string: 500000 = 50%, 1000000 = 100%. Exactly one of this and amountOff.
amountOffobjectNoAn amount off per invoice, in whole minor units of its currency; applies only to invoices in that currency. Exactly one of this and percentOffPpm.
amountOff.amountstringYesInteger micros as a string (12500000 = 12.50).
amountOff.currencystringYesISO 4217 currency code: the price's currency.
durationone of once, repeating, foreverYesonce (one billing period), repeating (durationInPeriods periods) or forever. Periods count from the first period boundary at or after the discount starts.
durationInPeriodsintegerNoRepeating coupons only: how many billing periods the discount lasts (1–120).
appliesToobjectNoLimit the coupon to lines of these products or prices (a line matches either). Absent: every line.
appliesTo.productIdsarray of prod_… IDNoOnly lines of these products.
appliesTo.priceIdsarray of price_… IDNoOnly lines of these prices, e.g. a monthly price but not the yearly one.
maxRedemptionsintegerNoHow many times it can be redeemed in total (counted on successful payment).
redeemBydate-timeNoNo new redemptions from this instant on (must lie ahead).
metadataobjectNoYour own key-value pairs (at most 50 keys). On updates the whole set is replaced.

Response

201 Created with a JSON object:

FieldTypeDescription
idcpn_… ID
object"coupon"
brandIdbrd_… ID
livemodeboolean
namestring
percentOffPpmstring or nullPercent off in parts per million (500000 = 50%), or null for an amount-off coupon.
amountOffmoney or nullAmount off per invoice, or null for a percent-off coupon.
durationone of once, repeating, forever
durationInPeriodsinteger or null
appliesToobject or nullThe products and prices the coupon is limited to, or null for every line.
maxRedemptionsinteger or null
redeemBydate-time or null
timesRedeemedinteger
validbooleanWhether it can be redeemed now: active, before redeemBy, below its limit.
usedbooleanA discount references it or a redemption was counted: only name, status and metadata can change.
activeboolean
metadataobject
createdAtdate-time
updatedAtdate-time

Examples

50% off the monthly price for three months

HTTP
POST /api/v1/coupons
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "name": "Launch offer",
  "percentOffPpm": "500000",
  "duration": "repeating",
  "durationInPeriods": 3,
  "appliesTo": {
    "priceIds": [
      "price_01m4tx5nj8emwbyf7y9vcf47y2"
    ]
  },
  "maxRedemptions": 500,
  "redeemBy": "2027-01-01T00:00:00.000Z"
}

Response (201):

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

HTTP
POST /api/v1/coupons
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "name": "€10 welcome credit",
  "amountOff": {
    "amount": "10000000",
    "currency": "EUR"
  },
  "duration": "once"
}

Response (201):

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"
}

Errors

CodeHTTP statusWhenWhat to do
validation_failed400Not 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.Fix the fields listed in details.issues and send the request again.
resource_not_found404A product or price in appliesTo isn't in this brand and mode.Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.
permission_denied403You 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_required, idempotency_key_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.