Skip to content

Change plan or quantity of a subscription

The subscriptions.update operation: PATCH /api/v1/subscriptions/{id} in the REST API and the subscriptions_update tool in the MCP server.

PATCH/api/v1/subscriptions/{id}

Changes a subscription's items, quantities, discount, payment method, collection method or metadata.

Items (items[]): { id, priceId } swaps the price of an item, { id, quantity } changes its seats, { priceId, quantity } adds an item and { id, deleted: true } removes one; items not named stay. When the change applies (effective) and how it is billed (prorationBehavior) follow spec §16.5:

  • Upgrades (a higher monthly amount) and interval changes default to now with always_invoice: the unused time of the old items is credited, the remaining time of the new ones charged, and the difference invoiced and charged at once (an interval change starts a new billing period now). If the payment fails, the change stays and the open invoice goes to dunning, unless paymentBehavior: pending_if_incomplete keeps the old items until the invoice is paid.
  • Downgrades and lateral changes default to the end of the period: the change is stored as pendingUpdate and applied at renewal, with no credit.
  • now with create_prorations: a net charge waits for the next invoice as pending invoice items; a net credit (fewer seats) goes to the customer's credit balance at once.
  • none: the items change now and nothing is billed.

Trialing subscriptions change at once with nothing to bill. Proration is exact to the second, rounded once per line; proration lines are taxed at the rates of the change date. Preview a change first with subscriptions.preview_change (the exact invoice), or dry-run this operation. Emits subscription.updated.

At a glance

  • REST: PATCH /api/v1/subscriptions/{id}
  • MCP tool: subscriptions_update
  • Operation ID: subscriptions.update
  • Scope: Brand
  • Permission: subscriptions:write
  • Risk tier: financial
  • Dry run: Supported (send Ownrecord-Dry-Run: true)
  • Idempotency: Idempotency-Key optional
  • 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
expectedVersioninteger (at least 1)NoThe version you read (optimistic concurrency).
itemsarray of objectNoItem changes: { id, priceId } swaps a price, { id, quantity } changes seats, { priceId, quantity } adds an item, { id, deleted: true } removes one. Items not named stay.
items[].idsi_… IDNoThe item to change or remove (si_…); leave out to add one.
items[].priceIdprice_… IDNoThe new price of the item, or of a new item.
items[].quantityinteger (1 to 1000000)NoSeats or units of a licensed price (1 to 1,000,000). Metered prices take no quantity.
items[].deletedbooleanNoRemove the item (with id).
prorationBehaviorone of create_prorations, always_invoice, noneNocreate_prorations (credit unused time and charge the remaining time on the next invoice; a net credit goes to the customer credit balance), always_invoice (invoice the proration now) or none.
effectiveone of now, period_endNonow, or at the end of the current period (period_end, stored as pendingUpdate). Default: upgrades and interval changes now with always_invoice, downgrades at the period end.
paymentBehaviorone of allow_incomplete, pending_if_incompleteNoFor a change invoiced now: allow_incomplete (default; the change applies and a failed payment goes to dunning) or pending_if_incomplete (the old items stay until the invoice is paid).
couponIdcpn_… IDNoApply a coupon to the subscription.
promotionCodestringNoA promotion code as the customer typed it (case doesn't matter).
collectionMethodone of charge_automatically, send_invoiceNocharge_automatically (the default payment method is charged) or send_invoice (the invoice is emailed with bank transfer details and paid by the due date).
daysUntilDueinteger (0 to 365) or nullNo
defaultPaymentMethodIdpm_… ID or nullNoThe payment method charged for this subscription (null: the customer's default).
cancelPendingUpdatebooleanNoDrop a change scheduled for the period end (pendingUpdate).
usageThresholdobject or nullNoInvoice metered usage as soon as what it would charge reaches this amount (spec §16.7): limits the credit risk of usage-heavy customers. In the subscription's currency, whole minor units. null removes it.
metadataobjectNoYour own key-value pairs (at most 50 keys). On updates the whole set is replaced.

Response

200 OK 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

Upgrade to Pro now

The unused Basic time is credited and the Pro time to the period end is invoiced and charged now.

HTTP
PATCH /api/v1/subscriptions/sub_01m4tx5nj8emwbyf7y9vcf47z5
Authorization: Bearer or_test_…
Content-Type: application/json

{
  "items": [
    {
      "id": "si_01m4tx5nj8emwbyf7y9vcf47za",
      "priceId": "price_01m4tx5nj8emwbyf7y9vcf47y3"
    }
  ],
  "expectedVersion": 2
}

Response (200):

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_01m4tx5nj8emwbyf7y9vcf47y3",
      "productId": "prod_01m4tx5nj8emwbyf7y9vcf47y0",
      "quantity": 3,
      "createdAt": "2026-10-13T12:00:00.000Z"
    }
  ],
  "currency": "EUR",
  "mrr": {
    "amount": "147000000",
    "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": [],
  "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"
}

