This page lists the error codes specific to the Money API. Authentication and the shared platform codes (such as unauthenticated, forbidden, validation_failed, and rate_limit_exceeded) are documented in Authentication and API Basics. The codes below are in addition to those.
Error envelope
The transaction details and account endpoints use the same { "success", "message", "error_code" } envelope, but with lowercase snake-case codes — their codes are listed in their own table below.
The charge and payout endpoints still return failures with the legacy machine-readable error_code:
{
"error_code": "INSUFFICIENT_FUNDS",
"message": "Payout amount exceeds the available balance.",
"details": {}
}
Charge and payout failures carry no success flag — branch on the HTTP status and error_code. Only transaction details and account include one.
Some errors carry extra fields alongside details: INSUFFICIENT_FUNDS includes available_balance, and LIMIT_EXCEEDED includes the relevant limit fields (min_amount, max_amount, daily_cap, monthly_cap, velocity_limit, …).
Branch on error_code, not on message. details is optional and carries validation or contextual data when present.
Charge and payout error codes
These are part of the public contract: new codes may be added over time, but existing rows never change meaning. Handle a code you do not recognize gracefully rather than failing hard.
| Code | HTTP | When it occurs |
|---|---|---|
VALIDATION_ERROR |
400 | A business rule rejected the request, for example a PSP channel with a gm_account identifier. details is an array of messages. |
validation_failed |
400 | The body or query failed schema validation before the handler ran — a missing or malformed field. details is an array of {field, message}. Lower case, because it comes from the platform layer rather than the Money handlers. |
CHANNEL_NOT_SUPPORTED |
400 | The channel value is not in the supported list. |
PAYER_NOT_ELIGIBLE |
400 | The payer exists but cannot be charged (charge). |
BENEFICIARY_NOT_ELIGIBLE |
400 | The beneficiary exists but cannot receive funds (payout). |
INSUFFICIENT_FUNDS |
402 | The payout amount exceeds the available balance. |
LIMIT_EXCEEDED |
403 | A payout limit was breached: min/max amount, daily or monthly cap, or velocity (20 payouts per 5 minutes). |
PAYER_NOT_FOUND |
404 | The payer could not be resolved (charge). |
BENEFICIARY_NOT_FOUND |
404 | The beneficiary could not be resolved (payout). |
CHARGE_NOT_FOUND |
404 | The charge is unknown, or not owned by this client. |
PAYOUT_NOT_FOUND |
404 | The payout is unknown, or not owned by this client. |
IDEMPOTENCY_CONFLICT |
409 | The same idempotency-key was reused with a different body (see Authentication). |
DUPLICATE_REQUEST |
409 | An identical payout — same beneficiary and amount — was already submitted within the last five minutes, so no new payout was created. details.reference is the existing payout. Vary the amount or wait out the window. |
PROVIDER_UNAVAILABLE |
503 | The PSP provider is temporarily unavailable. Safe to retry with backoff. |
A plain 401 surfaces for a missing or invalid JWT, HMAC signature, or timestamp, and 500 INTERNAL_ERROR for unexpected failures.
Transaction details and account error codes
GET /money/v1/transaction-details/:id and GET /money/v1/account return the standard failure envelope — branch on error_code:
{
"success": false,
"message": "No transaction details found for this API client.",
"error_code": "transaction_not_found"
}
Authentication and routing failures on these endpoints use the same shape with platform codes: 401 unauthenticated (missing or invalid token, bad signature, or a timestamp outside the 300-second window), 403 forbidden (the client is not entitled to the route’s slug), 404 not_found (no such route), 429 rate_limit_exceeded, and 500 internal_error.
Validation failures
A request that fails schema validation returns 400 validation_failed with a details array naming each offending field — the same shape the Courier API uses:
{
"success": false,
"message": "querystring/limit must be integer",
"error_code": "validation_failed",
"details": [
{ "field": "limit", "message": "must be integer" }
]
}
field is the path of the offending value, dotted for nested objects (payer_identifier.type). details is present only on validation failures; every other error carries success, message and error_code alone.
| Code | HTTP | When it occurs |
|---|---|---|
validation_failed |
400 | The request body or parameters failed validation. details names the fields. |
merchant_account_not_configured |
400 | The merchant has no primary Gozem Money wallet configured. |
merchant_profile_incomplete |
400 | Required merchant fields (unique_id, wallet number) are missing. |
merchant_not_linked |
403 | The client is not linked to a merchant account. |
merchant_not_resolved |
403 | The merchant profile could not be loaded for this client. |
transaction_not_found |
404 | The transaction was not found, or your client is neither payer nor payee. |
account_not_found |
404 | Wallet details could not be retrieved for this merchant. |
service_unavailable |
502 | Gozem could not load the data upstream. Safe to retry with backoff. |
Retrying
Treat 4xx as a problem with the request — fix the payload, payer, account, or idempotency condition named by the code rather than resending. The retryable cases are PROVIDER_UNAVAILABLE (503), service_unavailable (502, transaction details and account), and rate_limit_exceeded (429, see API Basics); retry those with exponential backoff rather than immediately.
A note on the bank error shapes
The Bank API (distributor balance, fund creation, and fund destruction) uses the same { success, message, error_code } failure envelope, with lowercase snake-case codes such as request_not_found and request_state_conflict. Validation failures there carry the same details array described above. Its approve and cancel acknowledgements add a request_id, and carry error_code only when success is false. This page covers the merchant-facing Money endpoints only.