Every error response carries a problem field — a stable string your
code branches on. Status codes group; problem discriminates.
Auth
problem | Status | Meaning |
|---|
auth.missing_token | 401 | No Authorization header. |
auth.invalid_token | 401 | Token doesn’t exist or was revoked. |
auth.expired_token | 401 | Rotation grace period elapsed. |
auth.wrong_environment | 401 | Sandbox token on live URL or vice-versa. |
auth.scope_required | 403 | Token lacks the required scope. |
auth.aal2_required | 403 | Endpoint requires AAL2; principal hasn’t asserted it. |
auth.aal2_step_up_required | 405 | AAL2 expired; step up. |
auth.tenant_mismatch | 403 | Cross-tenant access attempted. |
Idempotency
problem | Status | Meaning |
|---|
idempotency.required | 400 | Endpoint requires Idempotency-Key. |
idempotency.body_mismatch | 422 | Same key, different body. |
idempotency.in_flight | 409 | Same key request currently executing. |
idempotency.replay_too_late | 410 | Key TTL elapsed; original request can no longer be replayed. |
Validation
problem | Status | Meaning |
|---|
validation.failed | 422 | One or more fields invalid; see errors[]. |
validation.unknown_field | 400 | Request contained a field the API doesn’t recognise. |
validation.currency_invalid | 422 | Currency wasn’t ISO-4217 alpha-3 uppercase. |
validation.amount_invalid | 422 | Amount wasn’t a decimal string. |
Rate limiting
problem | Status | Meaning |
|---|
rate_limit.exceeded | 429 | Too many requests; back off per Retry-After. |
Subscriptions
problem | Status | Meaning |
|---|
subscription.illegal_transition | 422 | The requested transition isn’t allowed in the current state. |
subscription.commitment_active | 422 | Cancellation rejected; commitment forbids it. |
subscription.proration_invalid_currency | 422 | New plan currency differs from existing. |
subscription.no_payment_method | 422 | Activation requires a default payment method. |
subscription.plan_archived | 422 | The plan is archived; no new subscriptions can target it. |
Invoices
problem | Status | Meaning |
|---|
invoice.locked | 422 | Mutation attempted on a non-draft invoice. |
invoice.cannot_void_paid | 422 | Void rejected because amount_paid > 0. |
invoice.cannot_finalize_empty | 422 | Finalisation rejected; no line items. |
invoice.tax_recompute_required | 422 | Tax inputs changed; call recompute-tax first. |
Payments
problem | Status | Meaning |
|---|
payment.requires_action | 200 | 3DS / SCA needed; see next_action. (Not strictly an error.) |
payment.declined | 422 | Provider declined; see last_error. |
payment.duplicate | 409 | Payment already in-flight against this invoice. |
payment.cannot_refund_failed | 422 | Refund attempted on a non-succeeded payment. |
payment.refund_exceeds_amount | 422 | Sum of refunds would exceed payment amount. |
payment.method_expired | 422 | The payment method’s card has expired. |
payment.method_in_use | 409 | Detach attempted on a method bound to an active subscription. |
Wallets
problem | Status | Meaning |
|---|
wallet.insufficient_funds | 422 | Wallet balance < requested amount. |
wallet.currency_mismatch | 422 | Wallet currency != invoice currency. |
wallet.below_drift_floor | 422 | Settlement would drop balance below drift_floor. |
FX
problem | Status | Meaning |
|---|
fx.stale_rate | 503 | Most recent rate is older than the staleness threshold. |
fx.unsupported_pair | 422 | Currency pair has no rate source configured. |
Tax
problem | Status | Meaning |
|---|
tax.engine_unavailable | 503 | External tax engine returned an error; finalisation rolled back. |
tax.address_required | 422 | Tax computation requires a billing address. |
tax.id_invalid | 422 | Tax ID failed format validation. |
Webhooks
problem | Status | Meaning |
|---|
webhook.signature_invalid | 401 | Inbound provider webhook failed HMAC verification. |
webhook.endpoint_disabled | 410 | Endpoint was auto-disabled after sustained 4xx. |
Misc
problem | Status | Meaning |
|---|
not_found | 404 | Resource doesn’t exist (or isn’t visible to your tenant). |
gone | 410 | Resource existed and is permanently removed. |
internal_error | 500 | An unexpected error on our side; reach out with the trace_id. |
unavailable | 503 | A downstream is unavailable. Backoff and retry. |
What if you see one not listed here?
The list grows. New error codes are non-breaking — see
Versioning. If your code branches on an unknown
problem, fall back to the HTTP status. Open an issue or contact
support; we’ll add it.