> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kinematic.run/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These are the customer docs for kinematic, a name written in lowercase except at the start of a sentence. Kinematic provides provably fair casino games, served to a casino's site by a game server: a Cloudflare Worker that the kinematic dashboard deploys into the casino's own Cloudflare account.
> Never put SESSION_SECRET or WALLET_SECRET in code, prompts, logs, commits or chat. Read them from environment variables and ask the person you work with to set them. The dashboard's Integration page shows each secret once.
> Values such as lucky-star, luckystar.example, og-rgs-lucky-star.luckystar.workers.dev, games.luckystar.example and player_48213 are examples. Use the casino's own casino ID, game server origin, element tag, session issuer and currencies: the Instructions for your coding agent on the dashboard's Integration page hold them, or ask the person you work with.
> To debug, start at https://docs.kinematic.run/troubleshooting and https://docs.kinematic.run/reference/errors. The wallet contract is at https://docs.kinematic.run/api/wallet.openapi.yaml.
> Sandbox bets are recorded on Solana devnet. Live game servers record nothing on Solana until the audited mainnet program is deployed.

# Errors

> Look up any error code from your game server, the element, your wallet's calls or the dashboard, with what it means and what to do.

Error codes are lowercase words joined by underscores, such as `bet_limit`. Branch on the code, never on the HTTP status: most codes share `409`.

## How errors arrive

