# Error codes > Every error code the API returns, its status, and whether retrying can help. Source: https://casinoapipro.com/docs/error-codes Every code the API returns, what it means, and whether retrying can help. The envelope is described in [Errors](/docs/errors). > **Retryable is a field, not a guess** > > Each error carries `retryable`. Only five codes ever set it true — `WALLET_TIMEOUT`, `WALLET_UNAVAILABLE`, `RATE_LIMITED`, `SERVICE_UNAVAILABLE` and `INTERNAL_ERROR` — and they are marked below. Branch on the field rather than on the status: a `409` is never worth repeating, and a `504` always is, with the same `transaction_id`. ## Authentication and access | Name | Type | Meaning | | --- | --- | --- | | `UNAUTHORIZED` | 401 · no retry | Missing or malformed Authorization header. | | `INVALID_CREDENTIALS` | 401 · no retry | The key and secret pair did not match, or the token failed verification. | | `TOKEN_EXPIRED` | 401 · no retry | Mint a fresh token and repeat the request. Not marked retryable because the identical request would fail again — you must get a new token first. | | `INVALID_SIGNATURE` | 401 · no retry | An HMAC signature you sent us did not verify. | | `IP_NOT_ALLOWED` | 403 · no retry | Source address outside the credential’s allowlist. | | `FORBIDDEN` | 403 · no retry | Authenticated, but not permitted — your role, or a capability your plan does not include. The message names the plan that would allow it. | | `INSUFFICIENT_SCOPE` | 403 · no retry | The token is valid but was minted for another audience — a launch token used against the operator API, say. See [token audiences](/docs/authentication#audiences). | ## Account state | Name | Type | Meaning | | --- | --- | --- | | `OPERATOR_SUSPENDED` | 403 · no retry | The account is suspended. Contact support. | | `OPERATOR_NOT_LIVE` | 403 · no retry | A live-only operation on an account whose real-money play is suspended. | | `REAL_MONEY_DISABLED` | 403 · no retry | Real-money play is switched off — either platform-wide, or suspended on your account. The message says which; only one of them is something you can act on. | | `BRAND_NOT_FOUND` | 404 · no retry | No such brand on this account. | | `PLAYER_BLOCKED` | 403 · no retry | Your own wallet refused the player — self-exclusion, a KYC hold, or an unknown player id. | ## Validation | Name | Type | Meaning | | --- | --- | --- | | `VALIDATION_ERROR` | 422 · no retry | A field is missing or the wrong shape. Carries `details` naming each offending path — a JSON number where a decimal string was required is the usual cause. | | `CURRENCY_NOT_SUPPORTED` | 422 · no retry | The currency is not enabled for your account. Games do not restrict currencies — add it with PATCH /v1/business and the next session opens. | | `CURRENCY_MISMATCH` | 422 · no retry | The movement currency differs from the session currency. | ## Money | Name | Type | Meaning | | --- | --- | --- | | `INSUFFICIENT_FUNDS` | 402 · no retry | Your wallet refused: balance below the stake. Nothing moved. | | `BET_LIMIT_EXCEEDED` | 422 · no retry | Stake outside the min/max configured for the game on your account. | | `TRANSACTION_CONFLICT` | 409 · no retry | The same `transaction_id` arrived again with a different body. Fix the caller; nothing moved. Retrying can only conflict again. | | `TRANSACTION_NOT_FOUND` | 404 · no retry | No movement with that id on this account — including a `reference_transaction_id` on a refund or rollback that names nothing. | | `TRANSACTION_ALREADY_REVERSED` | 409 · no retry | The original movement has already been refunded or rolled back. | ## Your wallet These describe your wallet’s behaviour, not ours. See [wallet integration](/docs/wallet). | Name | Type | Meaning | | --- | --- | --- | | `WALLET_TIMEOUT` | 504 · retry | Your wallet did not answer in time. The transaction is left pending and the outcome is genuinely unknown — retry with the same `transaction_id`. | | `WALLET_UNAVAILABLE` | 503 · retry | We could not reach your wallet at all. Same treatment as a timeout. | | `WALLET_REJECTED` | 502 · no retry | Your wallet refused with a code we do not recognise, or one that is terminal. The raw code is preserved on the transaction record. | | `WALLET_NOT_CONFIGURED` | 503 · no retry | No active wallet integration for this environment. Most often a live key against an account that has only configured a sandbox wallet — the two are separate records and one never falls back to the other. | ## Games, sessions and rounds | Name | Type | Meaning | | --- | --- | --- | | `GAME_NOT_FOUND` | 404 · no retry | No such game, or it is not in your catalogue at all. | | `GAME_NOT_ENABLED` | 403 · no retry | The game is in your catalogue but switched off for you. | | `GAME_UNAVAILABLE` | 503 · no retry | The game exists and is enabled, but is down platform-wide right now. | | `SESSION_NOT_FOUND` | 404 · no retry | Unknown session, or it belongs to another operator. | | `SESSION_EXPIRED` | 410 · no retry | The session passed its expiry. Create a new one. | | `SESSION_CLOSED` | 410 · no retry | The sitting has ended. | | `ROUND_NOT_FOUND` | 404 · no retry | Unknown round id. | | `ROUND_IN_PROGRESS` | 409 · no retry | One open round per player _per game_ — a round open in one game never blocks another. If it will not clear, the round was opened but its stake never taken; void it with [`POST /v1/rounds/{roundId}/void`](/docs/history#void). | | `ROUND_ALREADY_SETTLED` | 409 · no retry | The round has already reached a terminal state; no further action is possible. | ## Platform | Name | Type | Meaning | | --- | --- | --- | | `RATE_LIMITED` | 429 · retry | Too many requests. Honour `Retry-After` and add jitter. See [rate limits](/docs/rate-limits). | | `NOT_FOUND` | 404 · no retry | No route, or a resource outside your account. We do not distinguish the two — see below. | | `CONFLICT` | 409 · no retry | The resource already exists. | | `INTERNAL_ERROR` | 500 · retry | Our fault. Retry; quote the request id if it persists. | | `SERVICE_UNAVAILABLE` | 503 · retry | Briefly unable to serve, or shedding load under saturation — see [rate limits](/docs/rate-limits#503). Always retryable. Settlement is never shed. | > **404 rather than 403 for another operator’s data** > > Asking for a session, round or transaction that belongs to someone else returns `NOT_FOUND`, not `FORBIDDEN`. Answering “forbidden” would confirm the resource exists, which leaks the shape of other tenants’ data to anyone willing to enumerate ids. Absence and denial look identical from outside.