Skip to content

Apply a coupon to a subscription

The subscriptions.discounts.create operation: POST /api/v1/subscriptions/{id}/discounts in the REST API and the subscriptions_discounts_create tool in the MCP server.

POST/api/v1/subscriptions/{id}/discounts

Applies a coupon (couponId) or a promotion code the customer gave (promotionCode) to the subscription from now on: it discounts the subscription's next invoices for the coupon's duration, counted in the subscription's billing periods. Promotion codes are checked against their restrictions; the redemption counts when the next invoice is paid (at once for a trial). End a discount with discounts.delete. Emits discount.created and subscription.updated.

At a glance

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

Request

Path parameters

FieldTypeDescription
idsub_… ID

Body

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

FieldTypeRequiredDescription
couponIdcpn_… IDNo
promotionCodestringNoA promotion code as the customer typed it (case doesn't matter).

Response

201 Created with a JSON object:

FieldTypeDescription
idsub_… ID
object"subscription"
livemodeboolean
entityIdent_… ID
brandIdbrd_… ID
customerIdcus_… ID
statusone of incomplete, incomplete_expired, trialing, active, past_due, unpaid, paused, canceledincomplete (waiting for the first payment, 23 hours), trialing, active, past_due, unpaid, paused, canceled or incomplete_expired (spec §16.1).
itemsarray of object
items[].idsi_… ID
items[].priceIdprice_… ID
items[].productIdprod_… ID
items[].quantityinteger
items[].createdAtdate-time
currencystring
mrrmoney or nullThe items' monthly recurring amount (licensed items, before discounts and tax).
collectionMethodone of charge_automatically, send_invoice
daysUntilDueinteger or null
defaultPaymentMethodIdpm_… ID or null
billingCycleAnchordate-time
currentPeriodStartdate-time
currentPeriodEnddate-time
trialStartdate-time or null
trialEnddate-time or null
trialEndBehaviorone of convert, cancel_if_no_payment_method, pause_if_no_payment_method
cancelAtPeriodEndboolean
cancelAtdate-time or null
canceledAtdate-time or null
endedAtdate-time or null
cancellationobject or null
pauseobject or null
discountIdsarray of disc_… ID
pendingUpdateobject or null
latestInvoiceIdinv_… ID or null
incompleteExpiresAtdate-time or null
testClockIdclk_… ID or null
dunningobject or nullDunning (§16.10) while past due or unpaid; null otherwise.
usageThresholdmoney or nullUnbilled metered usage worth this much is invoiced at once (spec §16.7).
entitlementGraceUntildate-time or null
metadataobject
versioninteger
createdAtdate-time
updatedAtdate-time

Examples

Apply the customer's promotion code

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

{
  "promotionCode": "LAUNCH50"
}

Response (201):

JSON
{
  "id": "sub_01m4tx5nj8emwbyf7y9vcf47z5",
  "object": "subscription",
  "livemode": false,
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47zm",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "customerId": "cus_01m4tx5nj8emwbyf7y9vcf47z4",
  "status": "active",
  "items": [
    {
      "id": "si_01m4tx5nj8emwbyf7y9vcf47za",
      "priceId": "price_01m4tx5nj8emwbyf7y9vcf47y2",
      "productId": "prod_01m4tx5nj8emwbyf7y9vcf47y0",
      "quantity": 3,
      "createdAt": "2026-10-13T12:00:00.000Z"
    },
    {
      "id": "si_01m4tx5nj8emwbyf7y9vcf47zb",
      "priceId": "price_01m4tx5nj8emwbyf7y9vcf47y9",
      "productId": "prod_01m4tx5nj8emwbyf7y9vcf47y1",
      "quantity": 1,
      "createdAt": "2026-10-13T12:00:00.000Z"
    }
  ],
  "currency": "EUR",
  "mrr": {
    "amount": "87000000",
    "currency": "EUR"
  },
  "collectionMethod": "charge_automatically",
  "daysUntilDue": null,
  "defaultPaymentMethodId": "pm_01m4tx5nj8emwbyf7y9vcf47zd",
  "billingCycleAnchor": "2026-10-13T12:00:00.000Z",
  "currentPeriodStart": "2026-10-13T12:00:00.000Z",
  "currentPeriodEnd": "2026-11-13T12:00:00.000Z",
  "trialStart": null,
  "trialEnd": null,
  "trialEndBehavior": "convert",
  "cancelAtPeriodEnd": false,
  "cancelAt": null,
  "canceledAt": null,
  "endedAt": null,
  "cancellation": null,
  "pause": null,
  "discountIds": ["disc_01m4tx5nj8emwbyf7y9vcf47z3"],
  "pendingUpdate": null,
  "latestInvoiceId": "inv_01m4tx5nj8emwbyf7y9vcf47zn",
  "incompleteExpiresAt": null,
  "testClockId": null,
  "dunning": null,
  "usageThreshold": null,
  "entitlementGraceUntil": null,
  "metadata": {
    "crm_account": "A-1042"
  },
  "version": 3,
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-20T09:00:00.000Z"
}

Errors

CodeHTTP statusWhenWhat to do
validation_failed400A field is malformed or out of range.Fix the fields listed in details.issues and send the request again.
resource_not_found404No subscription (or customer, price, coupon) with this ID is visible to the request.Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.
subscription_status_invalid409The subscription's status doesn't allow this (spec §16.1): see details.reason.Check details.status: items change only while trialing, active or past due; pausing needs an active subscription and resuming a paused one; reactivating needs a cancellation scheduled for the period end; canceled and expired subscriptions don't change.
coupon_not_redeemable409The coupon is archived, expired or used up.Check details.reason: reactivate an archived coupon, or create a new coupon when it expired or reached its redemption limit.
promotion_code_not_redeemable409The promotion code can't be used (details.reason).Check details.reason and tell the customer: the code may have expired, be used up, belong to another customer, need a larger purchase or be for first-time customers only.
discount_already_applied409The coupon is already applied to this subscription.End the existing discount first (discounts.delete), or apply a different coupon. The same coupon doesn't stack with itself.
permission_denied403The credentials can't change this brand's subscriptions (subscriptions:write).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.