Errors
HTTP status codes and the error body format.
Body shape
Every error response uses a single, stable shape:
{
"message": "human-readable explanation"
}The message is intended for logging and developer tooling, not for
end-user display.
Status codes
| Status | Meaning | Typical cause |
|---|---|---|
200 | Success. | Operation accepted; check the body for status. |
400 Bad Request | The request was malformed. | Invalid JSON; a required field is missing; a UUID field is not a strict, canonically-formatted UUID; an amount fails to parse. |
401 Unauthorized | API key missing or invalid. | X-API-Key header absent in strict mode, or the value is not a known key. |
403 Forbidden | API key scope mismatch. | The request tries to act outside the shop the X-API-Key resolves to. |
404 Not Found | The referenced entity does not exist (or belongs to a different shop). | Unknown operation_id on confirm/state endpoints. Cross-shop lookups deliberately return 404 rather than leaking existence. |
412 Precondition Failed | The operation is not in a state that allows the action, or merchant_operation_id collides with a different body. | Confirming an operation that is already terminal or does not require external confirmation; re-using a merchant_operation_id with a different request body. |
415 Unsupported Media Type | The uploaded receipt media type is not accepted. | Receipt is not PDF, JPEG, PNG, GIF, or WebP. |
429 Too Many Requests | A downstream resource limit is currently exhausted. | gRPC ResourceExhausted; retry with backoff. |
500 Internal Server Error | Unexpected server-side failure. | Generic sanitized catch-all; inspect/reconcile state before retrying a non-idempotent action. |
503 Service Unavailable | A required downstream service timed out or is unavailable. | gRPC Unavailable or DeadlineExceeded; retry an idempotent request with backoff. |
Idempotency collisions on merchant_operation_id (same id reused with
a different body) surface as 412 Precondition Failed with a message
describing the conflict — the dispatcher's idempotency check is mapped
from gRPC AlreadyExists to 412, not 409.
A client-side cancellation (the HTTP request context being cancelled
before the gateway finishes the gRPC call) is surfaced as
400 Bad Request with "request canceled", not 5xx.
“Retryable transport error” does not make an arbitrary business action safe to
repeat. Create endpoints are protected by (shop, merchant_operation_id);
state reads are naturally safe. For confirmation or another state-changing
request, first determine whether the server accepted the previous request and
reuse the same operation identity.
What is NOT an error
A successful HTTP response (200) does not mean the operation is
completed. The operation may still be pending and reach a final
status later — see Status lifecycle.
The same applies to /v1/confirm_payment: a 200 only means the confirmation
request was accepted. Public confirmation always uses
confirmation_type=receipt, so a 200 means the gateway uploaded the receipt
and attached it to the operation for validation/review. A clean validation may
complete the payment automatically; suspicious, wrong-amount, or unavailable
validation leaves it pending for an operator. Upload failures return an error
and the operation stays pending.