| Where | Shape |
| - | - |
| Game server, HTTP | `{"error": "<code>"}` with status `401` for `unauthorized`, `404` for `not_found`, `410` for `archived`, `503` for `control_unavailable` and `409` for every other code. |
| Game server, WebSocket | `{"type": "error", "code": "<code>", "requestId": "<command ID>"}`. The connection stays open. |
| Your page | The `og-error` event on the element, and the `onError` callback of `connect()`, carry an `Error` whose `message` is the code. See [Element](https://docs.kinematic.run/reference/element.md). |
| Your wallet's calls | Codes the game server records when your wallet's answer is not a valid receipt. Players never see them; the **Live** page counts them under **Errors in the last 24 hours**. |
| Dashboard | The **Live** page lists wallet and refusal codes with a sentence beside each code. The **Game server** page lists each game server's [newest errors](https://docs.kinematic.run/dashboard/game-server.md#newest-errors) by code on its card, and shows sentences for deploys, for the settings a game server refuses and for the Solana record; the codes below are what those sentences stand for. **Home** does not list codes. |

The element shows no error text on the game itself. Show the codes your players need to understand, such as `insufficient_funds` or `bet_limit`, in your own page.

A few answers are plain text rather than JSON:

| Status | Body | Cause |
| - | - | - |
| `403` | `Origin denied` | The page's origin is not in **Allowed origins**. The answer carries no CORS headers, so the browser reports a CORS or network failure. |
| `400` | `WebSocket origin/protocol required` | A WebSocket opened without an `Origin` header or without the `og.v1` protocol. Browsers send both through the element. |
| `404` | `Not found` | A signed-in request to a path the game server does not have. |
| `426` | `Upgrade required` | A request to `/v1/stream` that is not a WebSocket upgrade. |

## Sessions and connection

| Code | Meaning | What to do |
| - | - | - |
| `unauthorized` | The session token failed a check: format, signature, `sub`, `iss`, `aud`, `tenant`, `currency`, `exp` or `cnf`. The game server never says which. Also answered when no token was sent. | Decode the token and compare it with [Session token](https://docs.kinematic.run/reference/session-token.md). |
| `request_failed` | The game server could not read the request: a token that is not a JWT, or a body that is not JSON. | Send the token your endpoint signed, unchanged. |
| `session_expired` | The token expired while the connection was open. The connection closes with code `4001`. | Make sure your session endpoint keeps answering; the element renews before expiry. |
| `control_unavailable` | The game server has no settings from the dashboard yet. | Wait a minute after the first deploy, or redeploy. |
| `currency_required` | The catalog was asked for without `?currency=` while your casino serves several currencies. | Sign tokens with `currency`; the element then asks for the right catalog. |
| `not_found` | The catalog was asked for a currency your casino does not serve, or a bet or round does not exist. | Check the currency code. |
| `element_unavailable` | The game server could not serve an element file. | Redeploy the game server. |
| `message_limit` | A message was too large: over 32 KiB for a command, 4 KiB for a wallet event. | Send smaller messages. |

## Bets refused

A refused bet moves no money, and the element frees its stake on screen.

| Code | Meaning | What to do |
| - | - | - |
| `bet_limit` | The stake is below the minimum or above the maximum, or the bet's largest possible payout is above the maximum payout. | See [Games, currencies and limits](https://docs.kinematic.run/dashboard/games-and-limits.md). |
| `insufficient_funds` | Your wallet declined the debit. | Show the player their balance. |
| `bets_disabled` | New bets are stopped on the **Game server** page. Blackjack's extra stakes are refused too. | Select **Accept new bets again** on the **Game server** page when you are ready. |
| `game_disabled` | The game is turned off on **Games**. | Turn it on, or remove it from the page. |
| `currency_disabled` | The token's currency is no longer one of your currencies. | Sign tokens only for your current currencies. |
| `round_active` | The player already has an open round of Mines, Crash, HiLo, Blackjack or Pump played by hand. | Finish that round first. |
| `backpressure` | The player has as many open bets as **Queued bets per player** in [Settings](https://docs.kinematic.run/dashboard/settings.md#players-and-sessions) allows, or 2,000 bets wait for their debit. | Check that your wallet answers debits. |
| `settlement_backpressure` | Your wallet is falling behind: the oldest waiting bet has waited too long, or the payouts owed to the player passed the maximum unsettled. | Check your wallet's speed and errors on **Live**. |
| `storage_backpressure` | The player's storage on your game server is full. | Write to [hello@kinematic.run](mailto:hello@kinematic.run). |
| `authorization_required` | The bet carries no valid signature from the player's device key. | Use the element; it signs every bet. |
| `authorization_binding` | The bet was signed under terms that changed: a VRF key rotation, your return to player, a currency's limits or decimal places. | None: the player's next bet is signed under the new terms. |
| `player_key_changed` | The bet is signed by a device key the player never enrolled, and the token does not attest it. | Put the element's `publicKey` in the token's `cnf`. See [Device keys](https://docs.kinematic.run/integrate/session-tokens.md#device-keys). |
| `currency_unavailable` | The game server has no signed terms for the currency. | Check the currency on **Games**. |
| `invalid_id` | A bet ID is not 16 to 80 letters, digits, `_` or `-`. | Use the element. |
| `id_conflict` | A bet ID was sent again with a different bet. | Use the element. |
| `invalid_request` | The bet could not be read. | Use the element. |
| `stake`, `payout_overflow` and the games' own codes | The bet's options are not valid for the game. | See [Games](https://docs.kinematic.run/reference/games.md). |

## Rounds, reveals and proofs

| Code | Meaning |
| - | - |
| `reveal_early` | The player's seed arrived before the debit applied. The element sends it again. |
| `seed_mismatch` | The revealed seed does not match the commitment the bet carried. The bet is refunded after 24 hours as abandoned. |
| `action_pending` | A Blackjack extra stake still waits for its debit. |
| `stale_round` | A step was chosen on an earlier state of the round. The element catches up on its own. |
| `round_finished` | A step arrived after the round ended. |
| `archived` | You archived this round. Its effect is already in the balance. |
| `proof_pending` | The bet's proof is still being signed. The Provably fair dialog shows "This bet's proof is still being prepared." |

Three outcomes give the stake back through a refund credit: `engine_failure` (the engine could not evaluate the revealed round), `key_retired` (the debit applied only after a VRF key rotation) and an `abandoned` bet (accepted, but the player's seed never arrived within 24 hours).

## In the browser

The element and its client raise these before anything reaches your game server:

| Code | Meaning |
| - | - |
| `amount`, `amount_precision` | The stake typed is not a valid amount for the currency's decimal places. |
| `insufficient_projected_balance` | The stake is above the balance the player has left once open bets settle. |
| `disconnected` | The player acted while the connection was down. The element reconnects on its own. |
| `auto_cashout_required`, `auto_tiles_required` | Auto play started without a Crash cash out or Mines tiles. |
| `token_timeout` | Your session endpoint took longer than 10 seconds. |
| `token_invalid` | Your session endpoint answered something that is not a token. |
| `catalog_unavailable` | The loader could not read the catalog: the origin, the currency or the game server's settings. |
| `no_games` | Your casino offers no game the loader can show. |

## Your wallet's calls

The game server retries every one of these with the same ID and body. The bets they cover stay open, and the player's snapshots show settlement as delayed. See [How the game server reads your answer](https://docs.kinematic.run/reference/wallet-api.md#how-the-game-server-reads-your-answer).

| Code | Meaning | What to do |
| - | - | - |
| `wallet_url` | The wallet URL is not a valid https URL. | Fix it in **Settings**. |
| `wallet_timeout` | No answer within 8 seconds. | Answer faster, and keep the call's work small. |
| `wallet_network` | DNS, TLS or connection failure. | Check your wallet's host and certificate. |
| `wallet_unavailable` | Your wallet answered a status other than `200`, a redirect included. | Answer `200` at the URL itself. A `401` usually means a signature check with the wrong secret or a clock more than a minute off. |
| `wallet_response` | The answer is not JSON, or is larger than 16 KiB. | Answer a small JSON receipt. |
| `wallet_receipt` | The receipt does not repeat the `id` or `currency`, its `status` is not allowed, or `balance` or `revision` are not integer strings. | See [Receipt](https://docs.kinematic.run/reference/wallet-api.md#receipt). |
| `credit_unverified` | Your wallet's check refused a credit with `409`. The `reason` is logged. | See [Checking credits](https://docs.kinematic.run/integrate/wallet.md#checking-credits-before-they-apply). |
| `wallet_revision_conflict` | Your wallet reported the same revision with two different balances. | Give every balance change its own revision. |
| `secret_not_configured` | The game server's `WALLET_SECRET` is missing or too short. | Redeploy the game server. |

Posting a [wallet event](https://docs.kinematic.run/integrate/wallet.md#balance-changes-outside-the-games) can answer `unauthorized` (signature, timestamp or secret), `wallet_binding` (a player ID that breaks the pattern, or a currency you do not serve) or `message_limit` (a body over 4 KiB).

## Game server settings

After **Check now** on a game server's card, the **Game server** page shows these sentences in a line that starts with `Settings:` or `Reports:`. A game server that refuses the dashboard's settings keeps its last good settings in the meantime.

| Code | The page says |
| - | - |
| `control_mode` | `The settings are for the other mode. A mode switch takes effect when you redeploy the game server.` |
| `control_scale` | `The settings change the decimal scale of a currency the game server already served. A served currency keeps its scale.` |
| `control_config` | `Your game server refused this field of the settings: <field>.` |
| `control_revision` | `Your game server holds a newer version of the settings, or the same version with other contents.` |
| `control_deployment` | `The settings name another deployment.` |
| `control_signature` | `Your game server refused the settings' signature: its CONTROL_KEY is not the dashboard's key. Redeploy it.` |
| `control_key` | `Your game server's CONTROL_KEY is not a valid key. Redeploy it.` |
| `control_url` | `Your game server's CONTROL_URL is not a valid https URL. Redeploy it.` |
| `control_secret` | `Your game server lacks DEPLOYMENT_ID or CONTROL_SECRET. Redeploy it.` |
| `control_unavailable` | `Your game server has no settings yet.` |
| `control_network`, `control_fetch` | `Your game server could not reach the dashboard.` |
| `control_timeout` | `The dashboard did not answer the settings request in time.` |
| `control_http` | `The dashboard answered the settings request with <status>.` |
| `control_response` | `Your game server could not read the dashboard's answer.` |
| `telemetry_timeout`, `telemetry_network` | `Your game server could not post its report to the dashboard.` |
| `telemetry_http` | `The dashboard refused a report with <status>.` |

The game server fetches its settings again about once a minute and tries again with its next report, so the network, timeout and HTTP codes clear on their own once the connection returns.

## Solana record

The **Solana record** section of the **Game server** page shows why recording is paused in words:

| Paused | The page says |
| - | - |
| `funds` | `Its address cannot pay the next transaction's fee. Recording resumes within a minute after the address is funded.` |
| `unregistered_key` | `Bets wait for a new VRF key to be registered on Solana. It is registered automatically within 10 minutes, or you can register it now.` |
| `rpc` | `The Solana RPC failed. Your game server tries again every few seconds.` |
| Any other reason | `Recording paused with the reason <code>.` |

**Check Solana**, **Plan key registration** and **Register key** can fail with:

| Code | The page says |
| - | - |
| `solana_funds` | `Your game server's Solana address cannot pay the rent and fees of the plan. Fund it first.` |
| `solana_key` | `Solana refused a key registration: a key starts before the chain's current key, or the program refused it.` |
| `solana_rpc` | `The Solana RPC failed, or a registration expired unconfirmed. Run it again: keys already registered are skipped.` |
| `solana_config` | `This game server's Solana settings are not valid.` |
| `solana_disabled` | `This game server does not record on Solana.` |

## Deploys

When a deploy fails, the game server's card shows the sentence below. **What Cloudflare said** shows the stored code and Cloudflare's or the dashboard's message, such as `cloudflare_error: …`.

| Code | The card says |
| - | - |
| `no_workers_dev_subdomain` | `This Cloudflare account has no workers.dev subdomain yet. Open Workers & Pages once in its Cloudflare dashboard to create one, then deploy again.` See [Troubleshooting](https://docs.kinematic.run/troubleshooting.md#there-is-no-workers-dev-subdomain). |
| `unauthorized` | `Cloudflare refused the request. Reconnect Cloudflare, include this account when you approve, then deploy again.` |
| `not_connected` | `Cloudflare is not connected. Connect it, then deploy again.` |
| `reconnect` | `Cloudflare refused to renew access. Reconnect Cloudflare, then deploy again.` |
| `renew_failed` | `Cloudflare could not renew access just now. Deploy again in a minute.` |
| `renew_busy` | `Cloudflare access was being renewed. Deploy again.` |
| `oauth_not_configured` | `Connecting Cloudflare is not set up on this dashboard yet. Write to hello@kinematic.run.` |
| `dev_tokens_off` | `This connection uses a development token, which this dashboard does not accept. Connect Cloudflare again.` |
| `script_exists` | `Your account has a Worker with this name that was not deployed from here. Remove this game server and create one with another name.` |
| `migration_refused` | `This release renames or deletes Durable Object classes your game server holds. Write to hello@kinematic.run before you update.` |
| `migration_mismatch` | `Your Worker changed in Cloudflare while this deploy read it. Deploy again.` |
| `migration_unknown` | `Your Worker carries a migration this release does not know. Write to hello@kinematic.run.` |
| `missing_secret` | `A secret your game server needs is missing. Deploy again, and write to hello@kinematic.run if it fails again.` |
| `rate_limited` | `Cloudflare asked to slow down. Deploy again in a few minutes.` |
| `network_error` | `Cloudflare could not be reached. Deploy again.` |
| `release_unsupported` | `This release cannot run from the dashboard. Write to hello@kinematic.run.` |
| `release_missing` | `The release is not available. Write to hello@kinematic.run.` |
| `release_invalid` | `The release is damaged. Write to hello@kinematic.run.` |
| `file_mismatch` | `A file of this release does not match its checksum, so nothing was sent. Write to hello@kinematic.run.` |
| `invalid_target` | `This game server's own hostname lacks its zone. Remove it and create it again.` |
| `config_not_published` | `Your game server switched modes, but its settings could not be signed for the new mode. Save your settings again.` |
| `cloudflare_error` | `Cloudflare refused the deploy. Deploy again, and write to hello@kinematic.run if it fails again.` |
| Any other code | `The deploy failed. Deploy again, and write to hello@kinematic.run if it fails again.` |

A failure while Cloudflare attaches your own hostname (`workers/domains` in Cloudflare's message) reads differently: Cloudflare's code `100117` means a DNS record for the hostname already exists, `100116` means the hostname is attached to another Worker in your account, and any other code means the domain's zone or DNS does not meet the conditions. [The deploy fails on your own hostname](https://docs.kinematic.run/troubleshooting.md#the-deploy-fails-on-your-own-hostname) quotes each sentence with its fix.

## WebSocket close codes

| Code | Reason | Meaning |
| - | - | - |
| `4001` | `Session expired` | The token expired. The element asks for a new one and reconnects. |
| `4008` | `Too many connections` | The player has more than 8 connections in one currency; the oldest close. |
| `1011` | `Reconnect` | The game server asks the element to reconnect. |
| `4000`, `4002` | `Heartbeat timeout`, `Invalid state` | Closed by the element, which then reconnects. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.