Errors & rate limits
Every error response includes a stable code and a human-readable error. Use the code to switch behaviour.
Status codes
| HTTP | Meaning |
|---|---|
200 | Success (idempotent replay or read) |
201 | Success (resource created / first-time effect) |
400 | Validation error — body or query param invalid |
401 | Missing / invalid / revoked API key |
402 | Insufficient balance (e.g. tried to enter a raffle with too few WINZ) |
403 | Scope-denied or tenant-suspended |
404 | Resource not found |
409 | State conflict (e.g. lootbox paused, tournament already closed) |
429 | Rate limit exceeded |
500 | Server error — safe to retry with same idempotency key |
503 | Downstream temporarily unavailable — safe to retry |
Error codes
| Code | Meaning |
|---|---|
AUTH_REQUIRED | No bearer token on the request |
AUTH_INVALID | Bearer present but unknown / revoked / wrong format |
RATE_LIMIT | Per-tenant bucket emptied |
NO_PLAYER | Player resolution failed (unknown username) |
NO_LOOTBOX / NO_RAFFLE / NO_JACKPOT / etc. | Engagement record not found |
NOT_ACTIVE | The entity is paused or completed |
INSUFFICIENT_WINZ | Player has too few WINZ for the action (e.g. raffle entry cost) |
NO_TICKETS | Raffle draw attempted with zero entries |
SERVICE_UNAVAILABLE | Downstream service unreachable; retry safe |
Rate limits
Token-bucket per tenant. Default capacity 200 burst + 50/sec sustained. Limits apply to the cumulative /v1/* surface, not per-endpoint.
On a 429, back off and retry. Implementations typically use jittered exponential backoff (200ms → 400ms → 800ms → …, with ±25% jitter).
Enterprise plans negotiate higher caps. If you sustain >50 req/sec, contact us before going live so we can pre-size your bucket.
Idempotency
Every write endpoint accepts idempotency_key in the body. Same key + same endpoint → original result. Different key + same body → new effect. Use a UUID per logical operation (not per HTTP attempt) — that way network retries naturally collapse.