Skip to content

Errors ​

Every response is JSON. Success is {"ok":true,"data":{...}}; every error is:

json
{"ok":false,"error":"<message>"}

Check ok before reading data, and surface error when it's false.

Status codes at a glance ​

StatusMeaningWhat to do
200SuccessRead data
400Bad request — invalid input, rate limit, or validation failureRead the error message; see below
401 / 403Session token is dead (expired, logged out, or account removed)Discard the stored session and fall back to sign-in — see Authentication
404Route or resource not foundFor scoreboard lookups this is normal — the board just isn't configured
428Device-code auth still pendingKeep polling (every 5s)
5xxTransient upstream problemRetry with backoff — submits are safe to retry. Persistent? Check status.cheddatech.com

Common errors ​

Scoreboard '<id>' not found for this game. ​

You submitted a score with a scoreboardId that doesn't exist on this game. Every game gets its standard time-based boards (all-time, weekly, daily) automatically — but custom boards are never created by a submit. Create the board first in the Developer Console (Scoreboards tab), and for targeted submits make sure its Board Type is Targeted. Retrying the same submit without creating the board will fail forever.

This game requires starting a session before submitting. ​

The game has time validation enabled, which makes the play-session token required — a submit without a valid playSessionToken is rejected before any score checks run. Start a session when the run begins (POST /play-sessions/start), pass its token in the submit body, and end the session after submitting (POST /play-sessions/end). The SDKs attach the token for you but don't start sessions on their own — call start_play_session() / StartPlaySession() when the run begins. (The Godot template's game wrapper is the one place the whole lifecycle runs automatically.) If you're seeing this from an SDK integration, the session start didn't happen before the submit. (The startGameSession the message mentions is the backend's internal name — on REST the call is /play-sessions/start.) See Anti-cheat.

Submitting to a fan-out board by ID ​

A scoreboardId on a submit means "this one targeted board only", so sending the ID of a fan-out board (all-time, weekly, daily, or a custom fan-out board) is rejected. To update those boards, omit scoreboardId — a plain submit already fans out to every one of them. See Scores → targeted submits.

rejected by game validation rules ​

The score tripped one of the game's anti-cheat limits (score cap, streak cap, or time validation). The message is deliberately generic — the specific reason is logged to your dashboard's suspicion log, visible only to the game owner, so players can't probe your limits. If you're the developer and this surprises you, check the Security tab's caps and whether your submits carry a valid playSessionToken.

Nickname rejected ​

Nicknames must be 3–16 characters, letters, digits, and underscores only. The nickname endpoints return a 400 with one of:

  • Nickname must be at least 3 characters
  • Nickname must be 16 characters or less
  • Nickname can only contain letters, numbers, and underscores

A rejection is permanent for that value — don't retry the same nickname, ask the player for a different one.

A nickname that's merely taken is not an error: the backend appends a numeric suffix automatically (PlayerName → PlayerName_1), and the response tells you the name that was actually applied.

authorization_pending (HTTP 428) ​

Not an error — the player just hasn't approved the device-code sign-in yet. Keep polling /auth/device/token every 5 seconds until you get 200 or the code expires.

Rate limited ​

One submit per player per board every 2 seconds, enforced server-side — the error tells you to wait. Back-to-back submits to different boards are fine (the throttle is keyed per board). If you hit this in normal play, you're submitting more often than you need to.

Too many active play sessions ​

Play sessions are capped per player (enforced by the backend). End sessions when runs finish (POST /play-sessions/end); when testing, use a fresh playerId rather than accumulating sessions on one.

Too many pending device authorizations. Try again shortly. (HTTP 503) ​

The device-code sign-in flow has a cap on outstanding unapproved codes. Transient — wait a moment and request a new code.

OAuth / client ID errors ​

Games don't configure OAuth — there's nothing to register, and no client ID or bundle ID field in the Developer Console. Player sign-in goes through the device-code flow: request a code, show the player the link URL, and they sign in with Google or Apple on the CheddaBoards link page using CheddaBoards' own credentials. See Authentication.

If you're seeing an error that mentions client IDs or bundle IDs, you're calling a legacy direct-OAuth path that isn't supported for games. Switch to the device-code flow instead.

Unknown endpoint: <method> <path> ​

The route doesn't exist — check the endpoint reference for the exact path. The one people actually hit: there is no standalone achievements read route (GET /players/{id}/achievements doesn't exist). Achievements are only exposed on the profile — GET /players/{playerId}/profile returns them in gameProfile.achievements. SDK versions before 2.2.7 called the nonexistent route from GetAchievements() / get_achievements(); update the SDK or read achievements from the profile.

Invalid path segment (HTTP 400) ​

Game and scoreboard IDs in URL paths must be 1–64 characters of letters, digits, _ or -. Anything else is rejected before reaching the backend. Usually this means a bug in how you build the URL — an unescaped string, an uninitialized buffer, or a stray null byte.

Session expiry ​

Sessions last 30 days and renew on use, so active players stay signed in indefinitely. Any 401/403 on a session-authenticated request means the session is dead: clear the stored token, fall back to your sign-in flow, and don't retry the request with the same token. The official SDKs treat this as an automatic sign-out.

When it's not you ​

First stop: status.cheddatech.com. It checks the API, the on-chain leaderboards, the website and these docs every few minutes from outside CheddaBoards' own infrastructure, and any outage shows there with its history.

Occasionally the chain itself has a transient wobble (a subnet replica upgrade, for example) and you'll see a 5xx for a few seconds. Submits are safe to retry, so a single retry with a short backoff covers it. If the API is unreachable entirely, board reads still work directly from the canister.

See also: REST quick start · Authentication · Anti-cheat · Service status

Can't be taken away. Open source backend and SDKs.