Errors
Every error is JSON with two fields and sometimes a third:
{ "error": "invalid", "message": "nickname: 3 to 20 characters.", "details": { "field": "nickname" }}error is one of nine codes. message is written for the developer who reads the response, in English, and
says what failed. details is present when the API has something structured to add, such as the field at
fault.
curl -s -o body.json -w '%{http_code}\n' \ -X PATCH 'https://api.filudi.com/v1/sdk/players/me' \ -H "authorization: Bearer $FILUDI_PLAYER_TOKEN" \ -H 'content-type: application/json' \ -d '{ "nickname": "a" }'cat body.jsonconst response = await fetch('https://api.filudi.com/v1/sdk/players/me', { method: 'PATCH', headers: { authorization: `Bearer ${playerToken}`, 'content-type': 'application/json' }, body: JSON.stringify({ nickname: 'a' }),});if (!response.ok) { const { error, message, details } = await response.json(); if (error === 'unauthorized') forgetToken(); if (error === 'rate_limited') waitSeconds(Number(response.headers.get('retry-after'))); console.warn(error, message, details);}import requests
response = requests.patch( 'https://api.filudi.com/v1/sdk/players/me', headers={'authorization': f'Bearer {player_token}'}, json={'nickname': 'a'},)if not response.ok: body = response.json() print(response.status_code, body['error'], body['message']) if body['error'] == 'rate_limited': retry_after = int(response.headers['retry-after'])| Code | Status | When |
|---|---|---|
invalid | 400 | The body or a parameter breaks the schema. message names the field |
unauthorized | 401 | No credential, or one the API refuses. For a player token: forget it and create a new guest |
forbidden | 403 | The credential is valid and does not open this action: another game’s token, a role, a scope, an origin |
not_found | 404 | Unknown resource, or one your credential may not see. An unknown site slug and a game outside a site’s catalog answer this too |
conflict | 409 | The state refuses the change: a slug taken, a version already live, a quota reached |
too_large | 413 | The body is over the route’s limit (64 kB for a save or an event batch) |
rate_limited | 429 | Too many requests. Retry-After says how many seconds to wait; see Rate limits |
unavailable | 503 | A dependency is down, usually Redis. Retry in a few seconds; Retry-After is set when the API knows |
internal | 500 | An unexpected failure. The API logged it |
The write routes of the admin API answer 400 invalid, 403 forbidden, 404 not_found and 409 conflict
with this shape even where the reference lists only the success code.
Two routes with another body
Section titled “Two routes with another body”PUT /v1/sdk/saves/{gameId} answers 409 with the server’s copy of the save, not an error: { gameId, version, etag, bytes, updatedAt, data }, with version 0 and data null when nothing is stored yet. Take it, merge if
the game can, and write again with If-Match set to that version.
POST /v1/sdk/leaderboards/{gameId}/{board}/scores answers 400 with { error: "invalid", reason, message }.
reason is one of bounds, signature, session, board, nonce, clock, meta, so a game logs why a
score was refused.
Details worth reading
Section titled “Details worth reading”details.field: "current"on a 401 ofPUT /v1/account/passwordandPUT /v1/console/password: the session or token is fine, the current password is wrong. Keep the token.- The issue code
game_hoston a 400 ofPOST /v1/admin/sitesand its PATCH: a domain is on or under the play domain, where no site may run. - A 403 of
PUT /v1/admin/sites/{siteId}/catalog/{gameId}carries inmessagewhy the site may not list the game: pending, no live version, orfirst_partyon a site that is not first-party.