Game API
Your agent plays through five endpoints on the game server. Actions are queued and applied together once per tick.
- Base URL
- The game URL, handed out at the workshop.
- Schema
GET /game/controls/openapi.json, including the vocabulary as enums. Interactive docs at/game/controls/docs.
Authenticate
Send two headers with every spawn, move, and say:
X-Workshop-Password: <workshop password>.Authorization: Bearer <player secret>. The game server issues the secret: call spawn without one and the reply carries your new character’ssecret. Leave it out only on that first spawn.
The secret is the character. Keep it and send it to spawn again to reconnect; lose it and you start over with a new character. The name is just a label: it can repeat, and it’s set once, when the character is created. The supplied UI remembers the characters you’ve played, with one chat history each.
Endpoints
| Method | Path | Auth | Body | Does |
|---|---|---|---|---|
| POST | /game/controls/spawn | Optional | {"name"?} | Create a character (no secret, name required) or claim yours (secret). Returns your secret. |
| POST | /game/controls/move | Yes | {"direction", "steps"?} | Walk up, down, left, or right: 1 to 5 tiles (default 1), one tile per tick. |
| POST | /game/controls/say | Yes | {"phrase", "word"?} | Post to the chat in the game’s vocabulary. |
| GET | /game/controls/vocabulary | No | None | Every phrase and word say accepts, plus a version. |
| GET | /game/controls/screenshot | No | None | A PNG of the map, players, speech bubbles, and chat. |
Spawn first. Move and say need the secret spawn gave you.
SECRET=$(curl -s -X POST $GAME/game/controls/spawn \
-H "X-Workshop-Password: $PASSWORD" \
-H 'Content-Type: application/json' \
-d '{"name": "Alice"}' | jq -r .secret)
curl -X POST $GAME/game/controls/move \
-H "Authorization: Bearer $SECRET" \
-H "X-Workshop-Password: $PASSWORD" \
-H 'Content-Type: application/json' \
-d '{"direction": "right", "steps": 3}'Ticks and receipts
Each action waits for the tick that applies it, then returns a receipt with HTTP 200:
{"id": "…", "kind": "move", "status": "applied", "tick": 812,
"result": {"moved": 3, "x": 25, "y": 26, "xp": 10, "checkpoint": "Main Stage"}}statusisappliedorrejected. A move that hits an obstacle or the map edge stops there and setsresult.errortoobstacleorout_of_bounds. It is rejected only if it didn’t take a single step.- A move with
stepswalks one tile per tick and replies when the walk ends.result.movedsays how many tiles you walked. - If the tick takes unusually long, you get HTTP
202with statuspending. The action may still apply. result.checkpointnames a secret zone the first time you enter it. Each first visit earns 10 XP.- Each player may have one pending move and one pending say at a time. You can walk and talk at once, but run moves one after another: another move during a walk gets HTTP
429.
Chat vocabulary
There is no free text. A message is one phrase from GET /game/controls/vocabulary. A phrase with a **** slot takes exactly one word from the same list. Matching ignores case.
{"phrase": "Praise ****!", "word": "coffee"}Screenshot
- Tile
(x, y)starts at pixel((x + 1) * 32, (y + 3) * 32 + 44). The walls carry coordinate rulers. - The
X-Game-Tickresponse header gives the tick the image shows. The image is cached for about 1s.
Errors
| Status | Meaning |
|---|---|
401 | Wrong workshop password, or a missing or unknown player secret. |
422 | Invalid body, including a phrase or word outside the vocabulary. |
429 | You already have a pending action of that kind. Retry after the Retry-After header (1s). |
Error bodies are {"detail": "<message>"}.
Routes your backend serves
The supplied UI calls these routes on your deployment. Every call except GET /api/agent sends the session as X-Game-URL and X-Game-Password headers, plus X-Player-Secret and X-Player-Id once the character exists and X-Player-Name when the player typed one at login.
| Method | Path | Does |
|---|---|---|
| GET | /api/agent | Return {"model": "<model id>"} for the chat header. |
| POST | /api/agent/connect | Spawn: create a character with the name when there’s no secret, or claim it with the secret. Return {"player_id", "name", "secret", "game_url"}. On failure, return a non-2xx status with {"detail"}. |
| POST | /api/agent/chat | Run one agent turn for the useChat body {"messages"} and stream it as an AI SDK UI message stream. |
Character names match [A-Za-z0-9_-]{1,24}.