Errors
The shared error envelope, its categories and status codes, the access error codes, and request correlation.
Every Tanitar API reports errors in the same JSON envelope, so one error handler covers all of them.
The error envelope
{
"success": false,
"statusCode": 409,
"responseType": "CONFLICT",
"errorCode": "ACCESS_REPLAYED",
"message": "Conflict",
"data": null,
"requestId": "3b9e7a52-1c4d-4f0e-8a6b-5d2c9e1f7a04",
"timestamp": "2026-09-30T11:20:37.412Z"
}| Field | Type | Meaning |
|---|---|---|
success | false | Always false on an error. |
statusCode | number | The HTTP status, repeated in the body. |
responseType | string | The error category. It always agrees with statusCode (see below). |
errorCode | string, optional | A stable, machine-readable code such as ACCESS_SCOPE_DENIED or TOURNAMENT_NOT_FOUND. Present whenever the failure is a typed refusal; absent when it is not. |
message | string | A human-readable description. It can change; do not parse it. |
data | null | Always null on an error. |
requestId | string | The identifier of this request, for correlation (see below). |
timestamp | string | When the error was produced, in ISO 8601 UTC. |
details | array, optional | Field-level validation messages, present on validation failures. |
The Identity API also returns a code field beside errorCode, carrying a stable code on every
error body. Prefer errorCode where both are present.
Categories and status codes
responseType | Status | Typical cause |
|---|---|---|
BAD_REQUEST | 400 | Malformed JSON, failed validation, a missing or invalid Idempotency-Key. |
UNAUTHORIZED | 401 | Missing or invalid credential. |
FORBIDDEN | 403 | The credential is valid but not allowed to do this. |
NOT_FOUND | 404 | The route or the resource does not exist. |
CONFLICT | 409 | The request conflicts with current state: a duplicate, a stale revision, an idempotency conflict. |
UNPROCESSABLE_ENTITY | 422 | The request is well formed but breaks a domain rule, for example an unsupported value. |
TOO_MANY_REQUESTS | 429 | A rate limit. See rate limits. |
INTERNAL_SERVER_ERROR | 500, 502 | An unexpected failure (500), or an upstream provider failure (502). |
SERVICE_UNAVAILABLE | 503 | A dependency Tanitar needs to decide the request is unavailable. Retry later. |
A few behaviours to rely on:
- Validation failures answer
400withmessage"Validation failed"and the individual field messages indetails. - Unexpected failures answer
500with the fixed message"Internal Server Error". Internal detail never appears in a response; quote therequestIdto Tanitar support instead. - Branch on
errorCode, and fall back toresponseTypewhen there is noerrorCode. Never branch onmessage.
Successful responses are not wrapped the same way on every API: some return
{ "success": true, "data": …, "responseType": "SUCCESS" }, others return the resource directly.
The guides show the success body of each route.
Access error codes
These codes come from the checks every API applies before a request reaches its domain logic: authentication, scopes, rate limits, ownership and idempotency. They mean the same thing on every API.
errorCode | Status | Meaning | What to do |
|---|---|---|---|
ACCESS_CREDENTIAL_INVALID | 401 | The credential is missing, malformed, unknown, expired or revoked, or is the wrong kind for this route. | Check the key and the header. Do not retry unchanged. |
ACCESS_ACCOUNT_DISABLED | 403 | Your account is not active (for example, suspended). | Contact Tanitar. |
ACCESS_SCOPE_DENIED | 403 | The key does not carry the scope this route requires. | Use a key with the scope. See scopes. |
ACCESS_ENTITLEMENT_DENIED | 403 | The key carries the scope, but your plan no longer includes it. | Change plan or use a different route. |
ACCESS_RESOURCE_FORBIDDEN | 403 | The resource does not belong to your account or organization. Routes that check ownership also answer this for a resource that does not exist, so they never reveal whether an identifier is in use elsewhere. | Check the identifier. |
ACCESS_IDEMPOTENCY_REQUIRED | 400 | The route requires an Idempotency-Key header and none was sent. | Add the header. |
ACCESS_IDEMPOTENCY_INVALID | 400 | The Idempotency-Key is not 1–256 characters from A–Z a–z 0–9 . _ : -. | Fix the key. |
ACCESS_IDEMPOTENCY_IN_PROGRESS | 409 | An earlier request with the same key is still running. | Wait, then retry with the same key. |
ACCESS_REPLAYED | 409 | The key was already used for a different request. | Use a new key for the new request. |
ACCESS_RATE_LIMITED | 429 | The key exceeded its plan's limit for this rate class. | Back off exponentially and retry. |
ACCESS_STATE_UNAVAILABLE | 503 | Tanitar could not reach state it needs to decide the request, and refused rather than guess. | Back off and retry; for a mutation, reuse the same Idempotency-Key. |
Access refusals always carry the category's generic message ("Unauthorized", "Forbidden",
and so on). They never reveal key, plan or infrastructure detail.
Domain errors use codes named after their area, such as TOURNAMENT_NOT_FOUND or
LEADERBOARD_PERIOD_UNSUPPORTED, and are listed in the guide for each capability.
Tenant storage error codes
Your data lives in storage Tanitar resolves per account. When that resolution fails, the API refuses the request instead of reading or writing anywhere else, so none of these codes means your request partly happened. See multi-tenancy.
errorCode | Status | Meaning | What to do |
|---|---|---|---|
TENANT_DATABASE_UNAVAILABLE | 503 | Your database could not be reached. | Back off and retry; for a mutation, reuse the same Idempotency-Key. |
TENANT_CONNECTION_UNHEALTHY | 503 | The connection to your database is not healthy. | Back off and retry, as above. |
TENANT_CONTEXT_UNAVAILABLE | 503 | The routing state that says where your data lives could not be read. | Back off and retry, as above. |
TENANT_POOL_CAPACITY | 503 | Connection capacity for your account is exhausted. | Retry after a longer pause. Contact Tanitar if it persists. |
TENANT_SCHEMA_INCOMPATIBLE | 503 | Your database is mid-upgrade and not ready for this request. | Retry later. Contact Tanitar if it persists. |
TENANT_ACCESS_DISABLED | 403 | Your account is not in a state that permits data access. | Contact Tanitar. Do not retry unchanged. |
TENANT_ROUTING_MISMATCH | 403 | The request does not match your account's routing state. | Contact Tanitar. Do not retry unchanged. |
Correlating requests
Every success and error response carries an x-request-id header. Every error body also carries
the same value in requestId.
You can supply your own: send an X-Request-Id header of 1 to 128 characters from
A–Z a–z 0–9 . _ : -, and Tanitar uses it for the request and echoes it back. If you send none, or
one that does not match, Tanitar generates a UUID.
curl "$REWARD_API_URL/v1/currencies" \
-H "Authorization: Bearer $TANITAR_API_KEY" \
-H "X-Request-Id: checkout-7f3a-attempt-1" -iLog the request id with every call you make. When you contact Tanitar about a failed request, the request id is the fastest way to find it.