{
  "openapi": "3.1.0",
  "info": {
    "title": "EigenInteractive Engine API",
    "version": "0.2.1",
    "description": "The server-authoritative game engine API. Client routes (`/api/engine/*`) require a Firebase ID token; the external-bot webhook (`/api/bot/action`) is HMAC-authenticated."
  },
  "tags": [
    {
      "name": "Games",
      "description": "Create, join, start, play and finish games. The heart of the API: every move is a proposal the server validates against authoritative state."
    },
    {
      "name": "Me",
      "description": "The signed-in player — profile, username, ratings, rating history and registered devices."
    },
    {
      "name": "Players",
      "description": "Other players: their public profile, their finished games and their ratings."
    },
    {
      "name": "Social",
      "description": "Friends, friend requests, blocking and user search."
    },
    {
      "name": "Bots",
      "description": "The bots this deployment offers as opponents."
    },
    {
      "name": "BotWebhook",
      "description": "The HMAC-authenticated callback an external bot answers on. Not for clients."
    },
    {
      "name": "Health",
      "description": "Liveness probe."
    }
  ],
  "security": [
    {
      "firebase": []
    }
  ],
  "components": {
    "securitySchemes": {
      "firebase": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "A Firebase ID token, sent as `Authorization: Bearer <token>` (or `?token=` on WebSocket upgrades)."
      },
      "botHmac": {
        "type": "apiKey",
        "in": "header",
        "name": "Eigen-Signature",
        "description": "An external bot's HMAC signature over the exact request body, bound to the `action` domain. Scheme `v1,<base64>`; the per-bot key is `HMAC(BOT_SIGNING_SECRET, botId)`. The engine signs wakes with the same header in the other direction."
      }
    },
    "schemas": {
      "Health": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          }
        },
        "required": [
          "status"
        ]
      },
      "Lobby": {
        "type": "object",
        "properties": {
          "games": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GameSummary"
            }
          }
        },
        "required": [
          "games"
        ]
      },
      "GameSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "createdBy": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/GameStatus"
          },
          "access": {
            "$ref": "#/components/schemas/GameAccess"
          },
          "schemaVersion": {
            "type": "integer"
          },
          "config": {
            "type": "object"
          },
          "turnSeconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "budgetSeconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "incrementSeconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "rated": {
            "type": "boolean"
          },
          "ratingPool": {
            "type": [
              "string",
              "null"
            ]
          },
          "minPlayers": {
            "type": "integer"
          },
          "maxPlayers": {
            "type": "integer"
          },
          "shortCode": {
            "type": "string"
          },
          "pendingPlayers": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "integer"
            }
          },
          "turnDeadline": {
            "type": [
              "integer",
              "null"
            ]
          },
          "outcomes": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/Outcome"
            }
          },
          "ratings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RatingDelta"
            }
          },
          "finishedAt": {
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "type": "integer"
          },
          "updatedAt": {
            "type": "integer"
          },
          "participants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Seat"
            }
          }
        },
        "required": [
          "id",
          "createdBy",
          "status",
          "access",
          "schemaVersion",
          "config",
          "turnSeconds",
          "budgetSeconds",
          "incrementSeconds",
          "rated",
          "ratingPool",
          "minPlayers",
          "maxPlayers",
          "shortCode",
          "pendingPlayers",
          "turnDeadline",
          "outcomes",
          "finishedAt",
          "createdAt",
          "updatedAt",
          "participants"
        ]
      },
      "GameStatus": {
        "type": "string",
        "enum": [
          "waiting",
          "ready",
          "active",
          "finished",
          "aborted"
        ]
      },
      "GameAccess": {
        "type": "string",
        "enum": [
          "public",
          "private",
          "friends"
        ]
      },
      "Outcome": {
        "type": "object",
        "properties": {
          "playerIndex": {
            "type": "integer"
          },
          "result": {
            "type": "string",
            "enum": [
              "win",
              "loss",
              "draw",
              "eliminated"
            ]
          },
          "placement": {
            "type": "integer"
          },
          "teamIndex": {
            "type": "integer"
          },
          "score": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "playerIndex",
          "result",
          "placement",
          "teamIndex"
        ]
      },
      "RatingDelta": {
        "type": "object",
        "properties": {
          "identity": {
            "$ref": "#/components/schemas/RatingIdentity"
          },
          "pool": {
            "type": "string"
          },
          "muBefore": {
            "type": "number"
          },
          "sigmaBefore": {
            "type": "number"
          },
          "displayBefore": {
            "type": "integer"
          },
          "muAfter": {
            "type": "number"
          },
          "sigmaAfter": {
            "type": "number"
          },
          "displayAfter": {
            "type": "integer"
          },
          "displayChange": {
            "type": "integer"
          }
        },
        "required": [
          "identity",
          "pool",
          "muBefore",
          "sigmaBefore",
          "displayBefore",
          "muAfter",
          "sigmaAfter",
          "displayAfter",
          "displayChange"
        ]
      },
      "RatingIdentity": {
        "type": "object",
        "properties": {
          "userId": {
            "type": [
              "string",
              "null"
            ]
          },
          "botId": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "userId",
          "botId"
        ]
      },
      "Seat": {
        "type": "object",
        "properties": {
          "playerIndex": {
            "type": "integer"
          },
          "userId": {
            "type": [
              "string",
              "null"
            ]
          },
          "botId": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "human",
              "bot"
            ]
          }
        },
        "required": [
          "playerIndex",
          "userId",
          "botId",
          "type"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "$ref": "#/components/schemas/ErrorCode"
          }
        },
        "required": [
          "error"
        ]
      },
      "ErrorCode": {
        "type": "string",
        "enum": [
          "notActive",
          "notReady",
          "expired",
          "notPending",
          "stateUpdated",
          "invalidPayload",
          "illegalMove",
          "unknownGame",
          "notJoinable",
          "gameFull",
          "alreadyJoined",
          "notParticipant",
          "notCreator",
          "creatorCannotLeave",
          "schemaUnsupported",
          "usernameInvalid",
          "usernameTaken",
          "friendsOnly",
          "registrationRequired",
          "imageTooLarge",
          "unsupportedImageType",
          "rateLimited"
        ],
        "description": "A stable machine code identifying why a request failed."
      },
      "MyGames": {
        "type": "object",
        "properties": {
          "games": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GameSummary"
            }
          }
        },
        "required": [
          "games"
        ]
      },
      "Players": {
        "type": "object",
        "properties": {
          "players": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Player"
            }
          }
        },
        "required": [
          "players"
        ]
      },
      "Player": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "username": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "avatarUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "isAnonymous": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "username",
          "displayName",
          "avatarUrl",
          "isAnonymous"
        ]
      },
      "Bots": {
        "type": "object",
        "properties": {
          "bots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Bot"
            }
          }
        },
        "required": [
          "bots"
        ]
      },
      "Bot": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "username": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "avatarUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "schemaVersion": {
            "type": "integer"
          },
          "ratedEligible": {
            "type": "boolean"
          },
          "config": {
            "type": "object"
          }
        },
        "required": [
          "id",
          "username",
          "displayName",
          "avatarUrl",
          "schemaVersion",
          "ratedEligible",
          "config"
        ]
      },
      "Profile": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Player"
          },
          {
            "type": "object",
            "properties": {
              "email": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "createdAt": {
                "type": "integer"
              }
            },
            "required": [
              "email",
              "createdAt"
            ]
          }
        ]
      },
      "Ratings": {
        "type": "object",
        "properties": {
          "ratings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Rating"
            }
          }
        },
        "required": [
          "ratings"
        ]
      },
      "Rating": {
        "type": "object",
        "properties": {
          "pool": {
            "type": "string"
          },
          "mu": {
            "type": "number"
          },
          "sigma": {
            "type": "number"
          },
          "displayRating": {
            "type": "integer"
          },
          "updatedAt": {
            "type": "integer"
          }
        },
        "required": [
          "pool",
          "mu",
          "sigma",
          "displayRating",
          "updatedAt"
        ]
      },
      "PlayerGames": {
        "type": "object",
        "properties": {
          "games": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GameSummary"
            }
          }
        },
        "required": [
          "games"
        ]
      },
      "RatingHistory": {
        "type": "object",
        "properties": {
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RatingHistoryEntry"
            }
          }
        },
        "required": [
          "history"
        ]
      },
      "RatingHistoryEntry": {
        "type": "object",
        "properties": {
          "gameId": {
            "type": "string"
          },
          "pool": {
            "type": "string"
          },
          "displayBefore": {
            "type": "integer"
          },
          "displayAfter": {
            "type": "integer"
          },
          "displayChange": {
            "type": "integer"
          },
          "createdAt": {
            "type": "integer"
          }
        },
        "required": [
          "gameId",
          "pool",
          "displayBefore",
          "displayAfter",
          "displayChange",
          "createdAt"
        ]
      },
      "Created": {
        "type": "object",
        "properties": {
          "gameId": {
            "type": "string"
          },
          "shortCode": {
            "type": "string"
          }
        },
        "required": [
          "gameId",
          "shortCode"
        ]
      },
      "CreateGame": {
        "type": "object",
        "properties": {
          "access": {
            "$ref": "#/components/schemas/GameAccess"
          },
          "schemaVersion": {
            "type": "integer"
          },
          "config": {
            "type": "object"
          },
          "minPlayers": {
            "type": "integer",
            "minimum": 1
          },
          "maxPlayers": {
            "type": "integer",
            "minimum": 1
          },
          "rated": {
            "type": "boolean"
          },
          "turnSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "default": null
          },
          "budgetSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "default": null
          },
          "incrementSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "default": null
          }
        },
        "required": [
          "access",
          "schemaVersion",
          "config",
          "minPlayers",
          "maxPlayers"
        ]
      },
      "SoloStarted": {
        "type": "object",
        "properties": {
          "gameId": {
            "type": "string"
          },
          "shortCode": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "frame": {
            "$ref": "#/components/schemas/Frame"
          }
        },
        "required": [
          "gameId",
          "shortCode",
          "version",
          "frame"
        ]
      },
      "Frame": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "frame"
            ]
          },
          "version": {
            "type": "integer"
          },
          "data": {
            "type": "object"
          },
          "pendingPlayers": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "deadline": {
            "type": [
              "integer",
              "null"
            ]
          },
          "playerTimes": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "integer"
            }
          },
          "outcomes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Outcome"
            }
          },
          "ratings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RatingDelta"
            }
          }
        },
        "required": [
          "type",
          "version",
          "data",
          "pendingPlayers",
          "deadline",
          "playerTimes"
        ]
      },
      "CreateSolo": {
        "type": "object",
        "properties": {
          "schemaVersion": {
            "type": "integer"
          },
          "config": {
            "type": "object"
          },
          "minPlayers": {
            "type": "integer",
            "minimum": 1
          },
          "maxPlayers": {
            "type": "integer",
            "minimum": 1
          },
          "rated": {
            "type": "boolean"
          },
          "botIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1
          },
          "turnSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "default": null
          },
          "budgetSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "default": null
          },
          "incrementSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "default": null
          }
        },
        "required": [
          "schemaVersion",
          "config",
          "minPlayers",
          "maxPlayers",
          "botIds"
        ]
      },
      "Joined": {
        "type": "object",
        "properties": {
          "gameId": {
            "type": "string"
          },
          "roster": {
            "$ref": "#/components/schemas/Roster"
          }
        },
        "required": [
          "gameId",
          "roster"
        ]
      },
      "Roster": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "roster"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/GameStatus"
          },
          "players": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Seat"
            }
          }
        },
        "required": [
          "type",
          "status",
          "players"
        ]
      },
      "Join": {
        "type": "object",
        "properties": {
          "clientSchemaVersion": {
            "type": "integer"
          },
          "commandId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          }
        },
        "required": [
          "clientSchemaVersion"
        ]
      },
      "JoinByCode": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Join"
          },
          {
            "type": "object",
            "properties": {
              "shortCode": {
                "type": "string",
                "minLength": 1
              }
            },
            "required": [
              "shortCode"
            ]
          }
        ]
      },
      "LobbyAccepted": {
        "type": "object",
        "properties": {
          "roster": {
            "$ref": "#/components/schemas/Roster"
          }
        },
        "required": [
          "roster"
        ]
      },
      "LobbyCommand": {
        "type": "object",
        "properties": {
          "commandId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          }
        }
      },
      "AddBot": {
        "type": "object",
        "properties": {
          "botId": {
            "type": "string"
          },
          "commandId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          }
        },
        "required": [
          "botId"
        ]
      },
      "CommandAccepted": {
        "type": "object",
        "properties": {
          "version": {
            "type": "integer"
          },
          "frame": {
            "$ref": "#/components/schemas/Frame"
          }
        },
        "required": [
          "version",
          "frame"
        ]
      },
      "Action": {
        "type": "object",
        "properties": {
          "seat": {
            "type": "integer",
            "minimum": 0
          },
          "data": {},
          "expectedVersion": {
            "type": "integer",
            "minimum": 0
          },
          "commandId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          }
        },
        "required": [
          "seat",
          "expectedVersion"
        ]
      },
      "Forfeit": {
        "type": "object",
        "properties": {
          "seat": {
            "type": "integer",
            "minimum": 0
          },
          "commandId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          }
        },
        "required": [
          "seat"
        ]
      },
      "Frames": {
        "type": "object",
        "properties": {
          "frames": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Frame"
            }
          }
        },
        "required": [
          "frames"
        ]
      },
      "UsernameUpdated": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string"
          }
        },
        "required": [
          "username"
        ]
      },
      "UsernameUpdate": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "minLength": 3,
            "maxLength": 20
          }
        },
        "required": [
          "username"
        ]
      },
      "DisplayNameUpdated": {
        "type": "object",
        "properties": {
          "displayName": {
            "type": "string"
          }
        },
        "required": [
          "displayName"
        ]
      },
      "DisplayNameUpdate": {
        "type": "object",
        "properties": {
          "displayName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          }
        },
        "required": [
          "displayName"
        ]
      },
      "DeviceRegistration": {
        "type": "object",
        "properties": {
          "fid": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256
          },
          "platform": {
            "type": "string",
            "enum": [
              "ios",
              "android",
              "web"
            ]
          }
        },
        "required": [
          "fid",
          "platform"
        ]
      },
      "Friends": {
        "type": "object",
        "properties": {
          "friends": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Friend"
            }
          }
        },
        "required": [
          "friends"
        ]
      },
      "Friend": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "avatarUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "isAnonymous": {
            "type": "boolean"
          },
          "userId": {
            "type": "string"
          },
          "since": {
            "type": "integer"
          }
        },
        "required": [
          "username",
          "displayName",
          "avatarUrl",
          "isAnonymous",
          "userId",
          "since"
        ]
      },
      "FriendRequests": {
        "type": "object",
        "properties": {
          "requests": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FriendRequest"
            }
          }
        },
        "required": [
          "requests"
        ]
      },
      "FriendRequest": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "avatarUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "isAnonymous": {
            "type": "boolean"
          },
          "userId": {
            "type": "string"
          },
          "since": {
            "type": "integer"
          },
          "direction": {
            "type": "string",
            "enum": [
              "incoming",
              "outgoing"
            ]
          }
        },
        "required": [
          "username",
          "displayName",
          "avatarUrl",
          "isAnonymous",
          "userId",
          "since",
          "direction"
        ]
      },
      "FriendsGames": {
        "type": "object",
        "properties": {
          "games": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GameSummary"
            }
          }
        },
        "required": [
          "games"
        ]
      },
      "UserSearch": {
        "type": "object",
        "properties": {
          "users": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Player"
            }
          }
        },
        "required": [
          "users"
        ]
      },
      "FriendRequestResult": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "requested",
              "accepted"
            ]
          }
        },
        "required": [
          "status"
        ]
      },
      "FriendTarget": {
        "type": "object",
        "properties": {
          "targetUserId": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "targetUserId"
        ]
      },
      "BotAction": {
        "type": "object",
        "properties": {
          "botId": {
            "type": "string",
            "minLength": 1
          },
          "gameId": {
            "type": "string",
            "minLength": 1
          },
          "playerIndex": {
            "type": "integer",
            "minimum": 0
          },
          "version": {
            "type": "integer",
            "minimum": 0
          },
          "data": {}
        },
        "required": [
          "botId",
          "gameId",
          "playerIndex",
          "version"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/health": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "Health"
        ],
        "security": [],
        "summary": "Liveness probe",
        "description": "Public, unauthenticated liveness check. Performs no I/O and reads no configuration, so a 200 means only that the worker is deployed and routable — it does **not** imply that D1, the game Durable Objects, or auth are correctly configured. Served `no-store`. Safe to call without a token; a bad token is ignored rather than rejected.",
        "responses": {
          "200": {
            "description": "The worker is deployed and serving",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/lobby": {
      "get": {
        "operationId": "getLobby",
        "tags": [
          "Games"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Public joinable games, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Lobby"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/mine": {
      "get": {
        "operationId": "getMyGames",
        "tags": [
          "Games"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "finished"
              ],
              "default": "active"
            },
            "required": false,
            "name": "bucket",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The caller's games in the requested bucket",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MyGames"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/{gameId}": {
      "get": {
        "operationId": "getGame",
        "tags": [
          "Games"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "gameId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The game summary — never state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GameSummary"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/players": {
      "get": {
        "operationId": "getPlayers",
        "tags": [
          "Players"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "ids",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Public identity for up to 50 comma-separated user ids",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Players"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/bots": {
      "get": {
        "operationId": "getBots",
        "tags": [
          "Bots"
        ],
        "responses": {
          "200": {
            "description": "The bot catalog",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bots"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/me": {
      "get": {
        "operationId": "getProfile",
        "tags": [
          "Me"
        ],
        "responses": {
          "200": {
            "description": "The caller's own profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Profile"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteAccount",
        "tags": [
          "Me"
        ],
        "responses": {
          "204": {
            "description": "The account and its data were deleted"
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "502": {
            "description": "Deletion failed — the account is intact; retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/me/ratings": {
      "get": {
        "operationId": "getMyRatings",
        "tags": [
          "Me"
        ],
        "responses": {
          "200": {
            "description": "The caller's current rating per pool",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ratings"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/players/{playerId}/games": {
      "get": {
        "operationId": "getPlayerGames",
        "tags": [
          "Players"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "playerId",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "That player's finished public games, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerGames"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/players/{playerId}/ratings": {
      "get": {
        "operationId": "getPlayerRatings",
        "tags": [
          "Players"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "playerId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "That player's current rating per pool",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ratings"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/me/rating-history": {
      "get": {
        "operationId": "getMyRatingHistory",
        "tags": [
          "Me"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "pool",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The caller's rating log, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RatingHistory"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games": {
      "post": {
        "operationId": "createGame",
        "tags": [
          "Games"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateGame"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created game",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Created"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "State conflict — resync and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Assertion mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/solo": {
      "post": {
        "operationId": "createSoloGame",
        "tags": [
          "Games"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSolo"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created-and-started solo game",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SoloStarted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "State conflict — resync and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Assertion mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/{gameId}/join": {
      "post": {
        "operationId": "joinGame",
        "tags": [
          "Games"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "gameId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Join"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Seated — the game's id and the post-join roster",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Joined"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "State conflict — resync and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Assertion mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/join-by-code": {
      "post": {
        "operationId": "joinGameByCode",
        "tags": [
          "Games"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JoinByCode"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Seated — the game's id and the post-join roster",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Joined"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "State conflict — resync and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Assertion mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/{gameId}/leave": {
      "post": {
        "operationId": "leaveGame",
        "tags": [
          "Games"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "gameId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LobbyCommand"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Left — the post-leave roster",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LobbyAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "State conflict — resync and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Assertion mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/{gameId}/cancel": {
      "post": {
        "operationId": "cancelGame",
        "tags": [
          "Games"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "gameId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LobbyCommand"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LobbyAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "State conflict — resync and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Assertion mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/{gameId}/add-bot": {
      "post": {
        "operationId": "addBot",
        "tags": [
          "Games"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "gameId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddBot"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bot seated — the new roster",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LobbyAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "State conflict — resync and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Assertion mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/{gameId}/start": {
      "post": {
        "operationId": "startGame",
        "tags": [
          "Games"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "gameId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LobbyCommand"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Started — version 0 committed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "State conflict — resync and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Assertion mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/{gameId}/action": {
      "post": {
        "operationId": "submitAction",
        "tags": [
          "Games"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "gameId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Action"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Committed — the acting seat's frame",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "State conflict — resync and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Assertion mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/{gameId}/forfeit": {
      "post": {
        "operationId": "forfeitGame",
        "tags": [
          "Games"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "gameId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Forfeit"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Forfeit committed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "State conflict — resync and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Assertion mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/games/{gameId}/frames": {
      "get": {
        "operationId": "getFrames",
        "tags": [
          "Games"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "gameId",
            "in": "path"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0
            },
            "required": false,
            "name": "to",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The projected frames, version-ascending",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Frames"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "State conflict — resync and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Assertion mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/me/username": {
      "put": {
        "operationId": "updateUsername",
        "tags": [
          "Me"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UsernameUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new username",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsernameUpdated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid username",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Username already taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/me/display-name": {
      "put": {
        "operationId": "updateDisplayName",
        "tags": [
          "Me"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DisplayNameUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new display name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisplayNameUpdated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid display name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/me/devices": {
      "put": {
        "operationId": "registerDevice",
        "tags": [
          "Me"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceRegistration"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Registered — the caller's pushes will reach this install"
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/me/devices/{fid}": {
      "delete": {
        "operationId": "unregisterDevice",
        "tags": [
          "Me"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "fid",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Deregistered (idempotent)"
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/friends": {
      "get": {
        "operationId": "listFriends",
        "tags": [
          "Social"
        ],
        "responses": {
          "200": {
            "description": "The caller's accepted friends",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Friends"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/friends/requests": {
      "get": {
        "operationId": "listFriendRequests",
        "tags": [
          "Social"
        ],
        "responses": {
          "200": {
            "description": "Pending requests, incoming and outgoing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FriendRequests"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "sendFriendRequest",
        "tags": [
          "Social"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FriendTarget"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Request sent, or auto-accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FriendRequestResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/friends/games": {
      "get": {
        "operationId": "getFriendsGames",
        "tags": [
          "Social"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ]
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Joinable games created by the caller's friends",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FriendsGames"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/users/search": {
      "get": {
        "operationId": "searchUsers",
        "tags": [
          "Social"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "q",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching registered users, best match first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSearch"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/friends/requests/{userId}/accept": {
      "post": {
        "operationId": "acceptFriendRequest",
        "tags": [
          "Social"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "userId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "The request was accepted"
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/friends/{userId}": {
      "delete": {
        "operationId": "removeFriend",
        "tags": [
          "Social"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "userId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Unfriended / request withdrawn / declined (idempotent)"
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/engine/friends/{userId}/block": {
      "post": {
        "operationId": "blockUser",
        "tags": [
          "Social"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "userId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Blocked (idempotent)"
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "unblockUser",
        "tags": [
          "Social"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "userId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Unblocked (idempotent)"
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/action": {
      "post": {
        "operationId": "botAction",
        "tags": [
          "BotWebhook"
        ],
        "security": [
          {
            "botHmac": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BotAction"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "The move was accepted and applied"
          },
          "400": {
            "description": "Malformed body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Invalid signature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {}
}
