Skip to main content

Envelope

The one exception is GET /v3/payments/:id/receipt.pdf, which returns the PDF bytes.

Pagination

List endpoints are cursor-paginated. Pass ?limit= (1–100, default 20) and ?cursor= (the previous nextCursor). When hasMore is false, nextCursor is null.

Status codes

Universal codes

INTERNAL_SERVER_ERROR, NOT_FOUND, BAD_REQUEST, CONFLICT, RATE_LIMITED, UNPROCESSABLE_ENTITY, UNAUTHORIZED, FORBIDDEN, TOKEN_EXPIRED, TOKEN_INVALID, MFA_REQUIRED, ORG_NOT_FOUND, MEMBERSHIP_NOT_FOUND, INSUFFICIENT_ROLE, ENVIRONMENT_MISMATCH, SANDBOX_ONLY, LIVE_ONLY, IDEMPOTENCY_KEY_REQUIRED, IDEMPOTENCY_KEY_CONFLICT, VALIDATION_ERROR, MISSING_FIELD, INVALID_ID.

Domain codes

PAYMENT_NOT_FOUND, PAYMENT_NOT_REFUNDABLE, PRODUCT_NOT_FOUND, CUSTOMER_NOT_FOUND, PAYMENT_METHOD_NOT_FOUND, CRYPTO_ONLY_ORG, CARD_PAYMENT_BELOW_MIN, PROVIDER_NOT_CONFIGURED, KYB_NOT_APPROVED, INVALID_PAYMENT_STATUS_TRANSITION, CRYPTO_ASSET_NOT_ACCEPTED, CRYPTO_PAYOUT_ADDRESS_MISSING, CRYPTO_PAYMENT_BELOW_MIN, CRYPTO_PRICE_FEED_UNAVAILABLE, CRYPTO_CURRENCY_NOT_SUPPORTED, BTC_NOT_AWAITING_APPROVAL, SOL_PAYER_ADDRESS_REQUIRED, SOL_RPC_UNAVAILABLE, EVM_PAYER_ADDRESS_REQUIRED, EVM_RPC_UNAVAILABLE, EVM_CONTRACT_NOT_DEPLOYED, CRYPTO_AUTOSWAP_TARGET_MISSING, CRYPTO_VAULT_NOT_PROVISIONED, CRYPTO_STABLECOIN_DISABLED, CRYPTO_VOLATILE_DISABLED, CRYPTO_AUTOSWAP_MODE_NOT_SUPPORTED.Crypto charges live on /v3/crypto/charges and carry their own codes · see the Crypto section.CUSTOMER_NOT_FOUND (404) · no customer with that id belongs to the account the key authenticates. Same answer as an id that exists under another account — confirming that someone else’s customer is real would itself be a disclosure — so read it as “not yours”, not as “not anywhere”.This endpoint does not raise CUSTOMER_NAME_REQUIRED. A buyer name is needed when the payer is present and it travels to the rail with the card they are typing; an off-session debit sends the stored instrument and the acquirer’s own customer ref, and no identity at all. A customer you only know by email is chargeable here.CARD_PAYMENT_BELOW_MIN (422) · the card would be charged less than 1.00 in the payment currency, counted after the discount and with VAT included. Nothing is created. Crypto has its own per-chain minimum (CRYPTO_PAYMENT_BELOW_MIN).CHARGING_PAUSED_FOR_DEBT (402) · card charging is suspended until an outstanding balance is covered, then resumes automatically. Crypto is unaffected. Not the same as CARD_LIVE_NOT_ACTIVATED (never enabled).
SUBSCRIPTION_NOT_FOUND, SUBSCRIPTION_CUSTOMER_NOT_FOUND, SUBSCRIPTION_PRODUCT_NOT_FOUND, PRODUCT_NOT_RECURRING, PRODUCT_PRICE_MISSING, CUSTOMER_NAME_REQUIRED, FIRST_PAYMENT_DECLINED, SUBSCRIPTION_NOT_CANCELABLE, CRYPTO_ONLY_ORG.A subscription is opened by a checkout session, so FIRST_PAYMENT_DECLINED surfaces on that page rather than as an API error.Plan change: SUBSCRIPTION_NOT_MODIFIABLE, TARGET_PRODUCT_NOT_FOUND, TARGET_PRODUCT_NOT_RECURRING, SAME_PLAN, PLAN_CHANGE_CURRENCY_MISMATCH, UPGRADE_REQUIRES_CARD, PLAN_CHANGE_DECLINED, NO_SCHEDULED_PLAN_CHANGE, PLAN_CHANGE_NOT_CANCELABLE.SAME_PLAN (409) is not the way to undo a queued change · it fires precisely because the subscription is already on that plan. Call POST /v3/subscriptions/{id}/cancel-plan-change instead.NO_SCHEDULED_PLAN_CHANGE (404) · nothing is queued on this subscription, or the scheduledChangeId you passed does not name what is. Re-read scheduledChange from GET /v3/subscriptions/{id}.PLAN_CHANGE_NOT_CANCELABLE (409) · the change is an immediate upgrade whose charge is in flight or settled, so dropping the swap would leave the customer paying for a plan they never get. Refund the charge and change the plan back.Expiration date: SUBSCRIPTION_NOT_MODIFIABLE, PERIOD_END_NOT_IN_FUTURE, PERIOD_END_SHORTENING_UNCONFIRMED.PERIOD_END_SHORTENING_UNCONFIRMED (409) · the new date is earlier than the current one, which takes back days the customer already paid for. data.daysRemoved says how many. Send the same request with allowShorten: true to confirm.
PRODUCT_NOT_FOUND, PRODUCT_OUT_OF_STOCK, PRODUCT_ARCHIVED, RECURRING_REQUIRES_INTERVAL.PRODUCT_BILLING_INVALID (422) · the billing fields contradict each other: a pay_as_you_go product carrying a price, a subscription with no recurringInterval, an interval on a onetime product, or an ACTIVE product carrying no price. Raised on create and on PATCH (against the row merged with the patch); the message names the rule.A priceCents under 100 (1.00) is refused as a validation error (VALIDATION_ERROR, 422) on create and on any PATCH that sends one. A product priced lower before this rule stays editable as long as the PATCH leaves priceCents out.PRODUCT_BILLING_MODE_LOCKED (422) · a PATCH tried to move a product into or out of billingMode: subscription. The cadence drives running plans and is not editable, and changing the mode is that same edit by another name. Create a second product and move subscribers with a plan change.PRODUCT_PRICED_PER_USE (422) · a pay_as_you_go product reached a surface that prices products by itself — a charging checkout session, a bundle line, a subscription cycle. Those read an amount off the catalog with no caller in the loop, and this product has none to read. Sell it with a mode=setup session (the payer authorises the card) and charge each use with POST /v3/payments/off-session and an explicit priceCents.PRODUCT_NOT_PRICED_PER_USE (422) · the mirror of the one above — an off-session debit (POST /v3/payments/off-session) named a product the catalog already prices, or named none at all. That call carries its own amount because only a per-use product leaves one undecided: debiting a onetime product this way bills a customer again for something they authorised once, and a subscription product for a cycle Suby’s own clock already bills. Both would succeed at the acquirer. Send productId of a pay_as_you_go product together with priceCents.PRODUCT_AMOUNT_REQUIRED (422) · a pay_as_you_go charge named no amount, so nobody decided the price.PRODUCT_CURRENCY_MISMATCH (422) · a pay_as_you_go charge named a currency the product does not bill in. Refused rather than converted — the two readings differ by an exchange rate, and one of them debits a real customer the wrong amount.PRODUCT_PRICE_MISSING (422) · a catalog-priced product that was never priced reached a charge. Price it, or declare it pay_as_you_go.PRODUCT_PER_USE_UNAVAILABLE (422) · billingMode: pay_as_you_go on an account that cannot serve it. Per-use bills after the fact, against a credential stored at checkout, and two things have to hold.First, a method that leaves such a credential: a card, Apple Pay or Google Pay. A redirect rail authorises one amount on its own site and a crypto deposit is a transfer, not a mandate — enable one of the three.Second, an account whose card setup authenticates the card as it stores it. That authentication is what anchors the chain every later usage charge replays; without it the card is stored, authorises fine, and is refused on the first charge — days later, with the product already shared. Accounts on a rail that has no such step cannot create per-use products at all today, and there is nothing to enable: it opens for them when the rail gains one.Raised on create and on a PATCH that moves a product onto per-use.PRODUCT_METHOD_NOT_PER_USE (422) · a pay_as_you_go product’s paymentMethods whitelist named a method its checkout can never offer. That session only shows card, Apple Pay and Google Pay, so naming a redirect rail states a choice no payer will see — and a whitelist made only of those resolves to an empty method list, which is a checkout with nothing on it. The message names the offending categories.
CUSTOMER_NOT_FOUND, CUSTOMER_ALREADY_EXISTS, CUSTOMER_PAYMENT_METHOD_NOT_FOUND, CUSTOMER_PAYMENT_METHOD_REVOKED.
CHECKOUT_SESSION_NOT_FOUND, CHECKOUT_SESSION_EXPIRED, CHECKOUT_SESSION_INVALID_SIGNATURE, CHECKOUT_PRODUCT_INACTIVE, CHECKOUT_SUBSCRIPTION_REQUIRES_RECURRING_PRODUCT, CHECKOUT_METHOD_NOT_AVAILABLE, CHECKOUT_QUOTE_UNAVAILABLE, CHECKOUT_ASSET_NOT_ACCEPTED, CHECKOUT_ALREADY_COMPLETED, CHECKOUT_EMAIL_REQUIRED, CHECKOUT_BILLING_ADDRESS_REQUIRED, CHECKOUT_PAYMENT_FAILED, CHECKOUT_CAPTCHA_REQUIRED.CHECKOUT_SETUP_REQUIRES_PER_USE_PRODUCT (422) · mode=setup named a product that carries its own price. A setup session stores a card and charges nothing, so pointing it at a priced product gives the payer a page they complete and the merchant a saved card where they expected a sale. The mode is for the product priced per charge (billingMode: pay_as_you_go); anything else is sold with mode=payment.
Writing a code. POST and PATCH /v3/discount-codes refuse a code that could never price:A FIXED amount is not converted: "200" with currency: "EUR" takes €2 off and only ever €2. Scoping it to a $ product is refused here rather than at the checkout, because this is the moment you can still fix it — a code accepted now and refused later reaches your buyer, not you. Scope it to products priced in the same currency, mint one code per currency, or use PERCENTAGE, which carries no currency at all.A code with no productIds covers your whole catalogue and cannot be checked at write time; it is refused at redemption with DISCOUNT_CODE_CURRENCY_MISMATCH when it meets an order in the other currency.Redeeming a code. A submitted code either prices or is refused with a reason · never dropped in silence.POST /v3/checkout/sessions returns 422. The hosted apply-code call returns 200 with { "applied": false, "reason": "…" } · a payer’s typo isn’t an API error. The hosted pay call returns 422 rather than billing full price.
LICENSE_NOT_FOUND, LICENSE_NOT_ACTIVE, LICENSE_ACTIVATION_LIMIT_REACHED, LICENSE_INSTANCE_NOT_FOUND./v3/licenses/* is authenticated with your API key, like every other endpoint, and the lookup is scoped to your account · you can only ask about licences you issued. That is what stops the endpoint being an enumeration oracle: grinding candidate keys through your own credential only ever finds your own. The consequence is deliberate · your customer’s software cannot call this directly, because that would mean shipping sk_live inside it. Your backend relays the check.LICENSE_NOT_FOUND (404) · no licence with that key on your account. The only 4xx validate raises: an expired or revoked licence answers 200 with its status, because a 4xx there pushes callers into treating an expiry as an outage.LICENSE_NOT_ACTIVE (409, on activate) · the licence is expired, disabled, or was never claimed by the buyer. One code rather than three · what the caller does next is the same in all three cases.LICENSE_ACTIVATION_LIMIT_REACHED (409) · every seat is taken. Free one with POST /v3/licenses/deactivate. The limit is a commercial guard-rail, not DRM.LICENSE_INSTANCE_NOT_FOUND (404, on deactivate) · no instance with that id on this licence. Deactivating one already deactivated succeeds.
The headless rail (/v3/crypto/*) refuses before it prices, so a charge never reaches a payer it cannot settle.CRYPTO_AUTOSWAP_MODE_NOT_SUPPORTED (422) · the account converges incoming crypto to a stablecoin on another chain, and wallet_connect cannot do that: the connected wallet settles on the source chain in a single transaction, leaving nothing in between to convert. Retry with mode: "qr_deposit". Nothing is wrong with the account’s configuration.