The
orderId must belong to the customer that the session token was minted for. Tokens minted for one customer cannot cancel another customer’s subscription.Cancellation Behavior
Cancelling a pending plan-change subscription voids that change: the new subscription becomes
canceled directly and its PSP cancellation is dispatched right away, so nothing activates or is charged at the planned switch time. The original subscription is not put back into service — it stays canceling and moves one way to canceled, and Reactivate Subscription refuses it with 400. To keep paying, the customer places a new order.A
pending order that ends on its own while the cancellation is in flight — for example the PSP closes one of two plan-change candidates created concurrently for the same subscription — is treated as already canceled: the request returns 200 with the current terminal status (e.g. closed), any discount reservation still held is released once, and no cancellation reason is stored. If the order was changed concurrently but has not ended, the request returns 409 with the current status; re-read the order and retry.Cancelling a
past_due subscription takes effect immediately rather than at period end, and sends no “cancellation scheduled, access until X” email — the billing period has already lapsed. Such a cancellation cannot be reverted with Reactivate Subscription: any subscription that had an unpaid charge at the moment cancellation was requested is refused with 400.Request Body
The reason is stored only for buyer-side credentials and only once the cancellation actually succeeds — one row per cancellation action, so cancel → reactivate → cancel leaves two. Storing the reason is a side path: if it fails the cancellation still succeeds and the response is unchanged; a failed cancellation (400 / 409 / 500 / 502), or one that converges on an order that already ended, stores nothing. Merchants read the stored reasons through GraphQL.
What happens when
cancelReason is omitted depends on which credential made the call:
The two channels deliberately do not share the sentinel: folding “never asked” and “asked but declined” into one value would make the
not_provided share unreadable for merchants. If your integration also offers reason options and you want skips counted in the denominator, submit cancelReason explicitly (the value set is open, e.g. not_provided).
Example Request
Success Response (200)
Response Fields
Errors
Retry policy: Never retry 4xx — fix the request and resubmit. Retry 5xx with exponential backoff (start 5s, max 3 attempts).