{
  "openapi": "3.1.0",
  "info": {
    "title": "No ManChess API",
    "version": "1.0.0",
    "description": "Contrato público de datos, Bot API y Board API. Phoenix valida identidad, permisos, jugadas, relojes y resultados."
  },
  "servers": [
    {
      "url": "https://nomanchess.com"
    }
  ],
  "tags": [
    {
      "name": "Datos públicos",
      "description": "Lecturas anónimas, acotadas y sin información privada."
    },
    {
      "name": "Bot",
      "description": "Automatización de una cuenta convertida permanentemente en BOT."
    },
    {
      "name": "Tablero externo",
      "description": "Juego de una persona desde un cliente o tablero físico."
    }
  ],
  "paths": {
    "/api/bot/online": {
      "get": {
        "operationId": "botOnline",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Bots conectados en NDJSON",
        "description": "Flujo acotado de cuentas BOT con una conexión API activa; no expone secretos.",
        "parameters": [
          {
            "name": "nb",
            "in": "query",
            "required": false,
            "description": "Máximo de resultados, entre 1 y 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/directory": {
      "get": {
        "operationId": "botDirectory",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Directorio de bots conectados",
        "description": "Adaptador JSON acotado sobre la misma instantánea pública de bots conectados.",
        "parameters": [
          {
            "name": "nb",
            "in": "query",
            "required": false,
            "description": "Máximo de resultados, entre 1 y 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    },
    "/api/profiles/search": {
      "get": {
        "operationId": "profileSearch",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Buscar perfiles públicos",
        "description": "Busca perfiles públicos por nombre de usuario dentro del límite del servicio.",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": false,
            "description": "Texto de búsqueda.",
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/profiles/country-presence": {
      "get": {
        "operationId": "profileCountryPresence",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Presencia por país",
        "description": "Devuelve conteos públicos acotados por código de país.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Cantidad máxima de países.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/profiles/{username}": {
      "get": {
        "operationId": "profileShow",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Consultar perfil público",
        "description": "Devuelve la representación pública de una cuenta sin datos privados.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/profiles/{username}/mini": {
      "get": {
        "operationId": "profileMini",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Consultar perfil compacto",
        "description": "Devuelve la identidad pública mínima para vistas compactas.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/profiles/{username}/rating-history/{time_control}": {
      "get": {
        "operationId": "profileRatingHistory",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Historial de rating",
        "description": "Devuelve una serie acotada para un ritmo de juego válido.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "time_control",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/profiles/{username}/rating-distribution/{time_control}": {
      "get": {
        "operationId": "profileRatingDistribution",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Distribución de rating",
        "description": "Devuelve la distribución pública del ritmo solicitado.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "time_control",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/profiles/{username}/perf/{time_control}": {
      "get": {
        "operationId": "profilePerformance",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Rendimiento por ritmo",
        "description": "Devuelve estadísticas públicas de rendimiento para un ritmo válido.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "time_control",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/profiles/{username}/activity": {
      "get": {
        "operationId": "profileActivity",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Actividad pública",
        "description": "Devuelve la actividad pública visible de la cuenta.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/profiles/{username}/games": {
      "get": {
        "operationId": "profileGames",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Partidas públicas",
        "description": "Lista únicamente partidas públicas con paginación y filtros acotados.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/profiles/{username}/games/export.pgn": {
      "get": {
        "operationId": "profileGamesPgn",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Exportar partidas públicas",
        "description": "Descarga un conjunto acotado de partidas públicas en PGN.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/x-chess-pgn": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/x-chess-pgn": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/x-chess-pgn": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/fide/players": {
      "get": {
        "operationId": "fidePlayers",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Buscar jugadores FIDE",
        "description": "Lista una instantánea oficial, paginada y trazable del directorio FIDE.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Nombre o identificador FIDE exacto.",
            "schema": {
              "type": "string",
              "maxLength": 80
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "description": "Orden del directorio.",
            "schema": {
              "type": "string",
              "enum": [
                "standard",
                "name",
                "rapid",
                "blitz",
                "year"
              ],
              "default": "standard"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Página de 30 resultados.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/fide/players/{id}": {
      "get": {
        "operationId": "fidePlayer",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Consultar jugador FIDE",
        "description": "Devuelve identidad, ratings, historial mensual y fuente oficial sin datos privados.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/fide/federations": {
      "get": {
        "operationId": "fideFederations",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Listar federaciones FIDE",
        "description": "Lista federaciones con jugadores activos y promedios de sus diez mejores ratings.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Página de 30 federaciones.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/fide/federations/{code}": {
      "get": {
        "operationId": "fideFederation",
        "tags": [
          "Datos públicos"
        ],
        "summary": "Consultar federación FIDE",
        "description": "Devuelve estadísticas de la federación y sus jugadores paginados.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "description": "Orden de los jugadores.",
            "schema": {
              "type": "string",
              "enum": [
                "standard",
                "name",
                "rapid",
                "blitz",
                "year"
              ],
              "default": "standard"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Página de 30 resultados.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/account/upgrade": {
      "post": {
        "operationId": "botAccountUpgrade",
        "tags": [
          "Bot"
        ],
        "summary": "Convertir cuenta en BOT",
        "description": "Conversión irreversible para una cuenta humana activa, sin partidas ni privilegios de plataforma.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/status": {
      "get": {
        "operationId": "botStatus",
        "tags": [
          "Bot"
        ],
        "summary": "Comprobar cuenta BOT",
        "description": "Confirma la identidad, el permiso aceptado y las capacidades disponibles.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/game/{id}": {
      "get": {
        "operationId": "botGame",
        "tags": [
          "Bot"
        ],
        "summary": "Consultar partida propia",
        "description": "Devuelve el estado autoritativo de una partida jugada por este BOT.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/game/{id}/stream": {
      "get": {
        "operationId": "botGameStream",
        "tags": [
          "Bot"
        ],
        "summary": "Seguir partida en vivo",
        "description": "Abre un flujo NDJSON con el estado inicial, jugadas, cambios y keep-alives.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/game/{id}/move/{uci}": {
      "post": {
        "operationId": "botMove",
        "tags": [
          "Bot"
        ],
        "summary": "Jugar una jugada",
        "description": "Envía una intención UCI; el backend valida turno, legalidad, reloj y estado antes de persistirla.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "uci",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/game/{id}/abort": {
      "post": {
        "operationId": "botAbort",
        "tags": [
          "Bot"
        ],
        "summary": "Abortar partida",
        "description": "Aborta una partida propia únicamente mientras la regla autoritativa lo permita.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/game/{id}/resign": {
      "post": {
        "operationId": "botResign",
        "tags": [
          "Bot"
        ],
        "summary": "Rendirse",
        "description": "Registra la rendición del jugador autenticado en una partida activa.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/game/{id}/draw/{accept}": {
      "post": {
        "operationId": "botDraw",
        "tags": [
          "Bot"
        ],
        "summary": "Gestionar tablas",
        "description": "Ofrece, acepta, rechaza o cancela tablas mediante el valor booleano indicado.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "accept",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/game/{id}/takeback/{accept}": {
      "post": {
        "operationId": "botTakeback",
        "tags": [
          "Bot"
        ],
        "summary": "Gestionar devolución",
        "description": "Ofrece, acepta, rechaza o cancela una devolución de jugada cuando la partida lo permite.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "accept",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/game/{id}/chat": {
      "get": {
        "operationId": "botChat",
        "tags": [
          "Bot"
        ],
        "summary": "Leer chat de partida",
        "description": "Devuelve únicamente el chat reciente autorizado para el participante.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "botChatSend",
        "tags": [
          "Bot"
        ],
        "summary": "Enviar chat de partida",
        "description": "Publica un mensaje moderado, limitado y asociado al participante autenticado.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/game/{id}/claim-draw": {
      "post": {
        "operationId": "botClaimDraw",
        "tags": [
          "Bot"
        ],
        "summary": "Reclamar tablas",
        "description": "Solicita al backend resolver una reclamación válida de tablas.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/game/{id}/claim-victory": {
      "post": {
        "operationId": "botClaimVictory",
        "tags": [
          "Bot"
        ],
        "summary": "Reclamar victoria",
        "description": "Solicita al backend resolver una victoria reclamable por desconexión.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/challenges": {
      "get": {
        "operationId": "botChallenges",
        "tags": [
          "Bot"
        ],
        "summary": "Listar retos propios",
        "description": "Lista únicamente retos entrantes y salientes del titular del token.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "botChallengeCreate",
        "tags": [
          "Bot"
        ],
        "summary": "Crear reto",
        "description": "Crea un reto compatible con BOT; el backend decide permisos y modalidad.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChallengeRequest"
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "201": {
            "description": "Recurso creado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/challenges/{id}/accept": {
      "post": {
        "operationId": "botChallengeAccept",
        "tags": [
          "Bot"
        ],
        "summary": "Aceptar reto",
        "description": "Acepta un reto entrante compatible y crea la partida autoritativa.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/challenges/{id}/decline": {
      "post": {
        "operationId": "botChallengeDecline",
        "tags": [
          "Bot"
        ],
        "summary": "Rechazar reto",
        "description": "Rechaza un reto entrante que pertenece al titular del token.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/bot/challenges/{id}": {
      "delete": {
        "operationId": "botChallengeCancel",
        "tags": [
          "Bot"
        ],
        "summary": "Cancelar reto",
        "description": "Cancela un reto saliente que pertenece al titular del token.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "bot:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/status": {
      "get": {
        "operationId": "boardStatus",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Comprobar cuenta tablero externo",
        "description": "Confirma la identidad, el permiso aceptado y las capacidades disponibles.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/game/{id}": {
      "get": {
        "operationId": "boardGame",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Consultar partida propia",
        "description": "Devuelve el estado autoritativo de una partida jugada por este tablero externo.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/game/{id}/stream": {
      "get": {
        "operationId": "boardGameStream",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Seguir partida en vivo",
        "description": "Abre un flujo NDJSON con el estado inicial, jugadas, cambios y keep-alives.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/game/{id}/move/{uci}": {
      "post": {
        "operationId": "boardMove",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Jugar una jugada",
        "description": "Envía una intención UCI; el backend valida turno, legalidad, reloj y estado antes de persistirla.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "uci",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/game/{id}/abort": {
      "post": {
        "operationId": "boardAbort",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Abortar partida",
        "description": "Aborta una partida propia únicamente mientras la regla autoritativa lo permita.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/game/{id}/resign": {
      "post": {
        "operationId": "boardResign",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Rendirse",
        "description": "Registra la rendición del jugador autenticado en una partida activa.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/game/{id}/draw/{accept}": {
      "post": {
        "operationId": "boardDraw",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Gestionar tablas",
        "description": "Ofrece, acepta, rechaza o cancela tablas mediante el valor booleano indicado.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "accept",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/game/{id}/takeback/{accept}": {
      "post": {
        "operationId": "boardTakeback",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Gestionar devolución",
        "description": "Ofrece, acepta, rechaza o cancela una devolución de jugada cuando la partida lo permite.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "accept",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/game/{id}/chat": {
      "get": {
        "operationId": "boardChat",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Leer chat de partida",
        "description": "Devuelve únicamente el chat reciente autorizado para el participante.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "boardChatSend",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Enviar chat de partida",
        "description": "Publica un mensaje moderado, limitado y asociado al participante autenticado.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/game/{id}/claim-draw": {
      "post": {
        "operationId": "boardClaimDraw",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Reclamar tablas",
        "description": "Solicita al backend resolver una reclamación válida de tablas.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/game/{id}/claim-victory": {
      "post": {
        "operationId": "boardClaimVictory",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Reclamar victoria",
        "description": "Solicita al backend resolver una victoria reclamable por desconexión.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/challenges": {
      "get": {
        "operationId": "boardChallenges",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Listar retos propios",
        "description": "Lista únicamente retos entrantes y salientes del titular del token.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "boardChallengeCreate",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Crear reto",
        "description": "Crea un reto compatible con tablero externo; el backend decide permisos y modalidad.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChallengeRequest"
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "201": {
            "description": "Recurso creado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/challenges/{id}/accept": {
      "post": {
        "operationId": "boardChallengeAccept",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Aceptar reto",
        "description": "Acepta un reto entrante compatible y crea la partida autoritativa.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/challenges/{id}/decline": {
      "post": {
        "operationId": "boardChallengeDecline",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Rechazar reto",
        "description": "Rechaza un reto entrante que pertenece al titular del token.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/board/challenges/{id}": {
      "delete": {
        "operationId": "boardChallengeCancel",
        "tags": [
          "Tablero externo"
        ],
        "summary": "Cancelar reto",
        "description": "Cancela un reto saliente que pertenece al titular del token.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "x-required-scope": "board:play",
        "responses": {
          "200": {
            "description": "Solicitud completada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Falta un token Bearer válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "El token, scope, tipo de cuenta o permiso no es válido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe o no pertenece al actor autenticado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "El estado actual impide esta acción.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "La intención o sus parámetros no cumplen el contrato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite temporal alcanzado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "nmc_pat_*",
        "description": "Token personal creado en /account/api. El secreto se muestra una sola vez."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "ChallengeRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "challenged_username",
          "time_control",
          "initial_seconds",
          "increment_seconds"
        ],
        "properties": {
          "challenged_username": {
            "type": "string",
            "minLength": 1
          },
          "challenger_color": {
            "type": "string",
            "enum": [
              "auto",
              "white",
              "black"
            ],
            "default": "auto"
          },
          "time_control": {
            "type": "string"
          },
          "initial_seconds": {
            "type": "integer",
            "minimum": 0
          },
          "increment_seconds": {
            "type": "integer",
            "minimum": 0
          },
          "rated": {
            "type": "boolean",
            "default": false
          },
          "is_public": {
            "type": "boolean",
            "default": true
          }
        }
      }
    },
    "responses": {}
  }
}
