Errors & retry
Two independent error surfaces exist in this integration, and it matters which one you're debugging:
- Operator API errors — the platform's response to a call you made (
launch,games,kick,rounds/replay, free spins). - Wallet callback errors — how the platform interprets your response when it calls your
POST /v1/wallet/transactionorGET /v1/wallet/balance.
Signature & replay errors (shared by both directions)
These apply identically whether you're the one being verified (Operator API) or the platform is (wallet callback) — see Signing & authentication.
| Status | Meaning | Body |
|---|---|---|
401 | Bad, missing, or expired-timestamp signature | Plain text: invalid signature — not JSON, since this check happens before the request reaches any handler |
401 | A nonce that's already been used (replay) | JSON { "error": "replayed request" } |
400 | Missing or oversized X-Nonce | JSON { "error": "invalid nonce" } |
Operator API status codes
Every endpoint in the Operator API reference shares this shape. Business errors (game not found, currency not supported, etc.) are JSON { "error": "<message>" }.
| Status | Meaning |
|---|---|
400 | Invalid request body, or a value doesn't validate (unsupported currency/language, missing required field) |
401 | No operator identity resolved — see the signature errors above |
403 | The launch was blocked by risk control (player blocklist / self-exclusion) — see POST /v1/operator/games/launch |
404 | The game doesn't exist, or exists but isn't visible to your operator tenant (deliberately indistinguishable, so this can't be used to probe the catalog); or, for round replay, no matching round under your tenant |
429 | Rate limit exceeded for your tenant — see Rate limits below |
500 | Unexpected platform-side failure |
Rate limits
Every operator API call is rate-limited per tenant. Exceeding it returns:
429
Retry-After: <seconds>{ "error": "rate limit exceeded" }Back off and retry after the number of seconds in Retry-After rather than retrying immediately. If your expected traffic needs a higher limit than your default, raise it with your integration contact before you hit this in production.
Wallet callback: what the platform does with your response
You don't retry calls to the platform here — the platform is calling you, and interprets whatever you return:
| Your response | Platform's interpretation |
|---|---|
200, { status: 'OK', balance } | Applied successfully |
200, { status: 'DECLINED', balance } — BET only | Normal business decline (e.g. insufficient funds); not retried |
200, { status: 'DECLINED' } on WIN/ROLLBACK/ADJUSTMENT | Invalid — these types must not decline; treat a real failure as a non-200 instead |
Any non-200 status | Treated as a failure. The platform may retry with the exact same transactionId — see Idempotency |
| No response within your configured timeout | TIMED_OUT — retried or resolved later via reconciliation, same as a non-200 |
Because a retry reuses the identical transactionId, the single most important thing your implementation does is answer a repeat request with the same cached response, not re-apply the balance change — see Idempotency in the wallet callback API reference.
Idempotency and retries
Every retry — of a call you make, or a call the platform makes to you — reuses the exact same transactionId (or, for free spins, requestRef/idempotencyKey). That shared ID is what lets either side tell "the same logical attempt, retried" apart from "a new attempt." Whichever side is receiving the retry is responsible for deduplicating on it; neither side invents a new ID for a retry of the same attempt.