Two seats fewer from the next period

HTTP
PATCH /api/v1/subscriptions/sub_01m4tx5nj8emwbyf7y9vcf47z5
Authorization: Bearer or_test_…
Content-Type: application/json

{
  "items": [
    {
      "id": "si_01m4tx5nj8emwbyf7y9vcf47za",
      "quantity": 1
    }
  ],
  "effective": "period_end"
}

Response (200):

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": [],
  "pendingUpdate": {
    "kind": "period_end",
    "items": [
      {
        "id": "si_01m4tx5nj8emwbyf7y9vcf47za",
        "priceId": "price_01m4tx5nj8emwbyf7y9vcf47y2",
        "productId": "prod_01m4tx5nj8emwbyf7y9vcf47y0",
        "quantity": 1,
        "createdAt": "2026-10-13T12:00:00.000Z"
      }
    ],
    "effectiveAt": "2026-11-13T12:00:00.000Z",
    "requestedAt": "2026-10-20T09:00:00.000Z",
    "prorationBehavior": "none",
    "invoiceId": null,
    "expiresAt": 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.
version_conflict409The subscription changed since you read it (expectedVersion), or at the same time; read it and retry.Fetch the object again and retry the change.
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.
subscription_item_not_found404An item id isn't one of the subscription's items.Read the subscription and use one of its items[].id (si_…); to add a new price, leave out id.
subscription_price_invalid400A new price isn't recurring, is archived, is in another currency, or its interval doesn't combine.Use active recurring prices of one currency whose intervals are whole multiples of each other (monthly with yearly, weekly with 2-weekly). details.reason says what's wrong and param names the item.
subscription_meter_conflict409Another live item of the customer already bills this meter.Change the existing subscription's metered item instead, or cancel it first. A customer has at most one active metered item per meter.
subscription_change_invalid400A change waits for its invoice to be paid, or pending_if_incomplete was asked for a send_invoice subscription.Check details.reason and adjust the request.
subscription_payment_method_invalid400defaultPaymentMethodId isn't a saved, attached payment method of the customer.Use a saved payment method (pm_…) of the subscription's customer that isn't detached, or leave it out to use the customer's default.
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.
registration_required422An invoice issued now can't be taxed: the seller isn't registered where the customer is (a new sale).Register the entity for the regime named in details.regime (see its prerequisites and deadline), or arm the jurisdiction once the prerequisites are done. The customer can join the waitlist meanwhile.
jurisdiction_blocked422The customer's location is blocked for new sales.This jurisdiction isn't covered by usable tax knowledge for this seller. Contact support if you need to sell there.
evidence_conflict422The customer's stored location evidence isn't enough to tax an invoice issued now (§13.3).Collect another piece of location evidence (billing address, card country, or the customer's confirmation of their country) and quote again.
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_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.