Errors and limits
Errors and limits
One answer shape, fixed error codes, clear limits.
Answer shape
Every answer from our API has one shape. Your answers to our calls use the same shape without traceId, because the trace id is ours.
| Field | Type | What it is |
|---|---|---|
ok | boolean | One check tells you if it worked, for every call. |
result | object | The answer itself. |
error.code | string | Act on this fixed list, never on text. |
error.message | string | A person reading logs sees what went wrong. |
traceId | string | One search finds every call and retry that one action caused. |
{ "ok": true,
"result": { … },
"traceId": "tr-01JC…" }
{ "ok": false,
"error": { "code": "session_closed", "message": "This session is closed." },
"traceId": "tr-01JC…" }- Every call is a POST with a JSON body. The version is in the address. v1 never breaks: a field can be added, but never removed, renamed or given a new meaning.
- An answer that is not ok:true or ok:false, or no answer, counts as unknown. Retry with the same requestId.
Error codes
Each code has one fixed next step.
| Code | When | Your next step |
|---|---|---|
unknown_key | No API key with this Key-Id in this system, or it was deleted. A Sandbox API key on Live, or the other way. | Use the right API key. |
ip_not_allowed | The call came from an address not on the API key’s list. | Add the address to the API key in the back office. |
bad_signature | The signature does not match, or the API key’s public key is not the one used. | Fix the key or the signing. The back office lists the last refused calls and why. |
environment_not_found | No environment with this name in this system. | Check the name, or create it. |
not_allowed | The API key may not use this environment, or this operation. | Create an API key with that access in the back office. |
request_too_old | Sent-At is more than 5 minutes from our clock. | Fix the server clock. A retry with a new send time works. |
invalid_request | A field is missing or wrong, for example a firstSentAt more than 5 minutes in the future. message names the field. | Fix the call. Sending it again unchanged fails again. |
request_expired | firstSentAt is older than 7 days. Nothing is done. | A bug or a stuck job on your side. Look up the result with session.get or round.get. |
duplicate_mismatch | A requestId we have seen comes with a different body. | A bug on your side: each new change needs a new id. |
game_not_found | No game with this gameId for your account. | Pull games.list again. |
game_disabled | The game is turned off in the back office. | Hide the game. A player with an open Mines round can still start it. |
bet_settings_invalid | min is above max, default is outside them, or a value is 0 or less. | Fix the bet settings. |
currency_not_supported | The currency is not set up for your account. | Set it up in the back office, or use another currency. |
session_not_found | No session with this sessionId in this environment. | Check the id and the Environment header. |
session_closed | session.updateBetSettings on a closed session. | Nothing. The player’s next start gets the new settings. |
round_not_found | No round with this roundId in this environment. | Check the id and the Environment header. |
rate_limited | Over a limit. HTTP 429. error.retryAfter gives the seconds to wait. | Wait that long, then retry with the same requestId. |
temporarily_unavailable | Our side can’t answer now. | Retry after 1 s, 5 s, then 30 s, with the same requestId. |
session.close on a session that is already closed is not an error. It answers ok with the session as it is.
Retries
Your calls to us
- Every change carries a requestId. Send it again after a timeout: the repeat gets the first answer back.
- We keep each requestId and its answer for 7 days.
- Every change also carries firstSentAt, the time you first sent it. It stays the same on every retry. A change older than 7 days gets request_expired and nothing is done. A firstSentAt more than 5 minutes in the future gets invalid_request.
- A retry keeps the same body and ids, with a new Sent-At and a new signature.
- A call refused before it ran (rate_limited, temporarily_unavailable, unknown_key, ip_not_allowed, bad_signature, request_too_old) keeps nothing, so its retry runs for real.
- With temporarily_unavailable, retry after 1 s, 5 s, then 30 s.
- With rate_limited, wait retryAfter seconds.
Our calls to your server
- A wallet answer is accepted ("ok": true), refused ("ok": false with a code), or unknown (no answer in time, HTTP 500, or an answer we can’t read).
- wallet.win, wallet.loss and wallet.refund are sent again with the same transactionId: after 1 s, 5 s and 30 s. Then every 5 minutes for 24 hours. Then every hour, for 30 days.
- Not accepted after 1 hour: it goes on the stuck list in both back offices. After 30 days it stays there until a person resolves it. It is never dropped.
- We email every owner of your account when an item first goes on the stuck list. At most one email an hour per account.
- session.closed is sent again until you accept it, for up to 30 days. Then the back office shows “Not delivered”.
- A wallet.stake with an unknown answer is not retried. The round goes to refunding.
Rate limits
| Limit | Sandbox | Live |
|---|---|---|
| Calls to our API, per account | 20 a second | 200 a second |
| environment.create, per account | 100 an hour | Live has one environment |
- A call over a limit gets rate_limited, with retryAfter in seconds. Nothing is done, so the retry is safe. A refused call does not count.
- Calls are counted over the last second (and the last hour), not in fixed blocks.
- session.close and sessions.close never count, so a player can always be closed.
- Our calls to your wallet and events never count against your limits.
- Live limits can be raised.
{ "ok": false,
"error": {
"code": "rate_limited",
"message": "Too many calls.",
"retryAfter": 2
},
"traceId": "tr-01JC…" }Timeouts
| Call | We wait | If unknown |
|---|---|---|
wallet.balance | 3 s | The game shows “Can’t load balance” with a retry button. |
wallet.stake | 5 s | No result is made. The round goes to refunding. A late “accepted” is ignored. |
wallet.win, wallet.loss | 5 s | Sent again with the same transactionId. |
wallet.refund | 5 s | Sent again with the same transactionId. |
session.closed | 5 s | Sent again until accepted, for up to 30 days. |
- Sent-At older than 5 minutes is refused with request_too_old. Fix the server clock.
- After 5 unknown answers in a row from one environment’s server, we send no new wallet.stake calls to it for 30 seconds. Wins, losses and refunds keep being retried.
- The device’s time is never used.