{
  "openapi": "3.0.0",
  "info": {
    "title": "Slot Preview AI API",
    "description": "Generate unique preview tiles for casino slots and download them in every format you need.\n\nTHE HAPPY PATH. `GET /v1/me` (check the key and its environment) → `GET /v1/games` (find the title, or `POST /v1/games` to add it) →\n`GET /v1/styles`, `GET /v1/backgrounds`, `GET /v1/formats` (the allowed values for `art`) → `POST /v1/masters/estimate` (price it) →\n`POST /v1/masters` (generate) → poll `GET /v1/masters/jobs/{id}` → `POST /v1/masters/{id}/export` → poll `GET /v1/exports/jobs/{jobId}` →\n`GET /v1/exports/{exportSetId}` (file URLs). Subscribe with `POST /v1/webhook-endpoints` to skip the polling.\n\nAUTHENTICATION. Send `Authorization: Bearer spai_...` on every request. API KEYS ARE ISSUED AND ROTATED IN THE WEB DASHBOARD ONLY —\nthere is no endpoint for creating, rotating or revoking a key, and there never has been: key management lives under a browser session,\nnot under an API key. Keys carry scopes (`read`, `render`, `generate`, `export`, `batch`); the scope each operation needs is named in\nits description. `spai_test_...` keys never reach a paid provider, `spai_live_...` keys spend real money — `GET /v1/me` tells you which\none you hold.\n\nMONEY. `GET /v1/credits` shows the balance and `GET /v1/credits/ledger` shows where it went. TOPPING UP IS DONE IN THE WEB DASHBOARD\nONLY: there is no endpoint that buys tokens, so `insufficient_tokens` (402) has no programmatic remedy — watch the balance instead of\nreacting to the error. Always send an `Idempotency-Key` on `POST /v1/masters`: without it a retry creates a second job and charges\ntwice. The `X-Tokens-Charged` response header states what a call actually cost.\n\nERRORS. Every operation answers failures with one envelope: `{error: {code, message, request_id, details}}`. This document declares\n401, 402, 403 and 429 explicitly; the full table of error codes, including 404, 409 and 422, is in `docs/errors.md`. Quote `request_id`\nwhen you contact support.",
    "version": "0.3.0",
    "contact": {}
  },
  "servers": [
    {
      "url": "https://api.slotspreviewai.com"
    }
  ],
  "security": [
    {
      "api-key": []
    }
  ],
  "paths": {
    "/v1/games": {
      "get": {
        "operationId": "CatalogController_searchGames",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 2
            }
          },
          {
            "name": "provider",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "theme",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "upcoming",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "string"
                }
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the catalogue: games, the cursor of the next page and a has-more flag.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/gamesListResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "catalog"
        ],
        "summary": "Search the game catalogue",
        "description": "Finds a game to generate art for. Search by title with `q`, narrow by `provider`, `theme` or `upcoming`. Pages are cursor-based: pass `limit` (1..100), then feed `next_cursor` from the response back as `cursor`. If the game is not here yet, create it with POST /v1/games — unreleased titles are the point of this product. Requires the `read` scope. This call never spends tokens."
      },
      "post": {
        "operationId": "CatalogController_createGame",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The game is created, or its duplicate is found. 200 rather than 201 on purpose: repeating the same title and studio returns the same record.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/createGameResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "catalog"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "provider_name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "theme_hint": {
                    "type": "string",
                    "maxLength": 300
                  },
                  "release_date": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "provider_name"
                ],
                "additionalProperties": false
              },
              "example": {
                "name": "Gates of Fortune",
                "provider_name": "Pragmatic Play",
                "theme_hint": "ancient temple, gold, stormy sky",
                "release_date": "2026-09-15"
              }
            }
          }
        },
        "summary": "Add a game to the catalogue",
        "description": "Registers a title that is not in the catalogue yet — the main case for this product, since tiles are wanted before a slot ships. Returns 200, not 201, on purpose: sending the same name and provider again returns the same record instead of creating a duplicate, so this call is safe to retry. Use `GET /v1/providers` for the canonical studio spelling. A different spelling of a studio already in the catalogue (punctuation, spacing, a trailing Gaming or Studios) is matched to that studio; the response carries the canonical spelling. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/games/{id}": {
      "get": {
        "operationId": "CatalogController_getGame",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The game card: brief, finished masters, whether a reference exists. A foreign or unknown id answers 404.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/gameDetailDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "catalog"
        ],
        "summary": "Get one game",
        "description": "Everything the generator knows about a title: `art_brief` (theme, palette, motifs), `has_reference` and `brief_confidence`. Read `has_reference` before you set `use_game_reference` on POST /v1/masters: with no reference on file the flag changes nothing. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/styles": {
      "get": {
        "operationId": "CatalogController_getStyles",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The style catalogue in canonical display order. Hidden styles (is_public=false) are not listed but remain valid as generation input.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/stylesListResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "catalog"
        ],
        "summary": "List art styles",
        "description": "The allowed values for `art.style` on POST /v1/masters and POST /v1/batches. Returned in canonical display order. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/backgrounds": {
      "get": {
        "operationId": "CatalogController_getBackgrounds",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The background catalogue in canonical display order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/backgroundsListResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "catalog"
        ],
        "summary": "List backgrounds",
        "description": "The allowed values for `art.background`, which is required on POST /v1/masters and POST /v1/batches. Returned in canonical display order. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/providers": {
      "get": {
        "operationId": "CatalogController_getProviders",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Game studios that have at least one active public game.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/providersListResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "catalog"
        ],
        "summary": "List game providers",
        "description": "Studios that have at least one active public game. Use these spellings for the `provider` filter on GET /v1/games and for `provider_name` when you add a game or pass a game by name. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/formats": {
      "get": {
        "operationId": "CatalogController_getFormats",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The catalogue of export formats and the pricing rule: sizes within one aspect ratio are free, another aspect ratio is part of the set export.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/formatsListResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "catalog"
        ],
        "summary": "List export formats",
        "description": "What an export set will contain: format codes, pixel sizes and the pricing rule (extra sizes inside one aspect ratio are free, a different aspect ratio is billed as part of the set). This is also the ONLY public source of values for the required `art.composition` field: take them from the `composition` field of these formats — the server derives them from frame geometry and they must not be invented by hand. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/me": {
      "get": {
        "operationId": "MeController_me",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Key check in one call: organisation, plan, balance in whole tokens, the key environment and its scopes. The first call of the onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/meResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "auth"
        ],
        "summary": "Check the API key",
        "description": "The first call of any integration. Tells you which organization the key belongs to, the plan, the token balance, the scopes the key was issued with, and — most importantly — its `environment`: `test` keys never touch a paid provider, `live` keys spend real money. There is no other programmatic way to tell a `spai_test_...` key from a `spai_live_...` one. A key with a daily token cap also gets `daily_token_cap`, `daily_tokens_used` and `daily_cap_resets_at`: the cap counts only charges, per UTC day, and a refund gives the tokens back to the day of the charge. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/masters/{id}/export": {
      "post": {
        "operationId": "ExportsController_createExport",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Client-generated unique string that makes a repeated call safe. Retrying with the same key and the same body returns the original result and charges nothing (X-Tokens-Charged: 0); the same key with a different body is rejected. Without this header a retry creates a second job and charges a second time."
          }
        ],
        "responses": {
          "200": {
            "description": "The set for this master is already built: the repeated call is free (X-Tokens-Charged: 0).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/exportJobDtoSchema"
                }
              }
            }
          },
          "202": {
            "description": "The set build is queued. The charge is stated by X-Tokens-Charged, not by the status code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/exportJobDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "402": {
            "description": "Not enough tokens for this call. Error code `insufficient_tokens`. There is no endpoint that tops up the balance — do it in the web dashboard.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "exports"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "webhook_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "hero_position": {
                    "type": "string",
                    "enum": [
                      "center",
                      "left",
                      "right"
                    ],
                    "default": "center"
                  },
                  "file_format": {
                    "type": "string",
                    "enum": [
                      "webp",
                      "png",
                      "jpeg",
                      "avif"
                    ]
                  },
                  "file_formats": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "webp",
                        "png",
                        "jpeg",
                        "avif"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 4
                  },
                  "formats": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "tile-2x3",
                        "tile-2x3-2x",
                        "tile-3x4",
                        "tile-1x1",
                        "story-9x16",
                        "banner-16x9",
                        "banner-21x9"
                      ]
                    },
                    "minItems": 1
                  },
                  "max_tokens": {
                    "type": "number",
                    "minimum": 0
                  },
                  "overlay_config_id": {
                    "type": "string",
                    "description": "Caption of the set. An `overlay_config_id` from POST /v1/renders applies that caption; \"none\" builds art without a caption. Omitted: a new set gets the default caption (game title and studio, as in the web app), an already built set keeps its caption unchanged. Changing the caption of a built set is a free rebuild; every build is listed by GET /v1/exports/{exportSetId}/overlay-history."
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "hero_position": "center"
              }
            }
          }
        },
        "summary": "Export a master to every format",
        "description": "Builds the deliverable set of image files from a finished master. Two success codes and the difference matters for your wallet: 202 means a new set is being built and tokens were held, 200 means this master was already exported and the repeat is FREE — check `X-Tokens-Charged`, not the status code. On 202, poll `polling_url` (GET /v1/exports/jobs/{jobId}) until `export_set_id` appears, then fetch the files with GET /v1/exports/{exportSetId}. Price it first with GET /v1/masters/{id}/export/estimate. Caption: without `overlay_config_id` a new set gets the default caption (game title and studio) and an already built set is left exactly as it is, so repeating this call never redraws your files. Pass an `overlay_config_id` from POST /v1/renders for your own caption, or `\"none\"` for art without a caption; changing the caption of a built set is a free rebuild. File container of a built set: a set owns the containers it has paid for (`paid_file_formats` of the estimate and of GET /v1/exports/{id}). Switching to a paid container is free; naming a container the set has not paid for in `file_format` costs 1 token (`X-Tokens-Charged: 1`), is charged only if the rebuild in that container is published and is returned otherwise; the estimate gives every price in `container_prices`. A call without `file_format` never buys a container. While a build is running, naming an unpaid container holds 1 token at once for the rebuild that follows. The price of a container of an existing set follows the environment of the set, not of the key: a test key that names an unpaid container of a set built with a live key gets 409 `sandbox_key_on_live_set` and nothing is held. SEND AN `Idempotency-Key`: a repeat with the same key and the same body returns the original response with `X-Tokens-Charged: 0` and changes nothing, even if the set has since failed or been cancelled and its tokens returned; the same key with a different body (including another master) is 409 `idempotency_key_reuse`. Without the key a repeat after a failed or cancelled build charges again. `max_tokens` caps the price: if the live price of this call is higher, the answer is 409 `price_above_limit` and nothing is held. Requires the `export` scope. This call can spend tokens: the `X-Tokens-Charged` response header states what it actually cost."
      }
    },
    "/v1/masters/{id}/export/estimate": {
      "get": {
        "operationId": "ExportsController_estimateExport",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "overlay_config_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Caption of the set. An `overlay_config_id` from POST /v1/renders applies that caption; \"none\" builds art without a caption. Omitted: a new set gets the default caption (game title and studio, as in the web app), an already built set keeps its caption unchanged. Changing the caption of a built set is a free rebuild; every build is listed by GET /v1/exports/{exportSetId}/overlay-history."
            },
            "description": "Caption of the set. An `overlay_config_id` from POST /v1/renders applies that caption; \"none\" builds art without a caption. Omitted: a new set gets the default caption (game title and studio, as in the web app), an already built set keeps its caption unchanged. Changing the caption of a built set is a free rebuild; every build is listed by GET /v1/exports/{exportSetId}/overlay-history."
          },
          {
            "name": "formats",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "tile-2x3",
                  "tile-2x3-2x",
                  "tile-3x4",
                  "tile-1x1",
                  "story-9x16",
                  "banner-16x9",
                  "banner-21x9"
                ]
              },
              "minItems": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "What building the set for THIS master would charge right now. The same quote is used by the build itself: the promise and the charge are one number.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/exportEstimateResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "exports"
        ],
        "summary": "Price an export before running it",
        "description": "What GET-then-POST /v1/masters/{id}/export would charge for THIS master right now. Computed by the same code that the export itself uses, so the quote and the charge are one number. Zero means the set is already built and rebuilding it is free. Pricing holds nothing and creates nothing. Pass the same `overlay_config_id` (or `none`) and `formats` you are going to send to the export; `overlay_config_id` in the response is the caption the set is CURRENTLY built with, not the one you asked about. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/exports/jobs/{jobId}": {
      "get": {
        "operationId": "ExportsController_getExportJob",
        "parameters": [
          {
            "name": "jobId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The state of the set build: readiness per format and the address of the set once it is built.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/exportJobDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "exports"
        ],
        "summary": "Poll an export job",
        "description": "The polling half of POST /v1/masters/{id}/export. `progress[]` shows per-format readiness; `export_set_id` appears once the set is finished — that is your signal to call GET /v1/exports/{exportSetId} for the file URLs. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/exports/{exportSetId}": {
      "get": {
        "operationId": "ExportsController_getBundle",
        "parameters": [
          {
            "name": "exportSetId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The built set: files per format, links to them and to the archive. The archive itself is NOT the response body: the response carries a signed address (zip_url) the client downloads from; downloading is always free.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/exportBundleDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "exports"
        ],
        "summary": "Get a finished export set",
        "description": "The end of the chain: `files[].url` per format, `zip_url` for the whole set, plus `formats_ready`, `formats_expected` and `missing[]` if the set is partial. The URLs are pre-signed and expire, so download rather than store them — re-read this operation for fresh links, which is always free. `overlay_config_id` in the response is the caption the files are built with; `null` means art without a caption. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/exports/{exportSetId}/formats/{format}/retry": {
      "post": {
        "operationId": "ExportsController_retryFormat",
        "parameters": [
          {
            "name": "exportSetId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "The retry is queued (or already running). Free: X-Tokens-Charged: 0. Poll `polling_url`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/exportJobDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "exports"
        ],
        "summary": "Retry one missing format of a set",
        "description": "One free retry for a format that did not assemble in a partial set (`missing[].retry` is `available` in GET /v1/exports/{exportSetId}). The retry builds into the SAME set: no new hold, no charge (`X-Tokens-Charged: 0`). Exactly one retry per format for the whole life of the set: after it is used the answer is 409 `format_retry_used`. A format that is delivered, not ordered, cancelled by you, refused by the model, or a failed set answers 409 `format_retry_not_applicable`; while another build of this set runs the answer is 409 `export_set_busy`. Repeating the call while the retry runs returns the same job. If the retry fails too, the tokens for the lost formats come back by themselves: read `tokens_refunded` in GET /v1/exports/{exportSetId}. Requires the `export` scope. This call never spends tokens."
      }
    },
    "/v1/exports/{exportSetId}/archive": {
      "get": {
        "operationId": "ExportsController_downloadArchive",
        "parameters": [
          {
            "name": "exportSetId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The set archive as a binary stream with Content-Disposition: attachment. Free, like every download.",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "exports"
        ],
        "summary": "Download the set archive from this API",
        "description": "The same ZIP that `zip_url` points at, served by this API instead of the storage host: a binary stream with `Content-Disposition: attachment`. Use it when a cross-origin redirect to storage is inconvenient (browsers, extensions, corporate proxies). Downloading is always free. `zip_url` stays in the response body for clients that prefer to fetch from storage directly. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/exports/{exportSetId}/files/{filename}": {
      "get": {
        "operationId": "ExportsController_downloadFile",
        "parameters": [
          {
            "name": "exportSetId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filename",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One file of the set as a binary stream with Content-Disposition: attachment. The actual Content-Type is the file's own.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "exports"
        ],
        "summary": "Download one file of the set from this API",
        "description": "One file of the set by the `filename` this API gave it — a format frame (`tile-3x4.png`) or an operator size (`tile-3x4-427x575.png`). A binary stream with `Content-Disposition: attachment`, served by this API rather than by the storage host. Free, like every download. The pre-signed `files[].url` and `files[].sizes[].url` stay in the response body. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/exports/{exportSetId}/overlay-history": {
      "get": {
        "operationId": "ExportsController_overlayHistory",
        "parameters": [
          {
            "name": "exportSetId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Caption builds of the set, newest first, one page. Returning to any entry is a free rebuild with its overlay_config_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/overlayHistoryPageSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "exports"
        ],
        "summary": "List the caption builds of an export set",
        "description": "Every build of the set, newest first: which `overlay_config_id` was asked for (`null` = art without a caption), when it was requested and published, and how the attempt ended. To return the set to any earlier caption, call POST /v1/masters/{id}/export with that `overlay_config_id` (or `none` for an entry with `null`): the rebuild is free and the files come back byte for byte. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/webhook-endpoints": {
      "post": {
        "operationId": "WebhooksController_create",
        "parameters": [],
        "responses": {
          "201": {
            "description": "The subscription is created. The signing secret is returned ONCE, here; the url has passed the SSRF guard. 0 tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/webhookEndpointCreatedResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "master.generation.completed",
                        "master.generation.failed",
                        "export.completed",
                        "export.failed",
                        "tokens.low_balance",
                        "batch.completed"
                      ]
                    },
                    "minItems": 1
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 200
                  }
                },
                "required": [
                  "url",
                  "events"
                ],
                "additionalProperties": false
              },
              "example": {
                "url": "https://example.com/spai-hook",
                "events": [
                  "master.generation.completed"
                ],
                "description": "probe from the Postman collection"
              }
            }
          }
        },
        "summary": "Subscribe to webhooks",
        "description": "Registers a URL that receives job events, so you do not have to poll. THE SIGNING SECRET IS RETURNED ONLY ONCE, in this response — store it immediately; it is never shown again and cannot be recovered. Use it to verify the signature on every delivery. The URL is checked against internal addresses before it is accepted. Requires the `read` scope. This call never spends tokens."
      },
      "get": {
        "operationId": "WebhooksController_list",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The organisation's subscriptions without secrets, not even hashed ones.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/webhookEndpointsListResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "webhooks"
        ],
        "summary": "List webhook subscriptions",
        "description": "Your registered endpoints and the events each one is subscribed to. Secrets are never returned here — not even hashed; the secret is shown once, when the subscription is created. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/webhook-endpoints/{id}": {
      "delete": {
        "operationId": "WebhooksController_remove",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The subscription is deleted, IRREVERSIBLY: deliveries to this address stop, the secret cannot be recovered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/webhookEndpointDeletedResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "webhooks"
        ],
        "summary": "Delete a webhook subscription",
        "description": "IRREVERSIBLE: deliveries to this URL stop, and because the signing secret is never shown a second time, the subscription cannot be restored — recreating it issues a NEW secret that you must deploy. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/webhook-endpoints/{id}/test": {
      "post": {
        "operationId": "WebhooksController_ping",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A test delivery, synchronous and without the queue: the result is visible at once. 0 tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/webhookPingResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "webhooks"
        ],
        "summary": "Send a test delivery",
        "description": "Sends one delivery to this endpoint synchronously, bypassing the queue, and returns the result in the response — status code, timing and the truncated response body. Use it to verify your receiver and your signature check before real events arrive. Use it before real events arrive. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/webhook-deliveries": {
      "get": {
        "operationId": "WebhooksController_listDeliveries",
        "parameters": [
          {
            "name": "endpoint_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The delivery log. The subscriber's response body is truncated to 2 KB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/webhookDeliveriesListResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "webhooks"
        ],
        "summary": "List webhook deliveries",
        "description": "The delivery log: which event went where, the response status and the attempt count. Filter by `endpoint_id`. Your receiver's response body is truncated to 2 KB. Use this to find deliveries your endpoint missed, then replay them with POST /v1/webhook-deliveries/{id}/retry. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/webhook-deliveries/{id}/retry": {
      "post": {
        "operationId": "WebhooksController_retry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "A new delivery attempt is queued outside the regular retry schedule. 0 tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/webhookDeliverySchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "webhooks"
        ],
        "summary": "Retry a webhook delivery",
        "description": "Queues one more attempt for a delivery that failed, outside the normal retry schedule — for when your receiver was down and you have already fixed it. Returns 202: the attempt is queued, not made inline. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/masters": {
      "post": {
        "operationId": "MastersController_create",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Client-generated unique string that makes a repeated call safe. Retrying with the same key and the same body returns the original result and charges nothing (X-Tokens-Charged: 0); the same key with a different body is rejected. Without this header a retry creates a second job and charges a second time."
          }
        ],
        "responses": {
          "202": {
            "description": "The job is queued: a job envelope with id, status and polling address. The charge is in the X-Tokens-Charged header; in the sandbox the price of a live run is in X-Tokens-Would-Charge. Sandbox scenarios (X-Test-Scenario) answer 402/429 with the error envelope and spend no tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/generationJobDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "402": {
            "description": "Not enough tokens for this call. Error code `insufficient_tokens`. There is no endpoint that tops up the balance — do it in the web dashboard.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "masters"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "game": {
                    "$ref": "#/components/schemas/gameRefSchema"
                  },
                  "art": {
                    "$ref": "#/components/schemas/artParamsSchema"
                  },
                  "format_group": {
                    "type": "string",
                    "enum": [
                      "tile",
                      "banner"
                    ],
                    "default": "tile"
                  },
                  "use_game_reference": {
                    "type": "boolean"
                  },
                  "variants": {
                    "type": "number",
                    "enum": [
                      2
                    ],
                    "default": 2
                  },
                  "variant_seed": {
                    "type": "integer",
                    "nullable": true,
                    "default": null
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "external_id": {
                    "type": "string"
                  },
                  "max_tokens": {
                    "type": "number",
                    "minimum": 0
                  },
                  "follows_job_id": {
                    "$ref": "#/components/schemas/followsJobIdField"
                  }
                },
                "required": [
                  "game",
                  "art"
                ],
                "additionalProperties": false
              },
              "example": {
                "game": {
                  "id": "00000000-0000-0000-0000-0000000000d0"
                },
                "art": {
                  "style": "dark-premium",
                  "background": "rays-vignette",
                  "hero": {
                    "mode": "auto"
                  },
                  "composition": "hero_upper_60_lower_third_clear"
                },
                "format_group": "tile",
                "variants": 2,
                "variant_seed": null,
                "external_id": "postman-master-1"
              }
            }
          }
        },
        "summary": "Generate a master image",
        "description": "The paid call at the centre of this API. Queues a generation job and returns 202 with a job envelope; poll `polling_url` (GET /v1/masters/jobs/{id}) until the status is `succeeded`, or subscribe to a webhook instead. `art.style` and `art.background` come from GET /v1/styles and GET /v1/backgrounds; `art.composition` comes from the `composition` field of GET /v1/formats and must not be invented by hand. Price it first with POST /v1/masters/estimate — the estimate is binding. SEND AN `Idempotency-Key`: without it a retried request creates a second job and charges a second time. The amount actually charged is returned in the `X-Tokens-Charged` response header; with a test key `X-Tokens-Would-Charge` shows what a live run would cost. Requires the `generate` scope. This call can spend tokens: the `X-Tokens-Charged` response header states what it actually cost."
      },
      "get": {
        "operationId": "MastersController_list",
        "parameters": [
          {
            "name": "game_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "exported",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "string"
                }
              ]
            }
          },
          {
            "name": "created_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from_batch",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "string"
                }
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job history in pages. By default only the source art (role=anchor): set rows would crowd out whole jobs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/mastersListResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "masters"
        ],
        "summary": "List your masters",
        "description": "Your generation history, newest first. This is how you recover after losing a job id, and how you find a master again by the `external_id` you sent when you created it. Cursor-based: pass `limit`, then `next_cursor` as `cursor`. Filter by `game_id`, `external_id`, `exported` or `created_after`. `from_batch=true` keeps only masters made by a batch, `from_batch=false` only single works; every record names its batch in `batch_id` (the original batch, also for a retried item), or `null`. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/masters/jobs/{id}": {
      "get": {
        "operationId": "MastersController_getJob",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The job state and the finished masters. Poll it via polling_url from the submission response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/generationJobDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "masters"
        ],
        "summary": "Poll a generation job",
        "description": "The polling half of POST /v1/masters. Call the `polling_url` you were given until `status` is `succeeded` (masters are in `masters[]`, each with its id for export), `failed` or `cancelled`. Poll no more often than every few seconds; a webhook subscription (POST /v1/webhook-endpoints) removes the need to poll at all. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/masters/estimate": {
      "post": {
        "operationId": "MastersController_estimate",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The price of a run before it starts. The price named here is a commitment as long as the reference state (the anchor/reference fields) stays the same.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/estimateResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "masters"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "game": {
                    "$ref": "#/components/schemas/gameRefSchema"
                  },
                  "art": {
                    "$ref": "#/components/schemas/artParamsSchema"
                  },
                  "format_group": {
                    "type": "string",
                    "enum": [
                      "tile",
                      "banner"
                    ],
                    "default": "tile"
                  },
                  "operation": {
                    "type": "string",
                    "enum": [
                      "generate",
                      "export"
                    ],
                    "default": "generate"
                  },
                  "variant_seed": {
                    "type": "integer",
                    "nullable": true,
                    "default": null
                  },
                  "use_game_reference": {
                    "type": "boolean"
                  },
                  "follows_job_id": {
                    "$ref": "#/components/schemas/followsJobIdField"
                  }
                },
                "required": [
                  "game",
                  "art"
                ],
                "additionalProperties": false
              },
              "example": {
                "game": {
                  "id": "00000000-0000-0000-0000-0000000000d0"
                },
                "art": {
                  "style": "dark-premium",
                  "background": "rays-vignette",
                  "hero": {
                    "mode": "auto"
                  },
                  "composition": "hero_upper_60_lower_third_clear"
                },
                "format_group": "tile",
                "operation": "generate",
                "variant_seed": null
              }
            }
          }
        },
        "summary": "Price a generation before running it",
        "description": "Returns what POST /v1/masters would charge for exactly this body, and the number is binding: what you are quoted here is what the generation costs, as long as the reference state it was priced against (returned as `anchor` and `reference`) still holds. THE BODY IS NOT A COPY OF THE POST /v1/masters BODY: this operation is strict, so the request schema below is the complete list of what it accepts. It takes the inputs that move the price (`game`, `art`, `format_group`, `variant_seed`, the reference switches) plus `operation`, which POST /v1/masters does not take; the fields POST /v1/masters has on top of that (`variants`, `webhook_url`, `external_id`) do not change the price and are rejected here with 400 `validation_failed`, `unrecognized_keys`. Drop them to price, put them back to generate. Set `operation` to `export` to price an export instead. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/masters/{id}": {
      "get": {
        "operationId": "MastersController_getOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The master as a history record, the same shape as a row of GET /v1/masters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/masterHistoryItemDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "masters"
        ],
        "summary": "Get one master",
        "description": "One record of your generation history by master id, in exactly the shape of a row of GET /v1/masters: game, art parameters, set state and, for a ready master, `render_url` and `download_url`. A master of another organisation, a set file and an id that does not exist all answer the same 404. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/masters/{id}/download": {
      "get": {
        "operationId": "MastersController_download",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The master art as a binary stream with Content-Disposition: attachment. The actual Content-Type is the file's own. Free, like every download.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "masters"
        ],
        "summary": "Download the master image from this API",
        "description": "The raw art of a ready master as a binary stream with `Content-Disposition: attachment`, served by this API instead of the storage host. The file is named after the game and the studio (`<game>_<studio>_master.png`). Downloading is always free. `download_url` stays in the master body for clients that prefer to fetch from storage directly. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/masters/jobs/{id}/cancel": {
      "post": {
        "operationId": "MastersController_cancel",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The job state after cancellation. A job that has already finished answers with its current state, not an error: a repeated cancel is not a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/generationJobDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "masters"
        ],
        "summary": "Cancel a generation job",
        "description": "Stops a job that is stuck or no longer wanted and releases the tokens still held for it. IRREVERSIBLE: a cancelled job cannot be resumed, and work already sent to the provider is not refunded. A job that has already finished answers with its current state rather than an error, so cancelling twice is not a failure. Whoever can spend must be able to stop spending, so the scope here is the same one that starts a job. Requires the `generate` scope. This call never spends tokens."
      }
    },
    "/v1/renders": {
      "post": {
        "operationId": "RendersController_render",
        "parameters": [],
        "responses": {
          "201": {
            "description": "The frames are drawn: links to them and the overlay_config_id to build the set with. Free (0 tokens): the master itself is free in any size and container; a frame of an already built set only in the container that set was delivered in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/createRenderResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "renders"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "master": {
                    "type": "string"
                  },
                  "template": {
                    "type": "string"
                  },
                  "layers": {
                    "$ref": "#/components/schemas/renderLayersSchema",
                    "default": {}
                  },
                  "locale": {
                    "type": "string"
                  },
                  "outputs": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/renderRequestOutputSchema"
                    },
                    "minItems": 1,
                    "maxItems": 16,
                    "description": "At most 16 outputs, each side at most 2048 px, and the sum of width × height over all outputs at most 16777216 pixels; otherwise 400 validation_failed."
                  }
                },
                "required": [
                  "master",
                  "outputs"
                ],
                "additionalProperties": false
              },
              "example": {
                "master": "00000000-0000-0000-0000-000000000001",
                "layers": {
                  "title": {
                    "text": "Gates of Fortune",
                    "visible": true
                  },
                  "provider": {
                    "text": "Pragmatic Play",
                    "visible": true
                  }
                },
                "outputs": [
                  {
                    "name": "tile-1x1",
                    "width": 640,
                    "height": 640,
                    "format": "png"
                  }
                ]
              }
            }
          }
        },
        "summary": "Render a captioned preview of a master",
        "description": "Draws the caption described by `layers` over a ready master and returns the frames plus an `overlay_config_id`. Pass that id to POST /v1/masters/{id}/export (or to its estimate) to build the set with exactly this caption: what you see here is what the files get. `layers: {}` is the default caption (game title and studio). Free: 0 tokens. Only a variant in status `ready` can be rendered (400 `master_not_ready` otherwise). A name the fonts cannot draw answers 422 `font_coverage`; a frame of an already built set in a container the set has not paid for answers 422 `container_not_purchased`, with the paid containers in `details.paid_file_formats` and the price of buying the requested one in `details.tokens`. Requires the `render` scope. This call never spends tokens."
      }
    },
    "/v1/credits": {
      "get": {
        "operationId": "CreditsController_getCredits",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The token balance (available plus reserved), what can be spent now, what is reserved, the nearest expiry and the breakdown by lot. Whole tokens outside, microtokens alongside for exact accounting.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/creditsResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "billing"
        ],
        "summary": "Get the token balance",
        "description": "`balance` is what you own: `available` plus `reserved`. `available` is what a new paid call can spend right now, the same figure the server checks before it takes tokens. `reserved` is already set aside for work in progress (a job, an export, the unfinished part of a batch); it is not part of `available`, and it is either charged or returned to `available` when that work settles. Tokens of an expired lot (see `lots`) are counted nowhere, even before the expiry entry reaches the ledger. `spending_frozen` means new paid calls will be refused, and `next_expiry_at` is when the nearest batch of tokens expires. Check `available` before an expensive call: there is no programmatic way to top up, the balance is topped up from the web dashboard. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/credits/ledger": {
      "get": {
        "operationId": "CreditsController_getLedger",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "entry_type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Comma-separated list of entry types to include. Allowed values: purchase, grant, hold, capture, release, adjustment, expiry. Any other value (including a different letter case) is rejected with 400 validation_failed. Empty value or empty items are ignored; omitted means all types."
            },
            "description": "Comma-separated list of entry types to include. Allowed values: purchase, grant, hold, capture, release, adjustment, expiry. Any other value (including a different letter case) is rejected with 400 validation_failed. Empty value or empty items are ignored; omitted means all types."
          }
        ],
        "responses": {
          "200": {
            "description": "The token ledger in pages: purchases, grants, holds, charges, refunds, adjustments and expiries. Append-only, rows are never rewritten.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ledgerListResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "billing"
        ],
        "summary": "List token ledger entries",
        "description": "Append-only history of every token movement: purchases, grants, holds, captures, releases, adjustments and expiries. This is where you reconcile what a job actually cost — rows are never rewritten. Cursor-based: pass `limit`, then `next_cursor` as `cursor`. Filter with `entry_type` (comma-separated, closed list — see the parameter; an unknown value is a 400). Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/batches/csv-preview": {
      "post": {
        "operationId": "BatchesController_csvPreview",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The report of matching CSV rows against the catalogue BEFORE the batch starts: what was found and what was not. 0 tokens. Only an exact match is `matched`. A row that is only similar to a catalogue game comes with `reason: inexact_match_unconfirmed` and the suggested game in `suggestion`; rows with a suggestion are not counted in `matched`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/csvPreviewResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "batches"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "csv": {
                    "type": "string",
                    "minLength": 1
                  }
                },
                "required": [
                  "csv"
                ],
                "additionalProperties": false
              },
              "example": {
                "csv": "game_name,provider,external_id\nGates of Fortune,Pragmatic Play,row-1\nBook of Sunrise,Pragmatic Play,row-2\n"
              }
            }
          }
        },
        "summary": "Dry-run a CSV against the catalogue",
        "description": "Matches every row of your CSV against the game catalogue and reports what was found and what was not — BEFORE any money moves. Run this first: rows that do not match are the ones you will otherwise pay to generate wrongly, or lose from the batch. Unmatched rows are found here for free. Requires the `batch` scope. This call never spends tokens."
      }
    },
    "/v1/batches": {
      "post": {
        "operationId": "BatchesController_create",
        "parameters": [],
        "responses": {
          "202": {
            "description": "The batch is accepted: one hold for the whole batch, the list of items and the polling address. In the sandbox the price of a live run is in X-Tokens-Would-Charge.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batchDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "402": {
            "description": "Not enough tokens for this call. Error code `insufficient_tokens`. There is no endpoint that tops up the balance — do it in the web dashboard.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "batches"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "generate",
                      "generate_and_export"
                    ],
                    "default": "generate"
                  },
                  "art": {
                    "$ref": "#/components/schemas/batchArtParamsSchema"
                  },
                  "format_group": {
                    "type": "string",
                    "enum": [
                      "tile",
                      "banner"
                    ],
                    "default": "tile"
                  },
                  "variants": {
                    "anyOf": [
                      {
                        "type": "number",
                        "enum": [
                          1
                        ]
                      },
                      {
                        "type": "number",
                        "enum": [
                          2
                        ]
                      }
                    ],
                    "default": 1
                  },
                  "auto_pick": {
                    "type": "string",
                    "enum": [
                      "first_qa_passed",
                      "best_qa_score"
                    ],
                    "default": "first_qa_passed"
                  },
                  "use_game_reference": {
                    "type": "boolean"
                  },
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/batchItemInputSchema"
                    },
                    "maxItems": 5000
                  },
                  "csv": {
                    "type": "string"
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "overlay": {
                    "$ref": "#/components/schemas/batchOverlayLayersSchema"
                  }
                },
                "required": [
                  "art"
                ],
                "additionalProperties": false
              },
              "example": {
                "operation": "generate",
                "art": {
                  "style": "dark-premium",
                  "background": "rays-vignette",
                  "hero": {
                    "mode": "auto"
                  },
                  "composition": "hero_upper_60_lower_third_clear"
                },
                "format_group": "tile",
                "variants": 1,
                "auto_pick": "first_qa_passed",
                "items": [
                  {
                    "external_id": "row-1",
                    "game_id": "00000000-0000-0000-0000-0000000000d0"
                  },
                  {
                    "external_id": "row-2",
                    "game": {
                      "name": "Book of Sunrise",
                      "provider_name": "Pragmatic Play"
                    }
                  }
                ]
              }
            }
          }
        },
        "summary": "Start a batch generation",
        "description": "Generates art for many games in one paid call — from `items[]` or from a `csv` payload. Dry-run the CSV with POST /v1/batches/csv-preview first: unmatched rows are found there for free. Items named by name run only on an exact catalogue match; an approximate match returns 409 inexact_match_unconfirmed with the suggested game_id, before any charge. Tokens are held for the whole batch up front and released for any item that does not run. Track progress with GET /v1/batches/{id}; stop it with POST /v1/batches/{id}/cancel, which returns the hold on items that have not started. Requires the `batch` scope. This call can spend tokens: the `X-Tokens-Charged` response header states what it actually cost."
      },
      "get": {
        "operationId": "BatchesController_list",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The organisation's batches.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batchesListResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "batches"
        ],
        "summary": "List batches",
        "description": "All batches of your organization, newest first. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/batches/{id}": {
      "get": {
        "operationId": "BatchesController_getOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "items_limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "items_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "items_status",
            "in": "query",
            "required": false,
            "schema": {
              "allOf": [
                {
                  "type": "string"
                },
                {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/batchItemStatusSchema"
                  },
                  "minItems": 1
                }
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The batch state together with its items: what is ready, what did not match, how much is held and how much is refunded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batchDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "batches"
        ],
        "summary": "Get one batch",
        "description": "State of the batch together with its items: what is ready, what failed to match the catalogue, how much is still held and how much has been returned. Poll this to follow a batch, or subscribe to webhooks instead. LARGE BATCHES: page the items. Pass any of `items_limit` (1–100, default 50), `items_after` (the `items_next_after` of the previous page) or `items_status` (comma-separated item statuses) and the response carries one page of items in `index` order, `items_next_after` and `items_counts` (items per status across the whole batch). Only paged items carry `master_id`, `render_url`, `download_url` and `tokens_refunded`. Without any `items_*` parameter the response is unchanged: every item, no page fields. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/batches/{id}/cancel": {
      "post": {
        "operationId": "BatchesController_cancel",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The batch state after cancellation: items not yet started are removed, their hold is refunded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batchDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "batches"
        ],
        "summary": "Cancel a batch",
        "description": "IRREVERSIBLE: items that have not started are dropped and their hold is returned; items already generated stay generated and stay charged. A cancelled batch cannot be resumed — start a new one. Whoever can spend must be able to stop spending, so the scope here is the same one that starts a batch. Requires the `batch` scope. This call never spends tokens."
      }
    },
    "/v1/batches/{id}/export/estimate": {
      "post": {
        "operationId": "BatchExportController_estimate",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Total price, what a live key would pay, the plan per master and the items that cannot be built.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batchExportEstimateResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "batches"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hero_position": {
                    "type": "string",
                    "enum": [
                      "center",
                      "left",
                      "right"
                    ],
                    "default": "center"
                  },
                  "file_format": {
                    "type": "string",
                    "enum": [
                      "webp",
                      "png",
                      "jpeg",
                      "avif"
                    ]
                  },
                  "file_formats": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "webp",
                        "png",
                        "jpeg",
                        "avif"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 4
                  },
                  "formats": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "tile-2x3",
                        "tile-2x3-2x",
                        "tile-3x4",
                        "tile-1x1",
                        "story-9x16",
                        "banner-16x9",
                        "banner-21x9"
                      ]
                    },
                    "minItems": 1
                  },
                  "max_tokens": {
                    "type": "number",
                    "minimum": 0
                  },
                  "selection": {
                    "$ref": "#/components/schemas/batchExportSelectionSchema"
                  },
                  "overlay": {
                    "$ref": "#/components/schemas/batchOverlayLayersSchema"
                  }
                },
                "required": [
                  "selection"
                ],
                "additionalProperties": false
              },
              "example": {
                "selection": {
                  "select": "all_ready",
                  "except_indexes": [
                    3
                  ]
                },
                "file_format": "webp",
                "overlay": {
                  "title": {
                    "visible": true
                  }
                },
                "max_tokens": 40
              }
            }
          }
        },
        "summary": "Price building file sets for a batch",
        "description": "The price of POST /v1/batches/{id}/export with the same body, before any money moves. Each chosen master is priced exactly as POST /v1/masters/{id}/export would price it: an already built set in the same configuration costs 0, a new set costs its usual price. `selection` is a rule (`all_ready` minus `except_indexes`, or explicit `indexes`), resolved over the whole batch, not over a page. Items that cannot be built are listed in `invalid` with a reason. Requires the `batch` scope. This call never spends tokens."
      }
    },
    "/v1/batches/{id}/export": {
      "post": {
        "operationId": "BatchExportController_create",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Client-generated unique string that makes a repeated call safe. Retrying with the same key and the same body returns the original result and charges nothing (X-Tokens-Charged: 0); the same key with a different body is rejected. Without this header a retry creates a second job and charges a second time."
          }
        ],
        "responses": {
          "202": {
            "description": "The order is accepted and runs in the background. In the sandbox X-Tokens-Would-Charge is the price of a live run.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batchExportOrderDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "402": {
            "description": "Not enough tokens for this call. Error code `insufficient_tokens`. There is no endpoint that tops up the balance — do it in the web dashboard.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "batches"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hero_position": {
                    "type": "string",
                    "enum": [
                      "center",
                      "left",
                      "right"
                    ],
                    "default": "center"
                  },
                  "file_format": {
                    "type": "string",
                    "enum": [
                      "webp",
                      "png",
                      "jpeg",
                      "avif"
                    ]
                  },
                  "file_formats": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "webp",
                        "png",
                        "jpeg",
                        "avif"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 4
                  },
                  "formats": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "tile-2x3",
                        "tile-2x3-2x",
                        "tile-3x4",
                        "tile-1x1",
                        "story-9x16",
                        "banner-16x9",
                        "banner-21x9"
                      ]
                    },
                    "minItems": 1
                  },
                  "max_tokens": {
                    "type": "number",
                    "minimum": 0
                  },
                  "selection": {
                    "$ref": "#/components/schemas/batchExportSelectionSchema"
                  },
                  "overlay": {
                    "$ref": "#/components/schemas/batchOverlayLayersSchema"
                  }
                },
                "required": [
                  "selection"
                ],
                "additionalProperties": false
              },
              "example": {
                "selection": {
                  "select": "all_ready",
                  "except_indexes": [
                    3
                  ]
                },
                "file_format": "webp",
                "overlay": {
                  "title": {
                    "visible": true
                  }
                },
                "max_tokens": 40
              }
            }
          }
        },
        "summary": "Build file sets for a batch",
        "description": "Builds the file set of every chosen ready master of the batch, one set per master, each priced and held exactly as POST /v1/masters/{id}/export (sets already built in the same configuration cost 0). The choice is frozen when you call: `all_ready` takes every ready item at that moment minus `except_indexes`; explicit `indexes` with any item that has no ready master is refused whole with 422 `batch_export_items_invalid`. The balance must cover the price of the whole order (402 otherwise, nothing held). The order runs in the background; follow it in `export_order` of GET /v1/batches/{id} with any `items_*` parameter, and each item's set in `items[].set`. Tokens are held per set as the order reaches it, so `export_order.charged` grows while it runs (X-Tokens-Charged of this call is 0). If the balance runs out midway the order stops as `stopped_insufficient`; sets already built stay built, a new call prices the rest. One running order per batch: a second call meanwhile is 409 `batch_export_in_progress`. `max_tokens` caps the whole order. These charges are not part of the batch `tokens_*` fields, which describe the generation run. SEND AN `Idempotency-Key` to make a retry safe. Scope: `batch`, not `export` — the sets of a batch are bought with the organisation's tokens like the batch generation itself. Requires the `batch` scope. This call starts paid work: the response itself holds nothing (`X-Tokens-Charged: 0`), tokens are held while the work runs, and 402 comes back if the balance cannot cover it."
      }
    },
    "/v1/batches/{id}/archives": {
      "post": {
        "operationId": "BatchExportController_archive",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The archive of the current content: a signed link, or the reason there is none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batchArchiveDtoSchema"
                }
              }
            }
          },
          "202": {
            "description": "The archive is being built; call again to poll.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batchArchiveDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "batches"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "kind": {
                    "$ref": "#/components/schemas/batchArchiveKindSchema"
                  }
                },
                "required": [
                  "kind"
                ],
                "additionalProperties": false
              },
              "example": {
                "kind": "masters"
              }
            }
          }
        },
        "summary": "Get a ZIP of the batch",
        "description": "Free. `kind: sets` packs every built file set of the batch (the current set of each item's current master); `kind: masters` packs the raw art of every ready master, file names prefixed with the item index so two items of the same game never overwrite each other. The archive belongs to the current content: after another build the next call makes a new archive, an old link is never handed out. 202 while it is being built (call again to poll), 200 with `download_url` when ready. Over 2 GB the archive is not made: `status: omitted` with `omitted_reason: too_large`; nothing to pack gives `nothing_assembled`. The batch page (GET with `items_*`) lists the current archives in `archives`. Requires the `batch` scope. This call never spends tokens."
      }
    },
    "/v1/batches/{id}/archives/{kind}/download": {
      "get": {
        "operationId": "BatchExportController_downloadArchive",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The batch archive as a binary stream with Content-Disposition: attachment. Free.",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "batches"
        ],
        "summary": "Download a batch ZIP from this API",
        "description": "The ready archive of the current content (`kind`: `sets` or `masters`) as a binary stream with `Content-Disposition: attachment`, served by this API instead of the storage host. 404 until POST /v1/batches/{id}/archives reports it ready, and for an archive of content that has changed since. Free, like every download. Requires the `read` scope. This call never spends tokens."
      }
    },
    "/v1/batches/{id}/retry/estimate": {
      "post": {
        "operationId": "BatchRetryController_estimate",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The price of the retry: per item, in total, and the items that cannot be retried.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batchRetryEstimateResponseSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "batches"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "kind": {
                    "$ref": "#/components/schemas/batchRetryKindSchema"
                  },
                  "selection": {
                    "$ref": "#/components/schemas/batchRetrySelectionSchema"
                  },
                  "background": {
                    "$ref": "#/components/schemas/backgroundCodeSchema"
                  },
                  "max_tokens": {
                    "type": "number",
                    "minimum": 0
                  }
                },
                "required": [
                  "kind",
                  "selection"
                ],
                "additionalProperties": false
              },
              "example": {
                "kind": "retry",
                "selection": {
                  "select": "failed"
                },
                "max_tokens": 5
              }
            }
          }
        },
        "summary": "Price a retry of batch items",
        "description": "Prices `POST /v1/batches/{id}/retry` with the same body, without starting anything. `items` gives the price of each item and whether it is the free retry; `refused` lists the items that cannot be retried and why. Requires the `batch` scope. This call never spends tokens."
      }
    },
    "/v1/batches/{id}/retry": {
      "post": {
        "operationId": "BatchRetryController_create",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Client-generated unique string that makes a repeated call safe. Retrying with the same key and the same body returns the original result and charges nothing (X-Tokens-Charged: 0); the same key with a different body is rejected. Without this header a retry creates a second job and charges a second time."
          }
        ],
        "responses": {
          "202": {
            "description": "The retry batch is accepted: one hold for its paid items (none if every item is free), its items and the polling address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/batchDtoSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key. Error code `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "402": {
            "description": "Not enough tokens for this call. Error code `insufficient_tokens`. There is no endpoint that tops up the balance — do it in the web dashboard.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the scope this operation requires. Error code `forbidden_scope`; `details.required_scope` names the missing one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Error code `rate_limited`; the `Retry-After` response header says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          },
          "default": {
            "description": "The single error envelope: {error:{code,message,request_id,details}}. One component for every 4xx/5xx code; listing them per operation would be copy-paste that goes stale first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/apiErrorSchema"
                }
              }
            }
          }
        },
        "tags": [
          "batches"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "kind": {
                    "$ref": "#/components/schemas/batchRetryKindSchema"
                  },
                  "selection": {
                    "$ref": "#/components/schemas/batchRetrySelectionSchema"
                  },
                  "background": {
                    "$ref": "#/components/schemas/backgroundCodeSchema"
                  },
                  "max_tokens": {
                    "type": "number",
                    "minimum": 0
                  }
                },
                "required": [
                  "kind",
                  "selection"
                ],
                "additionalProperties": false
              },
              "example": {
                "kind": "retry",
                "selection": {
                  "select": "failed"
                },
                "max_tokens": 5
              }
            }
          }
        },
        "summary": "Retry failed batch items or regenerate ready ones",
        "description": "Starts a new batch for the chosen items of this batch; the new batch names this one in `retry_of_batch_id`, runs like any batch and sends its own `batch.completed`. `kind: retry` re-runs failed items. An item that failed with a generation error and never produced a master gets one free retry (`X-Tokens-Charged: 0`); the free retry counts as used once a call to the image generator was made for it — cancelling the retry before it starts keeps it, and so does a call the generator refused without doing any work (HTTP 429 quota, 401, 403). Any further retry, and a retry of an item whose master was delivered, costs 1 token. `kind: regenerate` draws a new master for a ready item for 1 token; the previous master stays downloadable and in your tiles. Only the master is generated: build the formats of the new master with the batch format build. `background` replaces the background of the original batch; everything else is inherited. Items still running give 409 `batch_retry_in_progress`; items that cannot be retried give 422 `batch_retry_not_applicable` with the reason per item; nothing is held in either case. `max_tokens` caps the price (409 `price_above_limit`). Send an `Idempotency-Key`: a repeat with the same key and body returns the same retry batch and charges nothing. Requires the `batch` scope. This call can spend tokens: the `X-Tokens-Charged` response header states what it actually cost."
      }
    }
  },
  "components": {
    "securitySchemes": {
      "api-key": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "type": "http",
        "description": "API key in the Authorization header: `Authorization: Bearer spai_live_...`. Keys are issued and rotated in the web dashboard; there is no endpoint for it. Use a `spai_test_...` key while integrating — test keys never reach a paid provider and never spend real tokens, while `spai_live_...` keys do."
      }
    },
    "schemas": {
      "anchorSchema": {
        "type": "string",
        "enum": [
          "applied",
          "none"
        ]
      },
      "apiErrorSchema": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              },
              "details": {
                "type": "object",
                "additionalProperties": {}
              }
            },
            "required": [
              "code",
              "message",
              "request_id"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "artParamsSchema": {
        "type": "object",
        "properties": {
          "style": {
            "$ref": "#/components/schemas/styleCodeSchema"
          },
          "background": {
            "$ref": "#/components/schemas/backgroundCodeSchema"
          },
          "hero": {
            "$ref": "#/components/schemas/heroParamsSchema",
            "default": {
              "mode": "auto"
            }
          },
          "composition": {
            "type": "string",
            "minLength": 1
          },
          "notes": {
            "type": "string",
            "maxLength": 500
          }
        },
        "required": [
          "style",
          "background",
          "composition"
        ]
      },
      "backgroundCodeSchema": {
        "type": "string",
        "enum": [
          "flat-gradient",
          "solid-cutout",
          "flat-pattern",
          "radial-glow",
          "rays-vignette",
          "light-studio",
          "material-backdrop",
          "treasure-burst",
          "themed-bokeh",
          "depth-scene"
        ]
      },
      "backgroundDtoSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "preview_url": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "label"
        ],
        "additionalProperties": false
      },
      "captionZoneSchema": {
        "type": "object",
        "properties": {
          "zone": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "minItems": 4,
            "maxItems": 4
          },
          "mean_rgb": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 0,
              "maximum": 255
            },
            "minItems": 3,
            "maxItems": 3
          }
        },
        "required": [
          "zone",
          "mean_rgb"
        ]
      },
      "createRenderResponseSchema": {
        "type": "object",
        "properties": {
          "master": {
            "type": "string"
          },
          "overlay_config_id": {
            "type": "string"
          },
          "layers_hash": {
            "type": "string"
          },
          "render_ms": {
            "type": "integer"
          },
          "outputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/renderOutputResultSchema"
            }
          },
          "cache_hits": {
            "type": "integer"
          }
        },
        "required": [
          "master",
          "overlay_config_id",
          "layers_hash",
          "render_ms",
          "outputs",
          "cache_hits"
        ]
      },
      "engineSchema": {
        "type": "string",
        "enum": [
          "pro",
          "showcase"
        ]
      },
      "estimateResponseSchema": {
        "type": "object",
        "properties": {
          "tokens": {
            "type": "number"
          },
          "tokens_breakdown": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "item": {
                  "type": "string"
                },
                "images": {
                  "type": "integer"
                },
                "tokens": {
                  "type": "number"
                }
              },
              "required": [
                "item",
                "tokens"
              ]
            }
          },
          "balance_after": {
            "type": "number"
          },
          "duplicate_of": {
            "type": "string",
            "nullable": true
          },
          "estimated_ready_in_seconds": {
            "type": "number"
          },
          "reason": {
            "type": "string"
          },
          "anchor": {
            "$ref": "#/components/schemas/anchorSchema"
          },
          "reference": {
            "$ref": "#/components/schemas/anchorSchema"
          }
        },
        "required": [
          "tokens",
          "tokens_breakdown"
        ],
        "additionalProperties": false
      },
      "exportBuildEntrySchema": {
        "type": "object",
        "properties": {
          "overlay_config_id": {
            "type": "string",
            "nullable": true
          },
          "build_no": {
            "type": "integer"
          },
          "requested_at": {
            "type": "string"
          },
          "published_at": {
            "type": "string",
            "nullable": true
          },
          "outcome": {
            "type": "string",
            "enum": [
              "succeeded",
              "partial",
              "failed",
              "cancelled"
            ],
            "nullable": true
          },
          "outcome_reason": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "overlay_config_id",
          "build_no",
          "requested_at",
          "published_at",
          "outcome",
          "outcome_reason"
        ]
      },
      "exportBundleDtoSchema": {
        "type": "object",
        "properties": {
          "export_set_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "partial",
              "succeeded",
              "failed"
            ]
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/exportFileDtoSchema"
            }
          },
          "zip_url": {
            "type": "string"
          },
          "tokens_charged": {
            "type": "number"
          },
          "tokens_refunded": {
            "type": "number"
          },
          "paid_file_formats": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "webp",
                "png",
                "jpeg",
                "avif"
              ]
            },
            "description": "File containers this set has paid for, in the order webp, png, jpeg, avif. Switching the set to any of them is free; the price of the others is in container_prices of the export estimate."
          },
          "file_format": {
            "type": "string",
            "enum": [
              "webp",
              "png",
              "jpeg",
              "avif"
            ],
            "nullable": true
          },
          "containers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "file_format": {
                  "type": "string",
                  "enum": [
                    "webp",
                    "png",
                    "jpeg",
                    "avif"
                  ]
                },
                "files": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/exportFileDtoSchema"
                  }
                }
              },
              "required": [
                "file_format",
                "files"
              ],
              "additionalProperties": false
            },
            "description": "Every published file container of the set, the primary one first, each with its own files. The primary container repeats `file_format` and `files`. One element for a single-container set, empty when the set has no files."
          },
          "formats_expected": {
            "type": "number"
          },
          "formats_ready": {
            "type": "number"
          },
          "files_ready": {
            "type": "number"
          },
          "missing": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/exportMissingFormatSchema"
            }
          },
          "not_selected": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/exportNotSelectedFormatSchema"
            }
          },
          "overlay_config_id": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "export_set_id",
          "status",
          "files",
          "tokens_charged",
          "tokens_refunded",
          "paid_file_formats",
          "file_format",
          "containers",
          "formats_expected",
          "formats_ready",
          "files_ready",
          "missing",
          "not_selected",
          "overlay_config_id"
        ],
        "additionalProperties": false
      },
      "exportFileDtoSchema": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "bytes": {
            "type": "integer"
          },
          "checksum": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "mime": {
            "type": "string"
          },
          "has_overlay": {
            "type": "boolean"
          },
          "sizes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/exportFileSizeDtoSchema"
            }
          }
        },
        "required": [
          "format",
          "filename",
          "width",
          "height",
          "bytes",
          "checksum",
          "url",
          "mime",
          "has_overlay"
        ],
        "additionalProperties": false
      },
      "exportFileSizeDtoSchema": {
        "type": "object",
        "properties": {
          "filename": {
            "type": "string"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "bytes": {
            "type": "integer"
          },
          "checksum": {
            "type": "string"
          },
          "mime": {
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "filename",
          "width",
          "height",
          "bytes",
          "checksum",
          "mime",
          "url"
        ],
        "additionalProperties": false
      },
      "exportFormatProgressSchema": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/exportFormatStatusSchema"
          },
          "regenerated": {
            "type": "boolean"
          },
          "quality": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "passed": {
                    "type": "boolean",
                    "enum": [
                      true
                    ]
                  }
                },
                "required": [
                  "passed"
                ]
              },
              {
                "type": "object",
                "properties": {
                  "passed": {
                    "type": "boolean",
                    "enum": [
                      false
                    ]
                  },
                  "reason": {
                    "type": "string"
                  }
                },
                "required": [
                  "passed"
                ]
              }
            ]
          }
        },
        "required": [
          "format",
          "status"
        ],
        "additionalProperties": false
      },
      "exportFormatStatusSchema": {
        "type": "string",
        "enum": [
          "pending",
          "generating",
          "qa",
          "ready",
          "rejected",
          "failed"
        ]
      },
      "exportJobDtoSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "generation_job"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "export"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "partial",
              "succeeded",
              "failed",
              "cancelled"
            ]
          },
          "tokens_held": {
            "type": "number"
          },
          "tokens_charged": {
            "type": "number"
          },
          "source_master": {
            "type": "string"
          },
          "export_set_id": {
            "type": "string",
            "nullable": true
          },
          "formats": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "layers_included": {
            "type": "boolean"
          },
          "progress": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/exportFormatProgressSchema"
            }
          },
          "polling_url": {
            "type": "string"
          },
          "file_format": {
            "type": "string",
            "enum": [
              "webp",
              "png",
              "jpeg",
              "avif"
            ],
            "nullable": true
          },
          "file_formats": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "webp",
                "png",
                "jpeg",
                "avif"
              ]
            },
            "description": "Every file container this job builds, the primary one (`file_format`) first. One element for a single-container build, empty when `file_format` is null."
          },
          "last_build": {
            "allOf": [
              {
                "$ref": "#/components/schemas/exportBuildEntrySchema"
              }
            ],
            "nullable": true
          },
          "cancel_requested_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "kind",
          "status",
          "source_master",
          "export_set_id",
          "formats",
          "layers_included",
          "file_format",
          "file_formats",
          "last_build"
        ],
        "additionalProperties": false
      },
      "exportJobStatusSchema": {
        "type": "string",
        "enum": [
          "queued",
          "running",
          "partial",
          "succeeded",
          "failed",
          "cancelled"
        ]
      },
      "exportMissingFormatSchema": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "available_free": {
            "type": "boolean"
          },
          "retry": {
            "type": "string",
            "enum": [
              "available",
              "in_progress",
              "used"
            ],
            "nullable": true
          }
        },
        "required": [
          "format",
          "status",
          "reason",
          "available_free",
          "retry"
        ],
        "additionalProperties": false
      },
      "exportNotSelectedFormatSchema": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "available_free": {
            "type": "boolean"
          }
        },
        "required": [
          "format",
          "reason",
          "available_free"
        ],
        "additionalProperties": false
      },
      "followsJobIdField": {
        "type": "string",
        "format": "uuid",
        "nullable": true
      },
      "formatDtoSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "group": {
            "type": "string",
            "enum": [
              "tile",
              "banner"
            ]
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "aspect": {
            "type": "string"
          },
          "composition": {
            "type": "string"
          },
          "is_group_anchor": {
            "type": "boolean"
          },
          "same_aspect_as_anchor": {
            "type": "boolean"
          },
          "sizes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "width": {
                  "type": "integer"
                },
                "height": {
                  "type": "integer"
                }
              },
              "required": [
                "width",
                "height"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "code",
          "label",
          "group",
          "width",
          "height",
          "aspect",
          "composition"
        ],
        "additionalProperties": false
      },
      "gameRefSchema": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "required": [
              "id"
            ]
          },
          {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "minLength": 1
              },
              "provider_name": {
                "type": "string",
                "minLength": 1
              }
            },
            "required": [
              "name",
              "provider_name"
            ]
          }
        ]
      },
      "generationJobDtoSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "generation_job"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "generate",
              "export",
              "pregen"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/jobStatusSchema"
          },
          "tokens_held": {
            "type": "number"
          },
          "tokens_charged": {
            "type": "number"
          },
          "tokens_released": {
            "type": "boolean"
          },
          "engine": {
            "$ref": "#/components/schemas/engineSchema"
          },
          "anchor": {
            "$ref": "#/components/schemas/anchorSchema"
          },
          "reference": {
            "$ref": "#/components/schemas/anchorSchema"
          },
          "degraded": {
            "type": "boolean"
          },
          "failure": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "content_refused",
                  "style_anchor_unavailable",
                  "processing_error",
                  "qa_rejected",
                  "provider_error",
                  "item_data_invalid",
                  "export_build_failed"
                ]
              },
              "retryable": {
                "type": "boolean"
              }
            },
            "required": [
              "code",
              "retryable"
            ],
            "additionalProperties": false
          },
          "started_at": {
            "type": "string"
          },
          "finished_at": {
            "type": "string"
          },
          "cancel_requested_at": {
            "type": "string"
          },
          "masters": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/masterArtDtoSchema"
            }
          },
          "text_sanitized": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/textSanitizedNoticeSchema"
            }
          },
          "polling_url": {
            "type": "string"
          },
          "events_url": {
            "type": "string"
          },
          "estimated_ready_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "kind",
          "status",
          "masters"
        ],
        "additionalProperties": false
      },
      "heroParamsSchema": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "auto",
              "custom"
            ],
            "default": "auto"
          },
          "cutout": {
            "type": "boolean"
          },
          "text": {
            "type": "string",
            "maxLength": 200
          }
        }
      },
      "jobStatusSchema": {
        "type": "string",
        "enum": [
          "queued",
          "running",
          "partial",
          "succeeded",
          "failed",
          "cancelled"
        ]
      },
      "masterArtDtoSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/masterStatusSchema"
          },
          "variant_index": {
            "type": "integer"
          },
          "format": {
            "type": "string"
          },
          "variant_seed": {
            "type": "integer",
            "nullable": true
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "engine": {
            "$ref": "#/components/schemas/engineSchema"
          },
          "degraded": {
            "type": "boolean"
          },
          "regenerated": {
            "type": "boolean"
          },
          "render_url": {
            "type": "string"
          },
          "download_url": {
            "type": "string"
          },
          "quality": {
            "$ref": "#/components/schemas/qualitySchema"
          }
        },
        "required": [
          "id",
          "status",
          "variant_index",
          "format"
        ],
        "additionalProperties": false
      },
      "masterHistoryItemDtoSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/masterStatusSchema"
          },
          "variant_index": {
            "type": "integer"
          },
          "format": {
            "type": "string"
          },
          "variant_seed": {
            "type": "integer",
            "nullable": true
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "engine": {
            "$ref": "#/components/schemas/engineSchema"
          },
          "degraded": {
            "type": "boolean"
          },
          "regenerated": {
            "type": "boolean"
          },
          "render_url": {
            "type": "string"
          },
          "download_url": {
            "type": "string"
          },
          "quality": {
            "$ref": "#/components/schemas/qualitySchema"
          },
          "created_at": {
            "type": "string"
          },
          "exported": {
            "type": "boolean"
          },
          "game": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "slug": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name",
              "slug"
            ]
          },
          "style_code": {
            "$ref": "#/components/schemas/styleCodeSchema"
          },
          "background_code": {
            "$ref": "#/components/schemas/backgroundCodeSchema"
          },
          "composition_code": {
            "type": "string"
          },
          "hero_mode": {
            "type": "string",
            "enum": [
              "auto",
              "custom"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "anchor",
              "derivative",
              "resize"
            ]
          },
          "anchor_master_id": {
            "type": "string",
            "nullable": true
          },
          "export_set_id": {
            "type": "string",
            "nullable": true
          },
          "export_job_id": {
            "type": "string",
            "nullable": true
          },
          "export_status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/exportJobStatusSchema"
              }
            ],
            "nullable": true
          },
          "batch_id": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "id",
          "status",
          "variant_index",
          "format"
        ],
        "additionalProperties": false
      },
      "masterStatusSchema": {
        "type": "string",
        "enum": [
          "pending",
          "generating",
          "qa",
          "ready",
          "rejected",
          "failed"
        ]
      },
      "mastersListResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/masterHistoryItemDtoSchema"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          },
          "has_more": {
            "type": "boolean"
          }
        },
        "required": [
          "data",
          "next_cursor",
          "has_more"
        ]
      },
      "privateStyleCodeSchema": {
        "type": "string",
        "pattern": "^org-[a-z0-9]{8}$",
        "description": "A private style code of your organisation, see GET /v1/styles."
      },
      "publicStyleCodeSchema": {
        "type": "string",
        "enum": [
          "glossy-3d",
          "painted-2d",
          "anime-cel",
          "ink-novel",
          "dark-premium",
          "grit-noir",
          "neon-night",
          "retro-chrome",
          "photo-real",
          "clean-minimal",
          "soft-storybook",
          "flat-bold"
        ]
      },
      "qualityReasonSchema": {
        "type": "string",
        "enum": [
          "artifact",
          "composition",
          "text_in_art"
        ]
      },
      "qualitySchema": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "passed": {
                "type": "boolean",
                "enum": [
                  true
                ]
              }
            },
            "required": [
              "passed"
            ]
          },
          {
            "type": "object",
            "properties": {
              "passed": {
                "type": "boolean",
                "enum": [
                  false
                ]
              },
              "reason": {
                "$ref": "#/components/schemas/qualityReasonSchema"
              }
            },
            "required": [
              "passed"
            ]
          }
        ]
      },
      "renderLayersSchema": {
        "type": "object",
        "properties": {
          "title": {
            "type": "object",
            "properties": {
              "text": {
                "type": "string",
                "maxLength": 40
              },
              "visible": {
                "type": "boolean"
              },
              "max_lines": {
                "type": "integer",
                "minimum": 1,
                "maximum": 4
              },
              "color": {
                "type": "string"
              },
              "position": {
                "type": "string",
                "enum": [
                  "bottom",
                  "center",
                  "top"
                ]
              },
              "treatment": {
                "type": "string",
                "enum": [
                  "outline-heavy",
                  "outline-thin",
                  "shadow-only",
                  "plate",
                  "gradient-scrim",
                  "outline-tinted"
                ]
              },
              "width_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "height_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "bottom_margin_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 0.5
              },
              "number_size_coef": {
                "type": "number",
                "minimum": 0.5,
                "maximum": 3
              },
              "shadow_offset_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 0.5
              },
              "shadow_blur_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 0.5
              }
            },
            "additionalProperties": false
          },
          "provider": {
            "type": "object",
            "properties": {
              "text": {
                "type": "string",
                "maxLength": 32
              },
              "visible": {
                "type": "boolean"
              },
              "size_fraction": {
                "type": "number",
                "minimum": 0.005,
                "maximum": 0.2
              },
              "gap_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 0.5
              }
            },
            "additionalProperties": false
          },
          "background": {
            "type": "object",
            "properties": {
              "force_gradient": {
                "type": "boolean",
                "nullable": true
              },
              "brightness_threshold": {
                "type": "integer",
                "minimum": 0,
                "maximum": 255
              }
            },
            "additionalProperties": false
          },
          "corners": {
            "type": "object",
            "properties": {
              "radius_pct": {
                "type": "number",
                "minimum": 0,
                "maximum": 50
              }
            },
            "required": [
              "radius_pct"
            ],
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      },
      "renderOutputResultSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "width": {
            "type": "integer",
            "exclusiveMinimum": true,
            "minimum": 0
          },
          "height": {
            "type": "integer",
            "exclusiveMinimum": true,
            "minimum": 0
          },
          "format": {
            "type": "string",
            "enum": [
              "webp",
              "png",
              "jpeg",
              "avif"
            ]
          },
          "quality": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          },
          "bytes": {
            "type": "integer"
          },
          "url": {
            "type": "string"
          },
          "immutable": {
            "type": "boolean"
          },
          "caption_zone": {
            "$ref": "#/components/schemas/captionZoneSchema"
          },
          "caption_zone_variants": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/captionZoneSchema"
            }
          }
        },
        "required": [
          "name",
          "width",
          "height",
          "format",
          "bytes",
          "url",
          "immutable"
        ]
      },
      "renderRequestOutputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "width": {
            "type": "integer",
            "exclusiveMinimum": true,
            "minimum": 0,
            "maximum": 2048
          },
          "height": {
            "type": "integer",
            "exclusiveMinimum": true,
            "minimum": 0,
            "maximum": 2048
          },
          "format": {
            "type": "string",
            "enum": [
              "webp",
              "png",
              "jpeg",
              "avif"
            ]
          },
          "quality": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          }
        },
        "required": [
          "name",
          "width",
          "height",
          "format"
        ]
      },
      "styleCodeSchema": {
        "anyOf": [
          {
            "$ref": "#/components/schemas/publicStyleCodeSchema"
          },
          {
            "$ref": "#/components/schemas/privateStyleCodeSchema"
          }
        ]
      },
      "styleDtoSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "preview_url": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "label"
        ],
        "additionalProperties": false
      },
      "textSanitizedNoticeSchema": {
        "type": "object",
        "properties": {
          "field": {
            "type": "string"
          },
          "action": {
            "type": "string",
            "enum": [
              "cleaned",
              "dropped"
            ]
          }
        },
        "required": [
          "field",
          "action"
        ],
        "additionalProperties": false
      },
      "artBriefDtoSchema": {
        "type": "object",
        "properties": {
          "theme": {
            "type": "string"
          },
          "key_visual": {
            "type": "string"
          },
          "character": {
            "type": "object",
            "properties": {
              "present": {
                "type": "boolean"
              },
              "description": {
                "type": "string"
              }
            }
          },
          "palette": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "mood": {
            "type": "string"
          }
        }
      },
      "backgroundsListResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/backgroundDtoSchema"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "batchArchiveDtoSchema": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "sets",
              "masters"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "building",
              "ready",
              "omitted"
            ]
          },
          "download_url": {
            "type": "string"
          },
          "omitted_reason": {
            "type": "string",
            "enum": [
              "too_large",
              "nothing_assembled"
            ]
          }
        },
        "required": [
          "kind",
          "status"
        ],
        "additionalProperties": false
      },
      "batchArchiveKindSchema": {
        "type": "string",
        "enum": [
          "sets",
          "masters"
        ]
      },
      "batchArtParamsSchema": {
        "type": "object",
        "properties": {
          "style": {
            "$ref": "#/components/schemas/styleCodeSchema"
          },
          "background": {
            "$ref": "#/components/schemas/backgroundCodeSchema"
          },
          "hero": {
            "$ref": "#/components/schemas/heroParamsSchema",
            "default": {
              "mode": "auto"
            }
          },
          "composition": {
            "type": "string",
            "minLength": 1,
            "default": "hero_upper_60_lower_third_clear"
          }
        },
        "required": [
          "style",
          "background"
        ]
      },
      "batchDtoSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "generation_batch"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/batchStatusSchema"
          },
          "operation": {
            "type": "string",
            "enum": [
              "generate",
              "generate_and_export"
            ]
          },
          "items_total": {
            "type": "integer"
          },
          "items_done": {
            "type": "integer"
          },
          "items_failed": {
            "type": "integer"
          },
          "items_unmatched": {
            "type": "integer"
          },
          "tokens_held": {
            "type": "number"
          },
          "tokens_charged": {
            "type": "number"
          },
          "tokens_refunded": {
            "type": "number"
          },
          "tokens_per_item": {
            "type": "number"
          },
          "style_anchor_applied": {
            "type": "boolean"
          },
          "zip_url": {
            "type": "string"
          },
          "bundle_omitted_reason": {
            "type": "string",
            "enum": [
              "export_not_ordered",
              "nothing_assembled",
              "too_large"
            ]
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/batchItemDtoSchema"
            }
          },
          "items_next_after": {
            "type": "integer",
            "nullable": true
          },
          "items_counts": {
            "type": "object",
            "required": [
              "unmatched",
              "queued",
              "pending_anchor",
              "generating",
              "ready",
              "exported",
              "exported_partial",
              "rejected",
              "failed",
              "cancelled"
            ],
            "properties": {
              "unmatched": {
                "type": "integer"
              },
              "queued": {
                "type": "integer"
              },
              "pending_anchor": {
                "type": "integer"
              },
              "generating": {
                "type": "integer"
              },
              "ready": {
                "type": "integer"
              },
              "exported": {
                "type": "integer"
              },
              "exported_partial": {
                "type": "integer"
              },
              "rejected": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "cancelled": {
                "type": "integer"
              }
            },
            "additionalProperties": false
          },
          "text_sanitized": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/textSanitizedNoticeSchema"
            }
          },
          "polling_url": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "finished_at": {
            "type": "string"
          },
          "retry_of_batch_id": {
            "type": "string"
          },
          "export_order": {
            "$ref": "#/components/schemas/batchExportOrderDtoSchema"
          },
          "export_charged": {
            "type": "number"
          },
          "archives": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/batchArchiveDtoSchema"
            }
          },
          "retries": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "status": {
                  "$ref": "#/components/schemas/batchStatusSchema"
                },
                "items_total": {
                  "type": "integer"
                },
                "tokens_held": {
                  "type": "number"
                },
                "tokens_charged": {
                  "type": "number"
                },
                "tokens_refunded": {
                  "type": "number"
                },
                "created_at": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "status",
                "items_total",
                "tokens_held",
                "tokens_charged",
                "tokens_refunded",
                "created_at"
              ],
              "additionalProperties": false
            }
          },
          "chain_money": {
            "type": "object",
            "properties": {
              "charged": {
                "type": "number"
              },
              "refunded": {
                "type": "number"
              }
            },
            "required": [
              "charged",
              "refunded"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "object",
          "status",
          "operation",
          "items_total",
          "items_done",
          "items_failed",
          "items_unmatched",
          "tokens_held",
          "tokens_charged",
          "tokens_refunded",
          "tokens_per_item"
        ],
        "additionalProperties": false
      },
      "batchExportEstimateResponseSchema": {
        "type": "object",
        "properties": {
          "tokens": {
            "type": "number"
          },
          "would_charge": {
            "type": "number"
          },
          "masters": {
            "type": "integer"
          },
          "plans": {
            "type": "object",
            "properties": {
              "new_set": {
                "type": "integer"
              },
              "requeue": {
                "type": "integer"
              },
              "in_flight": {
                "type": "integer"
              },
              "ready": {
                "type": "integer"
              },
              "container_purchase": {
                "type": "integer"
              }
            },
            "required": [
              "new_set",
              "requeue",
              "in_flight",
              "ready",
              "container_purchase"
            ],
            "additionalProperties": false
          },
          "invalid": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "reason": {
                  "$ref": "#/components/schemas/batchExportItemRefusalSchema"
                }
              },
              "required": [
                "index",
                "reason"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "tokens",
          "would_charge",
          "masters",
          "plans",
          "invalid"
        ],
        "additionalProperties": false
      },
      "batchExportItemRefusalSchema": {
        "type": "string",
        "enum": [
          "not_ready",
          "unmatched",
          "no_master",
          "not_in_batch",
          "sandbox_key_on_live_set"
        ]
      },
      "batchExportOrderDtoSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "running",
              "done",
              "stopped_insufficient",
              "stopped_price",
              "failed"
            ]
          },
          "cursor": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "charged": {
            "type": "number"
          },
          "stop_detail": {
            "type": "object",
            "properties": {
              "at_index": {
                "type": "integer"
              },
              "need": {
                "type": "number"
              },
              "skipped": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "index": {
                      "type": "integer"
                    },
                    "reason": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "index",
                    "reason"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "additionalProperties": false
          },
          "created_at": {
            "type": "string"
          },
          "finished_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "status",
          "cursor",
          "total",
          "charged",
          "created_at"
        ],
        "additionalProperties": false
      },
      "batchExportSelectionSchema": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "select": {
                "type": "string",
                "enum": [
                  "all_ready"
                ]
              },
              "except_indexes": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "minimum": 0
                },
                "default": []
              }
            },
            "required": [
              "select"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "select": {
                "type": "string",
                "enum": [
                  "items"
                ]
              },
              "indexes": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "minimum": 0
                },
                "minItems": 1
              }
            },
            "required": [
              "select",
              "indexes"
            ],
            "additionalProperties": false
          }
        ]
      },
      "batchGameRefSchema": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "game_id": {
                "type": "string"
              }
            },
            "required": [
              "game_id"
            ]
          },
          {
            "type": "object",
            "properties": {
              "game": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "provider_name": {
                    "type": "string",
                    "minLength": 1
                  }
                },
                "required": [
                  "name",
                  "provider_name"
                ]
              }
            },
            "required": [
              "game"
            ]
          }
        ]
      },
      "batchItemDtoSchema": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "external_id": {
            "type": "string"
          },
          "input": {
            "type": "object",
            "additionalProperties": {}
          },
          "matched": {
            "type": "boolean"
          },
          "match_confidence": {
            "type": "number"
          },
          "slot_id": {
            "type": "string"
          },
          "slot_name": {
            "type": "string"
          },
          "provider_name": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/batchItemStatusSchema"
          },
          "master_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "export_set_id": {
            "type": "string",
            "nullable": true
          },
          "error_code": {
            "type": "string"
          },
          "overlay_applied": {
            "type": "boolean"
          },
          "overlay_suppressed_reason": {
            "type": "string",
            "enum": [
              "not_ordered",
              "inexact_match",
              "font_coverage",
              "game_name_missing"
            ]
          },
          "master_id": {
            "type": "string"
          },
          "render_url": {
            "type": "string"
          },
          "download_url": {
            "type": "string"
          },
          "tokens_refunded": {
            "type": "number"
          },
          "set": {
            "type": "object",
            "properties": {
              "export_set_id": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "building",
                  "ready",
                  "partial",
                  "failed"
                ]
              },
              "download_url": {
                "type": "string"
              }
            },
            "required": [
              "export_set_id",
              "status"
            ],
            "additionalProperties": false
          },
          "attempts": {
            "type": "integer"
          },
          "retry": {
            "type": "object",
            "properties": {
              "price": {
                "type": "number"
              },
              "free": {
                "type": "boolean"
              }
            },
            "required": [
              "price",
              "free"
            ],
            "additionalProperties": false
          },
          "last_attempt_error_code": {
            "type": "string"
          }
        },
        "required": [
          "index",
          "input",
          "matched",
          "status"
        ],
        "additionalProperties": false
      },
      "batchItemInputSchema": {
        "allOf": [
          {
            "type": "object",
            "properties": {
              "external_id": {
                "type": "string"
              }
            }
          },
          {
            "$ref": "#/components/schemas/batchGameRefSchema"
          }
        ]
      },
      "batchItemStatusSchema": {
        "type": "string",
        "enum": [
          "unmatched",
          "queued",
          "pending_anchor",
          "generating",
          "ready",
          "exported",
          "exported_partial",
          "rejected",
          "failed",
          "cancelled"
        ]
      },
      "batchOverlayLayersSchema": {
        "type": "object",
        "properties": {
          "title": {
            "type": "object",
            "properties": {
              "visible": {
                "type": "boolean"
              },
              "max_lines": {
                "type": "integer",
                "minimum": 1,
                "maximum": 4
              },
              "color": {
                "type": "string"
              },
              "position": {
                "type": "string",
                "enum": [
                  "bottom",
                  "center",
                  "top"
                ]
              },
              "treatment": {
                "type": "string",
                "enum": [
                  "outline-heavy",
                  "outline-thin",
                  "shadow-only",
                  "plate",
                  "gradient-scrim",
                  "outline-tinted"
                ]
              },
              "width_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "height_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "bottom_margin_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 0.5
              },
              "number_size_coef": {
                "type": "number",
                "minimum": 0.5,
                "maximum": 3
              },
              "shadow_offset_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 0.5
              },
              "shadow_blur_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 0.5
              }
            },
            "additionalProperties": false
          },
          "provider": {
            "type": "object",
            "properties": {
              "visible": {
                "type": "boolean"
              },
              "size_fraction": {
                "type": "number",
                "minimum": 0.005,
                "maximum": 0.2
              },
              "gap_fraction": {
                "type": "number",
                "minimum": 0,
                "maximum": 0.5
              }
            },
            "additionalProperties": false
          },
          "background": {
            "type": "object",
            "properties": {
              "force_gradient": {
                "type": "boolean",
                "nullable": true
              },
              "brightness_threshold": {
                "type": "integer",
                "minimum": 0,
                "maximum": 255
              }
            },
            "additionalProperties": false
          },
          "corners": {
            "type": "object",
            "properties": {
              "radius_pct": {
                "type": "number",
                "minimum": 0,
                "maximum": 50
              }
            },
            "required": [
              "radius_pct"
            ],
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      },
      "batchRetryEstimateResponseSchema": {
        "type": "object",
        "properties": {
          "tokens": {
            "type": "number"
          },
          "would_charge": {
            "type": "number"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "price": {
                  "type": "number"
                },
                "free": {
                  "type": "boolean"
                }
              },
              "required": [
                "index",
                "price",
                "free"
              ],
              "additionalProperties": false
            }
          },
          "refused": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "reason": {
                  "$ref": "#/components/schemas/batchRetryRefusalSchema"
                }
              },
              "required": [
                "index",
                "reason"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "tokens",
          "would_charge",
          "items",
          "refused"
        ],
        "additionalProperties": false
      },
      "batchRetryKindSchema": {
        "type": "string",
        "enum": [
          "retry",
          "regenerate"
        ]
      },
      "batchRetryRefusalSchema": {
        "type": "string",
        "enum": [
          "unmatched",
          "cancelled",
          "in_progress",
          "nothing_to_regenerate",
          "not_in_batch",
          "not_failed",
          "game_not_found"
        ]
      },
      "batchRetrySelectionSchema": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "select": {
                "type": "string",
                "enum": [
                  "failed"
                ]
              }
            },
            "required": [
              "select"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "select": {
                "type": "string",
                "enum": [
                  "items"
                ]
              },
              "indexes": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "minimum": 0
                },
                "minItems": 1
              }
            },
            "required": [
              "select",
              "indexes"
            ],
            "additionalProperties": false
          }
        ]
      },
      "batchStatusSchema": {
        "type": "string",
        "enum": [
          "queued",
          "running",
          "partial",
          "succeeded",
          "failed",
          "cancelled"
        ]
      },
      "batchesListResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/batchDtoSchema"
            }
          },
          "has_more": {
            "type": "boolean"
          }
        },
        "required": [
          "data",
          "has_more"
        ]
      },
      "briefConfidenceSchema": {
        "type": "string",
        "enum": [
          "verified",
          "unverified",
          "none"
        ]
      },
      "createGameResponseSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "provider": {
            "$ref": "#/components/schemas/providerRefDtoSchema"
          },
          "visibility": {
            "type": "string",
            "enum": [
              "private"
            ]
          },
          "art_brief": {
            "$ref": "#/components/schemas/artBriefDtoSchema"
          },
          "brief_status": {
            "type": "string",
            "enum": [
              "ready",
              "pending"
            ]
          },
          "brief_confidence": {
            "$ref": "#/components/schemas/briefConfidenceSchema"
          },
          "text_sanitized": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/textSanitizedNoticeSchema"
            }
          },
          "similar_public_games": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "similarity": {
                  "type": "number"
                }
              },
              "required": [
                "id",
                "name",
                "similarity"
              ]
            }
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "provider",
          "visibility",
          "brief_status",
          "brief_confidence"
        ],
        "additionalProperties": false
      },
      "creditLotSchema": {
        "type": "object",
        "properties": {
          "entry_id": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "original": {
            "type": "number"
          },
          "remaining": {
            "type": "number"
          },
          "expires_at": {
            "type": "string",
            "nullable": true
          },
          "expired": {
            "type": "boolean"
          }
        },
        "required": [
          "entry_id",
          "kind",
          "reason",
          "original",
          "remaining",
          "expires_at",
          "expired"
        ]
      },
      "creditsResponseSchema": {
        "type": "object",
        "properties": {
          "balance": {
            "type": "number"
          },
          "balance_microtokens": {
            "type": "number"
          },
          "reserved": {
            "type": "number"
          },
          "available": {
            "type": "number"
          },
          "spending_frozen": {
            "type": "boolean"
          },
          "next_expiry_at": {
            "type": "string",
            "nullable": true
          },
          "lots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/creditLotSchema"
            }
          }
        },
        "required": [
          "balance",
          "balance_microtokens",
          "reserved",
          "available",
          "spending_frozen",
          "next_expiry_at",
          "lots"
        ]
      },
      "csvPreviewResponseSchema": {
        "type": "object",
        "properties": {
          "total_rows": {
            "type": "integer"
          },
          "matched": {
            "type": "integer"
          },
          "unmatched": {
            "type": "integer"
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/csvPreviewRowSchema"
            }
          }
        },
        "required": [
          "total_rows",
          "matched",
          "unmatched",
          "rows"
        ],
        "additionalProperties": false
      },
      "csvPreviewRowSchema": {
        "type": "object",
        "properties": {
          "row": {
            "type": "integer"
          },
          "game_name": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "external_id": {
            "type": "string"
          },
          "matched": {
            "type": "boolean"
          },
          "slot_id": {
            "type": "string"
          },
          "matched_name": {
            "type": "string"
          },
          "matched_provider": {
            "type": "string"
          },
          "confidence": {
            "type": "number"
          },
          "reason": {
            "type": "string",
            "enum": [
              "not_found",
              "inexact_match_unconfirmed"
            ]
          },
          "suggestion": {
            "type": "object",
            "properties": {
              "slot_id": {
                "type": "string"
              },
              "matched_name": {
                "type": "string"
              },
              "matched_provider": {
                "type": "string"
              },
              "confidence": {
                "type": "number"
              }
            },
            "required": [
              "slot_id",
              "matched_name",
              "matched_provider",
              "confidence"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "row",
          "game_name",
          "provider",
          "matched"
        ],
        "additionalProperties": false
      },
      "exportEstimateResponseSchema": {
        "type": "object",
        "properties": {
          "tokens": {
            "type": "number"
          },
          "free": {
            "type": "boolean"
          },
          "plan": {
            "$ref": "#/components/schemas/exportPlanSchema"
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "export_set_id": {
            "type": "string",
            "nullable": true
          },
          "export_job_id": {
            "type": "string",
            "nullable": true
          },
          "overlay_config_id": {
            "type": "string",
            "nullable": true
          },
          "file_format": {
            "type": "string",
            "enum": [
              "webp",
              "png",
              "jpeg",
              "avif"
            ],
            "nullable": true
          },
          "overlay_layers": {
            "nullable": true
          },
          "paid_file_formats": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "webp",
                "png",
                "jpeg",
                "avif"
              ]
            },
            "description": "File containers this set has paid for, in the order webp, png, jpeg, avif. Switching to any of them is free. Empty for a set that has never been published."
          },
          "file_formats": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "webp",
                "png",
                "jpeg",
                "avif"
              ]
            },
            "description": "File containers the set is published in, the primary one first. Empty for a set that has never been published."
          },
          "approx_zip_bytes": {
            "type": "object",
            "properties": {
              "one": {
                "type": "number",
                "nullable": true,
                "description": "Approximate ZIP size in bytes for the set in a single container."
              },
              "all": {
                "type": "number",
                "nullable": true,
                "description": "Approximate ZIP size in bytes for the set in all four containers."
              },
              "by_format": {
                "type": "object",
                "properties": {
                  "webp": {
                    "type": "number",
                    "description": "Approximate ZIP size in bytes for the set in WebP alone."
                  },
                  "png": {
                    "type": "number",
                    "description": "Approximate ZIP size in bytes for the set in PNG alone."
                  },
                  "jpeg": {
                    "type": "number",
                    "description": "Approximate ZIP size in bytes for the set in JPEG alone."
                  },
                  "avif": {
                    "type": "number",
                    "description": "Approximate ZIP size in bytes for the set in AVIF alone."
                  }
                },
                "required": [
                  "webp",
                  "png",
                  "jpeg",
                  "avif"
                ],
                "description": "Approximate ZIP size for each single container, so the size shown matches the container that is picked."
              }
            },
            "required": [
              "one",
              "all",
              "by_format"
            ],
            "description": "Approximate size of the set archive before the export call: exact for an already published archive of the same containers, otherwise a measured typical size of a full set."
          },
          "container_prices": {
            "type": "object",
            "properties": {
              "webp": {
                "type": "number",
                "description": "Tokens POST /v1/masters/{id}/export would charge with file_format webp."
              },
              "png": {
                "type": "number",
                "description": "Tokens POST /v1/masters/{id}/export would charge with file_format png."
              },
              "jpeg": {
                "type": "number",
                "description": "Tokens POST /v1/masters/{id}/export would charge with file_format jpeg."
              },
              "avif": {
                "type": "number",
                "description": "Tokens POST /v1/masters/{id}/export would charge with file_format avif."
              },
              "all": {
                "type": "number",
                "description": "Tokens POST /v1/masters/{id}/export would charge with file_formats [webp, png, jpeg, avif]: 3 for a new set, 1 for any purchase of containers on a built set, 0 when all four are paid."
              }
            },
            "required": [
              "webp",
              "png",
              "jpeg",
              "avif",
              "all"
            ],
            "description": "Full price of the export call for each single container, in tokens. On a built set a paid container costs 0 and an unpaid one is a container purchase; on a new set each equals the price of the set. In the sandbox every price is 0."
          }
        },
        "required": [
          "tokens",
          "free",
          "plan",
          "reason",
          "export_set_id",
          "export_job_id",
          "overlay_config_id",
          "file_format",
          "paid_file_formats",
          "file_formats",
          "approx_zip_bytes",
          "container_prices"
        ]
      },
      "exportPlanSchema": {
        "type": "string",
        "enum": [
          "new_set",
          "in_flight",
          "requeue",
          "ready"
        ]
      },
      "formatsListResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/formatDtoSchema"
            }
          },
          "note": {
            "type": "string"
          }
        },
        "required": [
          "data",
          "note"
        ]
      },
      "gameDetailDtoSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "provider": {
            "$ref": "#/components/schemas/providerRefDtoSchema"
          },
          "release_date": {
            "type": "string",
            "nullable": true
          },
          "is_upcoming": {
            "type": "boolean"
          },
          "theme_tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "showcase_preview_url": {
            "type": "string",
            "nullable": true
          },
          "brief_confidence": {
            "$ref": "#/components/schemas/briefConfidenceSchema"
          },
          "art_brief": {
            "$ref": "#/components/schemas/artBriefDtoSchema"
          },
          "masters": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/gameMasterSummaryDtoSchema"
            }
          },
          "has_reference": {
            "type": "boolean"
          },
          "reference_preview_url": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "provider",
          "is_upcoming",
          "brief_confidence"
        ],
        "additionalProperties": false
      },
      "gameListItemDtoSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "provider": {
            "$ref": "#/components/schemas/providerRefDtoSchema"
          },
          "release_date": {
            "type": "string",
            "nullable": true
          },
          "is_upcoming": {
            "type": "boolean"
          },
          "theme_tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "showcase_preview_url": {
            "type": "string",
            "nullable": true
          },
          "brief_confidence": {
            "$ref": "#/components/schemas/briefConfidenceSchema"
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "provider",
          "is_upcoming",
          "brief_confidence"
        ],
        "additionalProperties": false
      },
      "gameMasterSummaryDtoSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "variant_index": {
            "type": "integer"
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "status",
          "variant_index"
        ],
        "additionalProperties": false
      },
      "gamesListResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/gameListItemDtoSchema"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          },
          "has_more": {
            "type": "boolean"
          }
        },
        "required": [
          "data",
          "next_cursor",
          "has_more"
        ]
      },
      "ledgerEntrySchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "entry_type": {
            "type": "string"
          },
          "amount": {
            "type": "number"
          },
          "balance_after": {
            "type": "number"
          },
          "job_id": {
            "type": "string",
            "nullable": true
          },
          "order_id": {
            "type": "string",
            "nullable": true
          },
          "deposit_id": {
            "type": "string",
            "nullable": true
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "entry_type",
          "amount",
          "balance_after",
          "job_id",
          "order_id",
          "reason",
          "expires_at",
          "created_at"
        ]
      },
      "ledgerListResponseSchema": {
        "type": "object",
        "properties": {
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ledgerEntrySchema"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "entries",
          "next_cursor"
        ]
      },
      "meResponseSchema": {
        "type": "object",
        "properties": {
          "organization": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "plan": {
                "type": "string"
              },
              "token_balance": {
                "type": "number"
              }
            },
            "required": [
              "id",
              "name",
              "plan",
              "token_balance"
            ],
            "nullable": true
          },
          "environment": {
            "type": "string"
          },
          "api_key_id": {
            "type": "string"
          },
          "oauth_connection_id": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "daily_token_cap": {
            "type": "number"
          },
          "daily_tokens_used": {
            "type": "number"
          },
          "daily_cap_resets_at": {
            "type": "string"
          }
        },
        "required": [
          "organization",
          "environment",
          "scopes"
        ]
      },
      "overlayHistoryEntrySchema": {
        "type": "object",
        "properties": {
          "overlay_config_id": {
            "type": "string",
            "nullable": true
          },
          "build_no": {
            "type": "integer"
          },
          "requested_at": {
            "type": "string"
          },
          "published_at": {
            "type": "string",
            "nullable": true
          },
          "outcome": {
            "type": "string",
            "enum": [
              "succeeded",
              "partial",
              "failed",
              "cancelled"
            ],
            "nullable": true
          },
          "outcome_reason": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "overlay_config_id",
          "build_no",
          "requested_at",
          "published_at",
          "outcome",
          "outcome_reason"
        ]
      },
      "overlayHistoryPageSchema": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "export_set_id": {
            "type": "string"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/overlayHistoryEntrySchema"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        },
        "required": [
          "object",
          "export_set_id",
          "data",
          "has_more",
          "limit",
          "offset"
        ]
      },
      "providerRefDtoSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "created": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "slug",
          "name"
        ],
        "additionalProperties": false
      },
      "providerRefListItemSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "website": {
            "type": "string",
            "nullable": true
          },
          "has_logo": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "website",
          "has_logo"
        ]
      },
      "providersListResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/providerRefListItemSchema"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "stylesListResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/styleDtoSchema"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "webhookDeliveriesListResponseSchema": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/webhookDeliverySchema"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "webhookDeliverySchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "endpoint_id": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "event_type": {
            "type": "string"
          },
          "attempt": {
            "type": "integer"
          },
          "http_status": {
            "type": "integer"
          },
          "response_body": {
            "type": "string"
          },
          "duration_ms": {
            "type": "integer"
          },
          "next_retry_at": {
            "type": "string"
          },
          "delivered_at": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "endpoint_id",
          "event_id",
          "event_type",
          "attempt",
          "created_at"
        ]
      },
      "webhookEndpointCreatedResponseSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "secret": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "description": {
            "type": "string"
          },
          "is_active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "url",
          "secret",
          "events",
          "is_active",
          "created_at"
        ]
      },
      "webhookEndpointDeletedResponseSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "deleted"
        ]
      },
      "webhookEndpointSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "description": {
            "type": "string"
          },
          "is_active": {
            "type": "boolean"
          },
          "fail_streak": {
            "type": "integer"
          },
          "disabled_at": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "url",
          "events",
          "is_active",
          "fail_streak",
          "created_at"
        ]
      },
      "webhookEndpointsListResponseSchema": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/webhookEndpointSchema"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "webhookPingResponseSchema": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string"
          },
          "delivered": {
            "type": "boolean"
          },
          "http_status": {
            "type": "integer",
            "nullable": true
          },
          "error_code": {
            "type": "string"
          },
          "duration_ms": {
            "type": "integer"
          }
        },
        "required": [
          "event_id",
          "delivered",
          "http_status",
          "duration_ms"
        ]
      }
    }
  }
}
