Errors
The Merchant API uses conventional HTTP status codes and returns a consistent JSON error envelope for every error.
Error envelope
{
"code": "validation_failed",
"message": "Validation failed",
"request_id": "req_2c1f8a7b9e3d4051",
"errors": [
{"field": "currency", "message": "must be a valid ISO 4217 currency code"},
{"field": "amount", "message": "must not be null"}
]
}
| Field | Type | Description |
|---|---|---|
code | string | A stable, machine-readable error code (snake_case). Branch on this, not on message. |
message | string | A human-readable description. May change without notice; do not parse it. |
request_id | string | Correlation ID for this request. Include it when contacting support. |
errors | array | Present for validation failures. Each entry has a field and a message. May be omitted otherwise. |
request_idEvery response is traceable by its request_id. Logging it on your side makes support
investigations dramatically faster.
HTTP status codes
| Status | Meaning |
|---|---|
200 OK | Request succeeded. |
201 Created | Resource created (e.g. a checkout session). |
400 Bad Request | Malformed JSON, invalid request, or validation failure. |
401 Unauthorized | Missing/invalid API key, or signature verification failed. |
403 Forbidden | Authenticated, but not permitted to perform this action. |
404 Not Found | The referenced resource does not exist for your account. |
405 Method Not Allowed | The HTTP method is not supported on this path. |
409 Conflict | The request conflicts with current state (e.g. duplicate reference, invalid state transition, or a differing card-storage consent re-asserted for the same payment). |
415 Unsupported Media Type | The request Content-Type is not application/json. |
422 Unprocessable Entity | The request was well-formed but could not be reconciled — e.g. a provider callback whose amount or currency disagrees with the transaction. |
500 Internal Server Error | An unexpected error on Orchestr's side. Safe to retry idempotently. |
502 Bad Gateway | A provider or downstream service was unreachable. Retry with backoff. |
503 Service Unavailable | A dependency is temporarily unavailable. Retry with backoff. |
201POST /v1/checkout/sessions, /v1/refunds, /v1/payouts and /v1/transfers return 201, but
POST /v1/payments and the card verbs (/v1/payments/capture, /v1/payments/void,
/v1/payments/{id}/3ds/complete, /v1/transfers/{orderId}/execute) return 200. Branch on the
response body, not on the status code.
Transport & authentication error codes
These come from the API layer common to all endpoints.
code | HTTP | When it happens |
|---|---|---|
malformed_request | 400 | The request body is not valid JSON. |
invalid_request | 400 | The request is structurally invalid. |
validation_failed | 400 | One or more fields failed validation; see errors[]. |
unauthenticated | 401 | Missing or invalid API key. |
invalid_signature | 401 | The X-Signature did not verify, or its timestamp was outside the replay window. |
malformed_signature_header | 401 | The X-Signature header is missing or not in the t=…, v1=… form. |
forbidden | 403 | The account is not permitted to perform the action. |
conflict | 409 | The request conflicts with the resource's current state. |
method_not_allowed | 405 | The HTTP method is not supported on this path. |
unsupported_media_type | 415 | The Content-Type is not application/json. |
missing_request_parameter | 400 | A required query parameter is missing. |
missing_request_header | 400 | A required header is missing. |
invalid_parameter | 400 | A path parameter is malformed. |
internal_error | 500 | Unexpected server-side error. |
general_error | 500 | Unexpected server-side error raised below the API layer. |
downstream_error | 502 | A downstream service failed. The message relays its report. |
Domain error codes
Returned by specific resources. The code is the snake_case form of the values below.
Payments, orders & checkout sessions
code | HTTP | Meaning |
|---|---|---|
order_not_found | 404 | No order matches the identifier/reference. |
invalid_order_status_transition | 409 | The order cannot move to the requested state. |
merchant_reference_required | 400 | A merchant_reference is required. |
duplicate_merchant_reference_id | 409 | That merchant_reference already exists for your account. |
transaction_not_found | 404 | No transaction matches the identifier. |
invalid_transaction_status | 400 | The transaction is not in a valid status for this operation. |
checkout_session_not_found | 404 | No checkout session matches the identifier/reference. |
invalid_session_state | 409 | The checkout session cannot transition from its current state (e.g. cancelling a completed session). |
country_required | 400 | A country is required for this operation. |
unknown_payment_method | 400 | The supplied payment method is not recognised. |
payment_method_not_found | 404 | The referenced payment method does not exist. |
payment_method_not_supported_by_provider | 409 | The selected route's provider does not support this method. |
payment_unroutable | 404 | No route is available to process the payment. |
ambiguous_original_transaction_target | 400 | Supply exactly one of order_id, transaction_id, or merchant_reference when creating a refund. |
original_transaction_not_found | 404 | No original transaction matches the supplied refund target. |
original_transaction_not_eligible | 409 | The original transaction is not eligible for refund. |
refund_amount_invalid | 400 | The refund amount must be positive. |
refund_amount_exceeds_refundable | 400 | The refund amount exceeds the remaining refundable balance. |
refund_already_fully_refunded | 409 | The original transaction has already been fully refunded. |
refund_in_progress | 409 | A refund is already in progress for that transaction. |
refund_requires_transaction_target | 400 | The payment was captured in parts, so order_id is ambiguous — name the specific capture by transaction_id. |
merchant_reference_ambiguous | 409 | The merchant_reference matches more than one transaction. |
transaction_provider_mismatch | 400 | The transaction does not belong to the specified provider. |
customer_not_found | 404 | No customer matches the identifier. |
payment_method_not_supported_as_destination | 400 | This payment method cannot be used as a destination instrument. |
consent_conflict | 409 | A different card-storage consent was asserted for a payment that already has one. Re-sending an identical consent is safe. |
Card payments
code | HTTP | Meaning |
|---|---|---|
card_payment_invalid_order_state | 409 | The order is not awaiting a card payment. |
card_payment_in_progress | 409 | A payment for this order is already in progress. |
payment_unexpected_state | 409 | The payment is not in a state that allows this operation — capturing an already-captured payment, voiding after a final capture, and so on. The message names the verb, the current status, and the expected one. |
capture_amount_exceeds_authorized | 400 | The capture amount is more than was authorized, or is not positive. |
capture_amount_exceeds_remaining | 400 | The capture amount is more than the uncaptured remainder, counting captures still in flight. |
card_api_unavailable | 502 | The card authorization service is unreachable. Reaches you as code: provider_unavailable — see below. |
A card refused by the issuer or the provider returns 200 OK with status: "failed", not a
4xx. That applies to authorizations, captures, and voids alike. The codes above are pre-flight
rejections — the request never reached a provider. To learn why an attempt failed, read
decline_code on the transaction.
Payouts
code | HTTP | Meaning |
|---|---|---|
payout_not_found | 404 | No payout matches the identifier. |
payout_in_progress | 409 | A payout is already in progress for that transaction. |
Transfers
code | HTTP | Meaning |
|---|---|---|
transfer_not_found | 404 | No transfer matches the identifier. |
transfer_not_supported_by_provider | 409 | The routed provider channel does not support transfers. |
transfer_destination_invalid | 400 | The destination is invalid or incomplete — for a card transfer, most often a missing consent. |
transfer_destination_not_registered | 409 | The destination is not registered with the provider. |
transfer_unroutable | 400 | No provider channel could be routed. Supply channel_id. |
transfer_card_token_required | 400 | A card_token is required when the destination is a card. |
transfer_invalid_order_state | 409 | POST /v1/transfers/{orderId}/execute was called on a transfer not awaiting card collection. |
transfer_in_progress | 409 | This transfer is already being executed. |
recipient_pending_verification | 409 | The provider recipient is still pending verification. |
Additional provider-configuration error codes exist for account setup performed in the Dashboard. They are not part of the merchant request/response flow documented here.
Decline codes in the error envelope
Error codes and decline codes are normally separate vocabularies: an error
code says the request was rejected, a decline_code says a payment attempt failed.
There is one deliberate overlap. When an error carries an associated decline code, that decline code
is what appears in the envelope's code. The case you are most likely to meet is
card_api_unavailable, which reaches you as:
{
"code": "provider_unavailable",
"message": "Card authorization service is unavailable",
"request_id": "req_2c1f8a7b9e3d4051"
}
So a small number of values from the decline vocabulary can legitimately appear as an error code
with a 5xx status. Branch on code as usual — just don't assume a code you don't recognise is
absent from the decline-code reference.
Webhooks
code | HTTP | Meaning |
|---|---|---|
webhook_not_found | 404 | The referenced webhook endpoint does not exist. |
webhook_limit_exceeded | 400 | The maximum number of webhook endpoints has been reached. |
webhook_delivery_not_found | 404 | The referenced webhook delivery does not exist. |
webhook_delivery_not_retryable | 400 | The delivery is not in a state that can be retried. |
Handling errors
- Branch on
code, never onmessage. - For
validation_failed, surface the per-fielderrors[]to the caller or your logs. - Treat
401 invalid_signature/malformed_signature_headeras a signing bug — re-check the signed string and clock before retrying. 409 duplicate_merchant_reference_idmeans the operation was already accepted under that reference — fetch the existing resource by reference rather than retrying with a new reference.500and network errors are safe to retry, provided you reuse the samemerchant_referenceso the operation stays idempotent.