API Docs

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.
  • GET needs no CSRF token.
  • PUT and POST need the CSRF token: send X-CSRF-Token matching the crateyard_csrf cookie.
  • 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 of 16, 24, 32, 48, 64, 96, 128. Default 32. Other values fall back to 32.
  • outline (boolean), whether sprites get a 1px dark outline. Default true.
  • styleNotes (string), plain-language style rules added to every prompt. Trimmed to 400 characters.
  • palette (array or null), up to 64 colours as [r, g, b] with each channel 0 to 255. null means no palette.
  • paletteSourceFileId (integer or null), the sprite the palette came from. Set by palette-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/:projectId

Response 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:

  • style is null until a style is saved.
  • assets lists the project's pixel art assets. Each asset's role is one of character, enemy, npc, spell, item, tile, prop, or null.
  • primarySprite is the chosen sprite, or the newest sprite if none is chosen. It is null when the asset has no sprite.
  • animations lists finished animations only, newest first.
  • offPaletteColors on a sprite is null until 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.issues holds short messages such as No sprite yet., Sprite size is 24px; kit uses 32px. or 3 colours are off the kit palette.
  • url values 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 of character, enemy, npc, spell, item, tile, prop.
  • name (string, optional), up to 80 characters. Defaults to the role label, for example Enemy.

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 or null, 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:

  • gridSize defaults to the kit spriteSize when the project has a kit. Otherwise it defaults to 128. Range 8 to 256.
  • useKitPalette (boolean, default true when the kit has a palette). Snaps every pixel to the kit palette.
  • snapGrid (boolean, default false). Snaps a blurry AI image to its own pixel grid before the pipeline runs.
  • Kit outline is applied: when outline is false, the sprite has no outline.
  • The response adds snapped (boolean), pixelSize (integer or null), and offPaletteColors (integer or null).
  • The saved sprite stores kit_check in 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 returns 422 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 role and kit_applied: true in 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."}'