Error Codes
Three error models, one set of HTTP conventions
CrissCross APIs share HTTP-status semantics — a 401 always means an auth problem, a 422 always means a business-rule violation — but the error body shape is defined per service domain. There are three distinct error models:
- Authentication (
/auth/oauth2/token) follows the OAuth 2.0 standard (RFC 6749 §5.2):{ error, error_description, error_uri }with a fixed set oferrorvalues. - Exchange uses a structured envelope with a stable, machine-readable code enum (
QUOTE_EXPIRED,INSUFFICIENT_BALANCE, …) and adetailsobject. - Collect (payments), Payouts, and related services use a flat envelope (
message,error,statusCode) aligned with standard HTTP semantics. Branching is driven by the HTTP status; provider-specific failure reasons are carried on the transaction’s state, not on the HTTP error envelope.
These are three deliberate, distinct approaches. If you integrate one service, read just its section.
Shared HTTP-status semantics
Every CrissCross API uses standard REST status codes. Regardless of which service you call, the status code tells you the category of failure.
Authentication
The Authentication API (POST /auth/oauth2/token) implements OAuth 2.0 Client Credentials and returns errors in the format defined by RFC 6749 §5.2. This shape is specific to the token endpoint — once you have a token, the service you call next uses its own error envelope (see below).
Envelope
Code reference
For the Client Credentials flow used here, you will most commonly see invalid_client (bad credentials) and invalid_request (missing or malformed body).
Exchange
The Exchange API returns a structured error envelope with a stable, machine-readable code, a human-readable message, and a details object whose shape varies per code.
Envelope
code is part of the API contract. message and details may be refined over time.
Code reference
Codes are grouped by the situation that produces them.
Request validation
Authentication & authorization
Currency & rates
Quotes & orders
Balances & amounts
Beneficiaries & deposits
Withdrawals
Idempotency
Recommended handling for Exchange
Collect (payments) and Payouts
Collect (card, bank transfer, mobile money, Pay-by-Bank, Hosted Checkout, Secure Fields, Direct API), Payouts, and the related surfaces (Verification, Reporting, Settlement, Payment Rules, Payers) share a single error envelope. This envelope is different from Exchange’s by design: it is a flat shape aligned with standard HTTP semantics, where programmatic branching is driven by the HTTP status. Provider-specific failure reasons — card declines, mobile-money rejections, bank-transfer reversals — are carried on the transaction’s state, not on the HTTP error envelope.
Envelope
How to branch
Drive control flow off the HTTP response status. The body fields provide context for logs, support tickets, and user-facing messaging — they are not intended for programmatic branching.
Provider error details on the transaction
Generic HTTP failures (validation, auth, missing resource, conflict) are conveyed by HTTP status + a clear message in the envelope above. Payment-method-specific failures — card declines, mobile-money rejections, bank-transfer reversals — are different: they don’t surface as HTTP errors. The request that initiates a payment returns 201 Created even when the underlying provider declines the transaction. The provider’s response is attached to the transaction’s state instead, with the provider’s own code and message included verbatim.
The error/failed state on a transaction carries these fields:
Example — a card payment declined for insufficient funds, returned as HTTP 201 from POST /payment:
This lets your integration do both:
- Branch on the normalised
code(theAuthFailureCode/ErrorCodeenum below) for consistent cross-provider handling. - Surface or log the raw
connectorFailureCode/connectorFailureMessagewhen you need the provider’s own vocabulary — for reconciliation, support tickets, or showing the cardholder a network-specific reason.
The same shape applies to:
- The synchronous response from the payment-initiation endpoint.
- The transaction resource retrieved via polling.
- Webhook payloads carrying transaction state updates.
Normalised code reference (Collect & Payouts)
The values below are the complete set of normalised codes returned in authState.code across every payment method and payout rail. They split into two enums:
AuthFailureCode— the underlying provider, acquirer, or payer rejected the transaction. Set whenauthState.stateisfailed.ErrorCode— a system, configuration, validation, or compliance problem prevented the transaction from being processed. Set whenauthState.stateiserror.
Codes prefixed PAYIN_* are returned only on Collect (payments) transactions; codes prefixed PAYOUT_* are returned only on Payouts transactions; all others can appear on either.
AuthFailureCode — provider declines and rejections
3-D Secure failures (card only)
Generic declines
Insufficient funds
Invalid data, cards, and PINs
Limits exceeded
Timeouts and conflicts
ErrorCode — system, validation, and compliance errors
System and processing
Validation
Capability gating
Compliance screening
For guidance on which codes are most common per payment method, and the user-facing messaging appropriate for each, see the per-method guides:
- Card
- Bank Transfer
- Pay By Bank
- Mobile Money: MTN MoMo, Airtel, M-Pesa, Tanzania Mobile Money
- Refunds
Rules of thumb (any service)
- Never branch on
message. Message strings are for humans and may change without notice. - HTTP status is always part of the contract. For Exchange, you also have a stable
error.code. For Collect and Payouts, the HTTP status is the primary signal on the request response, and the normalisedauthState.codeis the contract on the transaction. - Retry only
429and5xx, with exponential backoff. Retrying4xxblindly will fail again the same way. - Use
Idempotency-Keyon every mutating call. It turns a5xxretry from “did this just happen twice?” into a safe, repeatable operation. - Log the full error body on every failure, even codes you don’t recognise — it’s the fastest path to a support resolution.