Game Kit API
A Game Kit is one shared pixel style per project. Every character, enemy, NPC, spell, item, tile and prop in the project is made against the same style, so they look like one game.
These routes are for the signed-in Crateyard web app. For agents, use the MCP tools in ChatGPT app and MCP.
Authentication
- Send the session cookie from a signed-in browser session.
GETneeds no CSRF token.PUTandPOSTneed the CSRF token: sendX-CSRF-Tokenmatching thecrateyard_csrfcookie.- Any project owner or member can read and write the kit. Users without access to the project get
404, so the API does not reveal that a project exists.
Style fields
The style is stored on the project at settings.pixelKit.
spriteSize(integer), one of16,24,32,48,64,96,128. Default32. Other values fall back to32.outline(boolean), whether sprites get a 1px dark outline. Defaulttrue.styleNotes(string), plain-language style rules added to every prompt. Trimmed to 400 characters.palette(array ornull), up to 64 colours as[r, g, b]with each channel 0 to 255.nullmeans no palette.paletteSourceFileId(integer ornull), the sprite the palette came from. Set bypalette-from-sprite. Cleared when you save a palette by hand. Unowned file ids are dropped silently.referenceFileIds(array of integers), up to 4 sprite file ids from this project used as style references.
The REST routes below use these camelCase names. The MCP tools for the kit (get_game_kit, set_game_kit_style, and the others listed in ChatGPT app) take snake_case arguments instead, such as sprite_size, style_notes, and reference_file_ids, and return snake_case keys in structured. Unknown MCP arguments are rejected.
Get the kit
GET /api/pixel-kit/:projectIdResponse 200:
{
"ok": true,
"kit": {
"projectId": 12,
"style": {
"palette": [[24, 20, 37], [255, 255, 255]],
"paletteSourceFileId": null,
"spriteSize": 32,
"outline": true,
"styleNotes": "Chunky shapes, warm light from the top left.",
"referenceFileIds": [401]
},
"assets": [
{
"id": 88,
"name": "Hero",
"slug": "hero",
"role": "character",
"primarySprite": {
"fileId": 412,
"url": "/asset-creator/file/412",
"gridW": 32,
"gridH": 32,
"offPaletteColors": 0
},
"animations": [
{
"id": 9,
"presetId": "humanoid-walk",
"presetName": "Walk",
"slot": "walk",
"direction": "S",
"frameCount": 8,
"columns": 8,
"fps": 8,
"sheetFileId": 430,
"sheetUrl": "/asset-creator/file/430",
"frames": [{ "index": 0, "fileId": 431, "url": "/asset-creator/file/431" }]
}
],
"consistency": { "sizeMatch": true, "offPaletteColors": 0, "issues": [] }
}
],
"counts": {
"byRole": { "character": 1, "enemy": 0, "npc": 0, "spell": 0, "item": 0, "tile": 0, "prop": 0 },
"sprites": 1,
"animations": 1
}
}
}Notes:
styleisnulluntil a style is saved.assetslists the project's pixel art assets. Each asset'sroleis one ofcharacter,enemy,npc,spell,item,tile,prop, ornull.primarySpriteis the chosen sprite, or the newest sprite if none is chosen. It isnullwhen the asset has no sprite.animationslists finished animations only, newest first.offPaletteColorson a sprite isnulluntil the sprite is checked against the kit. Saving the style or an asset's primary sprite re-checks sprites, so older sprites get a value then.consistency.issuesholds short messages such asNo sprite yet.,Sprite size is 24px; kit uses 32px.or3 colours are off the kit palette.urlvalues are relative to the site. Sign in to open them in a browser.
Save the style
PUT /api/pixel-kit/:projectId/style
Content-Type: application/json
X-CSRF-Token: <token>The body is the whole style. This call replaces the saved style, so send every field you want to keep. Omitted fields use their defaults.
Saving the style re-checks the primary sprite of each pixel asset in the project against the new palette and size (stored as kit_check in sprite metadata), so consistency is current on the next GET. At most 100 sprites are re-checked per save.
{
"spriteSize": 32,
"outline": false,
"styleNotes": "Soft shading, no black outlines.",
"palette": [[24, 20, 37], [255, 255, 255]],
"referenceFileIds": [401, 402]
}Response 200:
{
"ok": true,
"style": {
"palette": [[24, 20, 37], [255, 255, 255]],
"paletteSourceFileId": null,
"spriteSize": 32,
"outline": false,
"styleNotes": "Soft shading, no black outlines.",
"referenceFileIds": [401, 402]
}
}Set the palette from a sprite
POST /api/pixel-kit/:projectId/style/palette-from-sprite
Content-Type: application/json
X-CSRF-Token: <token>Extracts the palette from one sprite on this project and saves it as the kit palette.
{ "fileId": 412, "maxColors": 16 }fileId(integer, required), a sprite file on this project.maxColors(integer, optional), 2 to 64. Default 16.
Response 200 returns { "ok": true, "style": { ... } }, the saved style, with paletteSourceFileId set to fileId.
Create a kit asset
POST /api/pixel-kit/:projectId/assets
Content-Type: application/json
X-CSRF-Token: <token>{ "name": "Slime", "role": "enemy" }role(string, required), one ofcharacter,enemy,npc,spell,item,tile,prop.name(string, optional), up to 80 characters. Defaults to the role label, for exampleEnemy.
Response 201:
{
"ok": true,
"assetId": 91,
"studioUrl": "/projects/alex/my-game/asset/slime"
}Open studioUrl in the browser to make sprites in Pixel Studio.
Set an asset's role or sprite
PUT /api/pixel-kit/asset/:assetId
Content-Type: application/json
X-CSRF-Token: <token>Send role, primarySpriteFileId, or both. Send primarySpriteFileId: null to clear the chosen sprite and fall back to the newest one.
Setting a primary sprite re-checks that sprite against the kit, so offPaletteColors is current on the next GET.
{ "role": "enemy", "primarySpriteFileId": 455 }role(string, optional), one of the role values above.primarySpriteFileId(integer ornull, optional). The sprite must be a saved sprite on this asset.
Response 200:
{ "ok": true, "role": "enemy", "primarySpriteFileId": 455 }Kit-aware sprite and image generation
The Pixel Studio generation routes read the project's kit. They are used by the studio and are signed-in routes.
POST /api/pixel-art/generate-sprite:
gridSizedefaults to the kitspriteSizewhen the project has a kit. Otherwise it defaults to 128. Range 8 to 256.useKitPalette(boolean, defaulttruewhen the kit has a palette). Snaps every pixel to the kit palette.snapGrid(boolean, defaultfalse). Snaps a blurry AI image to its own pixel grid before the pipeline runs.- Kit outline is applied: when
outlineisfalse, the sprite has no outline. - The response adds
snapped(boolean),pixelSize(integer ornull), andoffPaletteColors(integer ornull). - The saved sprite stores
kit_checkin its metadata when a kit applies.
POST /api/pixel-art/generate:
role(string, optional), one of the role values. Sets the image framing: character, enemy, and NPC use a character sprite, item uses an item icon, prop uses an environment prop, tile uses a tile, and spell uses a spell effect. An unknown role returns422 Unknown role.- When the project has a kit, its style notes and palette colours are added to the prompt.
- With the Gemini provider, up to 4 reference sprites from the kit are sent as images. The FAL provider receives text only.
- Saved images store
roleandkit_applied: truein metadata when a kit applies.
Errors
Errors from the JSON routes are { "ok": false, "error": "message" }. A CSRF failure returns { "error": "Invalid CSRF token." }.
| Status | When |
|---|---|
| 401 | No signed-in user. Body { "ok": false, "error": "Authentication required." }. |
| 403 | Invalid or missing CSRF token. The check runs before the handler, so the body has no ok field. |
| 404 | The project, asset, or sprite does not exist or you cannot access it. Message: Project not found., Asset not found., or Sprite not found. |
| 422 | Invalid input. Examples: Invalid request body., Unknown role., fileId is required., Sprite has no visible colours., Name is too long (max 80 chars)., Nothing to update., Invalid sprite id., Sprite must belong to this asset. |
Example error:
{ "ok": false, "error": "Unknown role." }Example
curl -X PUT https://crateyard.example/api/pixel-kit/12/style \
-H 'Content-Type: application/json' \
-H 'Cookie: session=example; crateyard_csrf=example-token' \
-H 'X-CSRF-Token: example-token' \
-d '{"spriteSize":32,"outline":true,"styleNotes":"Chunky shapes."}'