{
  "openapi": "3.0.0",
  "info": {
    "title": "BrettZone API",
    "version": "2.0.0",
    "description": "Read-only API for accessing BrettZone robot combat data: video feeds, fight information, tournament metadata, bots, highlight clips, live cage state, live streams, schedules, and search. No authentication required. Every request is logged for usage stats (anonymous client hash, no raw IPs are stored). Callers may optionally self-identify with an X-API-Key header (or ?key= query parameter) for usage attribution; see GET /usage.",
    "contact": {
      "name": "BrettZone Support"
    }
  },
  "servers": [
    {
      "url": "https://brettzone.nhrl.io/brettZone/api.php",
      "description": "Production server"
    }
  ],
  "paths": {
    "/tournaments": {
      "get": {
        "summary": "List all tournaments",
        "description": "Returns a list of all public tournaments, excluding private and test tournaments",
        "operationId": "listTournaments",
        "tags": ["Tournaments"],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tournaments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Tournament"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/tournaments/{tournamentID}": {
      "get": {
        "summary": "Get tournament details",
        "description": "Returns detailed information about a specific tournament",
        "operationId": "getTournament",
        "tags": ["Tournaments"],
        "parameters": [
          {
            "name": "tournamentID",
            "in": "path",
            "required": true,
            "description": "The ID of the tournament",
            "schema": {
              "type": "string",
              "example": "nhrl_apr25_12lb"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tournament": {
                      "$ref": "#/components/schemas/Tournament"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Tournament not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tournaments/{tournamentID}/fights": {
      "get": {
        "summary": "List all fights in a tournament",
        "description": "Returns a list of all fights/matches in a specific tournament",
        "operationId": "getTournamentFights",
        "tags": ["Fights"],
        "parameters": [
          {
            "name": "tournamentID",
            "in": "path",
            "required": true,
            "description": "The ID of the tournament",
            "schema": {
              "type": "string",
              "example": "nhrl_apr25_12lb"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fights": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FightSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Tournament not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tournaments/{tournamentID}/fights/{gameID}": {
      "get": {
        "summary": "Get detailed fight information",
        "description": "Returns detailed information about a specific fight, including video recordings from multiple camera angles",
        "operationId": "getFightDetails",
        "tags": ["Fights"],
        "parameters": [
          {
            "name": "tournamentID",
            "in": "path",
            "required": true,
            "description": "The ID of the tournament",
            "schema": {
              "type": "string",
              "example": "nhrl_apr25_12lb"
            }
          },
          {
            "name": "gameID",
            "in": "path",
            "required": true,
            "description": "The ID of the game/fight",
            "schema": {
              "type": "string",
              "example": "W-14"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fight": {
                      "$ref": "#/components/schemas/FightDetails"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Fight not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tournaments/{tournamentID}/players": {
      "get": {
        "summary": "List all players in a tournament",
        "description": "Returns a list of all players/robots registered in a specific tournament",
        "operationId": "getTournamentPlayers",
        "tags": ["Players"],
        "parameters": [
          {
            "name": "tournamentID",
            "in": "path",
            "required": true,
            "description": "The ID of the tournament",
            "schema": {
              "type": "string",
              "example": "nhrl_apr25_12lb"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "players": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Player"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Tournament not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tournaments/{tournamentID}/players/{playerID}": {
      "get": {
        "summary": "Get player details",
        "description": "Returns detailed information about a specific player/robot, including their match history",
        "operationId": "getPlayerDetails",
        "tags": ["Players"],
        "parameters": [
          {
            "name": "tournamentID",
            "in": "path",
            "required": true,
            "description": "The ID of the tournament",
            "schema": {
              "type": "string",
              "example": "nhrl_apr25_12lb"
            }
          },
          {
            "name": "playerID",
            "in": "path",
            "required": true,
            "description": "The ID of the player",
            "schema": {
              "type": "string",
              "example": "91773e54cbb444e8"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "player": {
                      "$ref": "#/components/schemas/PlayerDetails"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Player not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tournaments/{tournamentID}/brackets": {
      "get": {
        "summary": "Get tournament bracket structure",
        "description": "Returns the complete bracket structure for a tournament, including all games organized by bracket type (Winners, Losers, Exhibition) with slot information for bracket connections",
        "operationId": "getTournamentBrackets",
        "tags": ["Tournaments"],
        "parameters": [
          {
            "name": "tournamentID",
            "in": "path",
            "required": true,
            "description": "The ID of the tournament",
            "schema": {
              "type": "string",
              "example": "nhrl_apr25_12lb"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "brackets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Bracket"
                      }
                    },
                    "players": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Player"
                      }
                    },
                    "format": {
                      "type": "object",
                      "description": "Tournament format information from TrueFinals"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Tournament not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/fights": {
      "get": {
        "summary": "Get all fights with pagination",
        "description": "Returns a paginated list of all fights across all public tournaments",
        "operationId": "getAllFights",
        "tags": ["Fights"],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of results per page (max 100)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fights": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Fight"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/fights/recent": {
      "get": {
        "summary": "Get recent fights across all tournaments",
        "description": "Returns a list of recent fights from all public tournaments",
        "operationId": "getRecentFights",
        "tags": ["Fights"],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of fights to return (max 100)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fights": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RecentFight"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/stats/fight/{tournamentID}/{gameID}": {
      "get": {
        "summary": "Get detailed fight stats",
        "description": "Returns detailed statistics for a specific fight including timing, pause info, judges scorecard, and per-player stats",
        "operationId": "getFightStats",
        "tags": ["Stats"],
        "parameters": [
          {
            "name": "tournamentID",
            "in": "path",
            "required": true,
            "description": "The ID of the tournament",
            "schema": { "type": "string", "example": "nhrl_mar26_12lb" }
          },
          {
            "name": "gameID",
            "in": "path",
            "required": true,
            "description": "The ID of the game/fight",
            "schema": { "type": "string", "example": "W-1" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fightStats": { "$ref": "#/components/schemas/FightStats" }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Fight not found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/stats/bot/{botName}": {
      "get": {
        "summary": "Get bot career stats",
        "description": "Returns aggregate career statistics for a bot across all public tournaments",
        "operationId": "getBotStats",
        "tags": ["Stats"],
        "parameters": [
          {
            "name": "botName",
            "in": "path",
            "required": true,
            "description": "The bot's clean name (lowercase, alphanumeric only)",
            "schema": { "type": "string", "example": "void" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "botStats": { "$ref": "#/components/schemas/BotStats" }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Bot not found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/stats/tournament/{tournamentID}": {
      "get": {
        "summary": "Get tournament aggregate stats",
        "description": "Returns aggregate statistics for an entire tournament including fight counts, durations, win methods, and pause counts",
        "operationId": "getTournamentStats",
        "tags": ["Stats"],
        "parameters": [
          {
            "name": "tournamentID",
            "in": "path",
            "required": true,
            "description": "The ID of the tournament",
            "schema": { "type": "string", "example": "nhrl_mar26_12lb" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tournamentStats": { "$ref": "#/components/schemas/TournamentStats" }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Tournament not found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/stats/head-to-head/{bot1}/{bot2}": {
      "get": {
        "summary": "Get head-to-head record between two bots",
        "description": "Returns the complete head-to-head record between two bots across all public tournaments",
        "operationId": "getHeadToHead",
        "tags": ["Stats"],
        "parameters": [
          {
            "name": "bot1",
            "in": "path",
            "required": true,
            "description": "First bot's clean name",
            "schema": { "type": "string", "example": "void" }
          },
          {
            "name": "bot2",
            "in": "path",
            "required": true,
            "description": "Second bot's clean name",
            "schema": { "type": "string", "example": "cherri" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "headToHead": { "$ref": "#/components/schemas/HeadToHead" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/stats/leaderboard": {
      "get": {
        "summary": "Get leaderboard rankings",
        "description": "Returns ranked lists of bots by various performance metrics",
        "operationId": "getLeaderboard",
        "tags": ["Stats"],
        "parameters": [
          {
            "name": "metric",
            "in": "query",
            "description": "Ranking metric: wins, winRate, koRate, avgMatchDuration",
            "schema": {
              "type": "string",
              "enum": ["wins", "winRate", "koRate", "avgMatchDuration"],
              "default": "wins"
            }
          },
          {
            "name": "weightClass",
            "in": "query",
            "description": "Filter by weight class in pounds",
            "schema": { "type": "integer", "example": 12 }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of results (max 100)",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "leaderboard": { "$ref": "#/components/schemas/Leaderboard" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/video-feeds/{recordingID}": {
      "get": {
        "summary": "Get video feed information",
        "description": "Returns detailed information about a specific video recording, including URLs for different quality levels",
        "operationId": "getVideoFeed",
        "tags": ["Video Feeds"],
        "parameters": [
          {
            "name": "recordingID",
            "in": "path",
            "required": true,
            "description": "The ID of the recording",
            "schema": {
              "type": "integer",
              "example": 31036
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "videoFeed": {
                      "$ref": "#/components/schemas/VideoFeed"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Video feed not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/video-feeds/fight/{tournamentID}/{gameID}": {
      "get": {
        "summary": "Get every camera angle for a fight",
        "description": "Returns all available video recordings (camera angles) for a specific fight, plus fightStartOffset: the number of seconds into each recording file at which the fight actually starts.",
        "operationId": "getFightVideoFeeds",
        "tags": ["Video Feeds"],
        "parameters": [
          {
            "name": "tournamentID",
            "in": "path",
            "required": true,
            "description": "The ID of the tournament",
            "schema": { "type": "string", "example": "nhrl_apr25_12lb" }
          },
          {
            "name": "gameID",
            "in": "path",
            "required": true,
            "description": "The ID of the game/fight",
            "schema": { "type": "string", "example": "W-14" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "gameID": { "type": "string", "example": "W-14" },
                    "tournamentID": { "type": "string", "example": "nhrl_apr25_12lb" },
                    "fightStartOffset": {
                      "type": "number",
                      "nullable": true,
                      "description": "Seconds into the recording files at which the fight starts (null when it cannot be derived)",
                      "example": 21.6
                    },
                    "recordings": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Recording" }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Tournament or fight not found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/fights/last24h": {
      "get": {
        "summary": "Get fights from the last 24 hours",
        "description": "Returns every public fight that ended within the last 24 hours, each with a low-resolution preview video URL. Safe to poll every 5 seconds.",
        "operationId": "getFightsLast24Hours",
        "tags": ["Fights"],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fights": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "gameID": { "type": "string", "example": "W-14" },
                          "tournamentID": { "type": "string", "example": "nhrl_apr25_12lb" },
                          "player1": { "type": "string", "example": "Void" },
                          "player2": { "type": "string", "example": "Cherri" },
                          "tournamentName": { "type": "string", "example": "NHRL April 2025 12lb" },
                          "WeightClass": { "type": "integer", "example": 12 },
                          "stopTime": { "type": "integer", "example": 1743904303 },
                          "proxy72": { "type": "string", "nullable": true, "description": "72p preview video URL" }
                        }
                      }
                    },
                    "count": { "type": "integer", "example": 12 }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/bots": {
      "get": {
        "summary": "List all bots",
        "description": "Returns every bot that has competed in a public tournament, with career record, tournament count, and weight classes. Supports name filtering and pagination.",
        "operationId": "listBots",
        "tags": ["Bots"],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Filter by bot name (matched against the clean name, alphanumeric only)",
            "schema": { "type": "string", "example": "void" }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number (1-based)",
            "schema": { "type": "integer", "minimum": 1, "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of results per page (max 200)",
            "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bots": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/BotSummary" }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/bots/{botName}": {
      "get": {
        "summary": "Get bot career profile",
        "description": "Returns the bot's full career profile across all public tournaments (same payload as /stats/bot/{botName}).",
        "operationId": "getBot",
        "tags": ["Bots"],
        "parameters": [
          {
            "name": "botName",
            "in": "path",
            "required": true,
            "description": "The bot's clean name (lowercase, alphanumeric only)",
            "schema": { "type": "string", "example": "void" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bot": { "$ref": "#/components/schemas/BotStats" }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Bot not found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/bots/{botName}/fights": {
      "get": {
        "summary": "Get every fight for a bot",
        "description": "Returns every public fight the bot has participated in, newest first, including camera counts for each fight.",
        "operationId": "getBotFights",
        "tags": ["Bots"],
        "parameters": [
          {
            "name": "botName",
            "in": "path",
            "required": true,
            "description": "The bot's clean name (lowercase, alphanumeric only)",
            "schema": { "type": "string", "example": "void" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "botName": { "type": "string", "example": "void" },
                    "fights": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/FightSummary" }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Bot not found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/clips": {
      "get": {
        "summary": "List highlight clips",
        "description": "Returns completed public highlight clips, newest first, with video URLs at multiple resolutions plus view and like counts.",
        "operationId": "listClips",
        "tags": ["Clips"],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number (1-based)",
            "schema": { "type": "integer", "minimum": 1, "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of results per page (max 100)",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "clips": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Clip" }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clips/{publicToken}": {
      "get": {
        "summary": "Get a single clip",
        "description": "Returns one highlight clip by its public share token.",
        "operationId": "getClip",
        "tags": ["Clips"],
        "parameters": [
          {
            "name": "publicToken",
            "in": "path",
            "required": true,
            "description": "The clip's public share token",
            "schema": { "type": "string", "example": "598ccdb2902db2d53f4d68293b7b3edc" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "clip": { "$ref": "#/components/schemas/Clip" }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Clip not found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/cages": {
      "get": {
        "summary": "Get live state of every cage",
        "description": "Returns the real-time state of every cage: the active (or last completed) match, both players, the queue, and cage status. Keys are cage1, cage2, cage4, cage5, cage6.",
        "operationId": "getCages",
        "tags": ["Live"],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cages": {
                      "type": "object",
                      "additionalProperties": { "$ref": "#/components/schemas/CageState" }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/cages/{cageNum}": {
      "get": {
        "summary": "Get live state of one cage",
        "description": "Returns the real-time state of a single cage.",
        "operationId": "getCage",
        "tags": ["Live"],
        "parameters": [
          {
            "name": "cageNum",
            "in": "path",
            "required": true,
            "description": "Cage number (1, 2, 4, 5, or 6)",
            "schema": { "type": "integer", "example": 1 }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cage": { "$ref": "#/components/schemas/CageState" }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid cage number",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/live/streams": {
      "get": {
        "summary": "List live streams",
        "description": "Returns every currently broadcasting stream from the live streaming server, grouped by location (Cage 1, Pits, SkyCam, ...), each with a ready-to-play HLS URL.",
        "operationId": "getLiveStreams",
        "tags": ["Live"],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "groups": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "array",
                        "items": { "$ref": "#/components/schemas/LiveStream" }
                      }
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "Live streaming server unreachable",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/schedule": {
      "get": {
        "summary": "Get upcoming scheduled matches",
        "description": "Returns upcoming matches for active tournaments, ordered by scheduled time, with per-match status (In Cage, Load In, Called, Available, Starting Soon, Delayed).",
        "operationId": "getSchedule",
        "tags": ["Live"],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "matches": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/ScheduledMatch" }
                    },
                    "count": { "type": "integer", "example": 8 }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/search": {
      "get": {
        "summary": "Search bots, tournaments, and fights",
        "description": "Full-site search. Single-token queries match bots and tournaments; two-token queries (e.g. \"void cherri\") also match fights between the two named bots.",
        "operationId": "search",
        "tags": ["Search"],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search query (minimum 2 characters)",
            "schema": { "type": "string", "example": "void cherri" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SearchResults" }
              }
            }
          },
          "400": {
            "description": "Missing or too-short query",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/usage": {
      "get": {
        "summary": "Get API usage stats",
        "description": "Returns aggregate usage stats for this API: request totals, unique clients, error rates, response times, per-day and per-endpoint breakdowns, status code distribution, and top (anonymized) consumers.",
        "operationId": "getUsage",
        "tags": ["Usage"],
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "description": "Window size in days (1-365)",
            "schema": { "type": "integer", "minimum": 1, "maximum": 365, "default": 30 }
          },
          {
            "name": "endpoint",
            "in": "query",
            "description": "Filter to a single route template, e.g. /tournaments/{tournamentID}/fights",
            "schema": { "type": "string", "example": "/fights/recent" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "usage": { "$ref": "#/components/schemas/UsageStats" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/openapi": {
      "get": {
        "summary": "Get this OpenAPI specification",
        "description": "Returns the OpenAPI 3.0 document describing this API.",
        "operationId": "getOpenApiSpec",
        "tags": ["Usage"],
        "responses": {
          "200": {
            "description": "The OpenAPI 3.0 specification"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Tournament": {
        "type": "object",
        "properties": {
          "tournamentID": {
            "type": "string",
            "description": "Unique identifier for the tournament",
            "example": "nhrl_apr25_12lb"
          },
          "tournamentName": {
            "type": "string",
            "description": "Display name of the tournament",
            "example": "NHRL April 2025 12lb"
          },
          "createTime": {
            "type": "string",
            "format": "date-time",
            "description": "When the tournament was created"
          },
          "scheduledStartTime": {
            "type": "string",
            "format": "date-time",
            "description": "Scheduled start time of the tournament"
          },
          "startTime": {
            "type": "string",
            "format": "date-time",
            "description": "Actual start time of the tournament"
          },
          "endTime": {
            "type": "string",
            "format": "date-time",
            "description": "End time of the tournament"
          },
          "updateTime": {
            "type": "string",
            "format": "date-time",
            "description": "Last update time"
          },
          "numPlayers": {
            "type": "integer",
            "description": "Number of players/robots in the tournament",
            "example": 30
          },
          "WeightClass": {
            "type": "integer",
            "description": "Weight class in pounds",
            "example": 12
          },
          "active": {
            "type": "integer",
            "description": "Whether the tournament is currently active (0 or 1)",
            "example": 0
          },
          "isTest": {
            "type": "integer",
            "description": "Whether this is a test tournament (0 or 1)",
            "example": 0
          },
          "isFreestyle": {
            "type": "integer",
            "description": "Whether this is a freestyle tournament (0 or 1)",
            "example": 0
          },
          "privacy": {
            "type": "string",
            "description": "Privacy setting of the tournament",
            "example": "public"
          }
        }
      },
      "FightSummary": {
        "type": "object",
        "properties": {
          "tournamentID": {
            "type": "string",
            "example": "nhrl_apr25_12lb"
          },
          "id": {
            "type": "string",
            "description": "Fight/game ID",
            "example": "W-14"
          },
          "name": {
            "type": "string",
            "description": "Fight name",
            "example": "W-14"
          },
          "round": {
            "type": "integer",
            "description": "Round number (0-based, -1 for exhibition)",
            "example": 3
          },
          "cage": {
            "type": "string",
            "description": "Cage/location name",
            "example": "Cage 1"
          },
          "player1": {
            "type": "string",
            "description": "Name of player 1 (red corner)",
            "example": "Void"
          },
          "player1clean": {
            "type": "string",
            "description": "Clean name of player 1 (for URLs)",
            "example": "void"
          },
          "player2": {
            "type": "string",
            "description": "Name of player 2 (blue corner)",
            "example": "Cherri"
          },
          "player2clean": {
            "type": "string",
            "description": "Clean name of player 2 (for URLs)",
            "example": "cherri"
          },
          "player1wins": {
            "type": "integer",
            "description": "Whether player 1 won (0 or 1)",
            "example": 0
          },
          "player2wins": {
            "type": "integer",
            "description": "Whether player 2 won (0 or 1)",
            "example": 1
          },
          "cams": {
            "type": "integer",
            "description": "Number of camera angles available",
            "example": 16
          },
          "winAnnotation": {
            "type": "string",
            "description": "Type of win (KO, JD, TKO, etc.)",
            "example": "JD"
          },
          "startTime": {
            "type": "integer",
            "description": "Unix timestamp of fight start",
            "example": 1743904123
          },
          "stopTime": {
            "type": "integer",
            "description": "Unix timestamp of fight stop",
            "example": 1743904303
          },
          "endTime": {
            "type": "integer",
            "description": "Unix timestamp of fight end",
            "example": 1743904671045
          },
          "matchLength": {
            "type": "integer",
            "description": "Duration of the match in seconds",
            "example": 180
          },
          "weightClass": {
            "type": "integer",
            "description": "Weight class in pounds",
            "example": 12
          },
          "tournamentName": {
            "type": "string",
            "example": "NHRL April 2025 12lb"
          }
        }
      },
      "FightDetails": {
        "type": "object",
        "properties": {
          "player1": {
            "$ref": "#/components/schemas/FightPlayer"
          },
          "player2": {
            "$ref": "#/components/schemas/FightPlayer"
          },
          "tournament": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "example": "NHRL April 2025 12lb"
              },
              "numPlayers": {
                "type": "string",
                "example": "30"
              },
              "WeightClass": {
                "type": "string",
                "example": "12"
              },
              "active": {
                "type": "string",
                "example": "0"
              },
              "isTest": {
                "type": "string",
                "example": "0"
              },
              "isFreestyle": {
                "type": "string",
                "example": "0"
              },
              "privacy": {
                "type": "string",
                "example": "public"
              }
            }
          },
          "recordings": {
            "type": "array",
            "description": "Array of video recordings from different camera angles",
            "items": {
              "$ref": "#/components/schemas/Recording"
            }
          },
          "notice": {
            "type": "string",
            "description": "Any notices about the fight"
          }
        }
      },
      "FightPlayer": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "example": "Void"
          },
          "gameID": {
            "type": "string",
            "example": "W-14"
          },
          "tournamentID": {
            "type": "string",
            "example": "nhrl_apr25_12lb"
          },
          "slotIdx": {
            "type": "string",
            "example": "0"
          },
          "playerID": {
            "type": "string",
            "example": "91773e54cbb444e8"
          },
          "prevGameID": {
            "type": "string",
            "example": "W-12"
          },
          "score": {
            "type": "string",
            "example": "0"
          },
          "isWinner": {
            "type": "string",
            "example": "0"
          },
          "isReady": {
            "type": "string",
            "example": "0"
          },
          "cleanName": {
            "type": "string",
            "example": "void"
          },
          "seed": {
            "type": "string",
            "example": "1"
          },
          "wins": {
            "type": "string",
            "example": "3"
          },
          "losses": {
            "type": "string",
            "example": "1"
          },
          "isBye": {
            "type": "string",
            "example": "0"
          },
          "isDisqualified": {
            "type": "string",
            "example": "0"
          },
          "round": {
            "type": "string",
            "example": "3"
          },
          "scoreToWin": {
            "type": "string",
            "example": "1"
          },
          "state": {
            "type": "string",
            "example": "done"
          },
          "winAnnotation": {
            "type": "string",
            "example": "JD"
          },
          "loserPlacement": {
            "type": "string",
            "example": "2"
          },
          "startTime": {
            "type": "string",
            "example": "1743904123"
          },
          "stopTime": {
            "type": "string",
            "example": "1743904303"
          },
          "duration": {
            "type": "string",
            "example": "180"
          },
          "judgesString": {
            "type": "string",
            "example": "Void weapon full<br>Void drive partial<br>Cherri weapon disabled<br>Cherri drive partial"
          },
          "roundName": {
            "type": "string",
            "example": "Final"
          }
        }
      },
      "Recording": {
        "type": "object",
        "properties": {
          "recordingID": {
            "type": "string",
            "example": "31036"
          },
          "gameID": {
            "type": "string",
            "example": "W-14"
          },
          "tournamentID": {
            "type": "string",
            "example": "nhrl_apr25_12lb"
          },
          "cage": {
            "type": "string",
            "example": "1"
          },
          "camera": {
            "type": "string",
            "description": "Camera angle name",
            "example": "SkyCam"
          },
          "s3path": {
            "type": "string",
            "description": "Full quality video URL",
            "example": "https://nhrl-matches.us-east-1.linodeobjects.com/streams/SkyCam-2025-04-06_01-48-15.591.mp4"
          },
          "s3http": {
            "type": "string",
            "description": "HTTP status code",
            "example": "200"
          },
          "proxy720": {
            "type": "string",
            "description": "720p proxy video URL",
            "example": "https://nhrl-matches.us-east-1.linodeobjects.com/proxy/SkyCam-2025-04-06_01-48-15.591_720p.mp4"
          },
          "proxy360": {
            "type": "string",
            "description": "360p proxy video URL",
            "example": "https://nhrl-matches.us-east-1.linodeobjects.com/proxy/SkyCam-2025-04-06_01-48-15.591_360p.mp4"
          },
          "proxy72": {
            "type": "string",
            "description": "72p proxy video URL",
            "example": "https://nhrl-matches.us-east-1.linodeobjects.com/proxy/SkyCam-2025-04-06_01-48-15.591_72p.mp4"
          }
        }
      },
      "Player": {
        "type": "object",
        "properties": {
          "playerID": {
            "type": "string",
            "example": "91773e54cbb444e8"
          },
          "name": {
            "type": "string",
            "description": "Display name of the robot/player",
            "example": "Void"
          },
          "cleanName": {
            "type": "string",
            "description": "URL-safe name",
            "example": "void"
          },
          "seed": {
            "type": "integer",
            "description": "Tournament seed",
            "example": 1
          },
          "wins": {
            "type": "integer",
            "description": "Number of wins",
            "example": 3
          },
          "losses": {
            "type": "integer",
            "description": "Number of losses",
            "example": 1
          },
          "isBye": {
            "type": "integer",
            "description": "Whether this is a bye player (0 or 1)",
            "example": 0
          },
          "isDisqualified": {
            "type": "integer",
            "description": "Whether the player is disqualified (0 or 1)",
            "example": 0
          },
          "lastPlayTime": {
            "type": "integer",
            "description": "Unix timestamp of last match",
            "example": 1743904671045
          },
          "mostRecentGameID": {
            "type": "string",
            "description": "ID of most recent game",
            "example": "W-14"
          }
        }
      },
      "PlayerDetails": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Player"
          },
          {
            "type": "object",
            "properties": {
              "gameIDs": {
                "type": "array",
                "description": "List of game IDs the player participated in",
                "items": {
                  "type": "string"
                },
                "example": ["W-4", "W-8", "W-12", "W-14"]
              }
            }
          }
        ]
      },
      "RecentFight": {
        "type": "object",
        "properties": {
          "tournamentID": {
            "type": "string",
            "example": "nhrl_apr25_12lb"
          },
          "gameID": {
            "type": "string",
            "example": "W-14"
          },
          "name": {
            "type": "string",
            "example": "W-14"
          },
          "round": {
            "type": "integer",
            "example": 3
          },
          "cage": {
            "type": "string",
            "example": "Cage 1"
          },
          "player1": {
            "type": "string",
            "example": "Void"
          },
          "player2": {
            "type": "string",
            "example": "Cherri"
          },
          "player1wins": {
            "type": "integer",
            "example": 0
          },
          "player2wins": {
            "type": "integer",
            "example": 1
          },
          "winAnnotation": {
            "type": "string",
            "example": "JD"
          },
          "startTime": {
            "type": "integer",
            "example": 1743904123
          },
          "stopTime": {
            "type": "integer",
            "example": 1743904303
          },
          "matchLength": {
            "type": "integer",
            "example": 180
          },
          "tournamentName": {
            "type": "string",
            "example": "NHRL April 2025 12lb"
          },
          "WeightClass": {
            "type": "integer",
            "example": 12
          }
        }
      },
      "VideoFeed": {
        "type": "object",
        "properties": {
          "recordingID": {
            "type": "integer",
            "example": 31036
          },
          "gameID": {
            "type": "string",
            "example": "W-14"
          },
          "tournamentID": {
            "type": "string",
            "example": "nhrl_apr25_12lb"
          },
          "cage": {
            "type": "string",
            "example": "1"
          },
          "camera": {
            "type": "string",
            "example": "SkyCam"
          },
          "s3path": {
            "type": "string",
            "description": "Original video URL",
            "example": "https://nhrl-matches.us-east-1.linodeobjects.com/streams/SkyCam-2025-04-06_01-48-15.591.mp4"
          },
          "s3http": {
            "type": "integer",
            "description": "HTTP status code",
            "example": 200
          },
          "red": {
            "type": "string",
            "description": "Red corner player name",
            "example": "Void"
          },
          "blue": {
            "type": "string",
            "description": "Blue corner player name",
            "example": "Cherri"
          },
          "result": {
            "type": "string",
            "description": "Fight result",
            "example": "Blue"
          },
          "matchStart": {
            "type": "integer",
            "description": "Unix timestamp of match start",
            "example": 1743904123
          },
          "videoQualities": {
            "type": "object",
            "description": "URLs for different video quality levels",
            "properties": {
              "original": {
                "type": "string",
                "description": "Original quality video URL"
              },
              "proxy720": {
                "type": "string",
                "description": "720p proxy video URL"
              },
              "proxy360": {
                "type": "string",
                "description": "360p proxy video URL"
              },
              "proxy72": {
                "type": "string",
                "description": "72p proxy video URL"
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Error message"
          }
        }
      },
      "Bracket": {
        "type": "object",
        "properties": {
          "bracketID": {
            "type": "string",
            "description": "Bracket identifier (W, L, EX, etc.)",
            "example": "W"
          },
          "bracketName": {
            "type": "string",
            "description": "Human-readable bracket name",
            "example": "Winners Bracket"
          },
          "games": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BracketGame"
            }
          }
        }
      },
      "BracketGame": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Game ID",
            "example": "W-14"
          },
          "name": {
            "type": "string",
            "description": "Game name",
            "example": "W-14"
          },
          "round": {
            "type": "integer",
            "description": "Round number",
            "example": 3
          },
          "scoreToWin": {
            "type": "integer",
            "description": "Score needed to win",
            "example": 1
          },
          "state": {
            "type": "string",
            "description": "Game state (pending, active, done)",
            "example": "done"
          },
          "winAnnotation": {
            "type": "string",
            "description": "Type of win",
            "example": "JD"
          },
          "loserPlacement": {
            "type": "integer",
            "nullable": true,
            "description": "Placement of the loser",
            "example": 2
          },
          "startTime": {
            "type": "integer",
            "description": "Unix timestamp of game start",
            "example": 1743904123
          },
          "stopTime": {
            "type": "integer",
            "description": "Unix timestamp of game stop",
            "example": 1743904303
          },
          "slots": {
            "type": "array",
            "description": "Player slots in the game",
            "items": {
              "$ref": "#/components/schemas/GameSlot"
            }
          }
        }
      },
      "GameSlot": {
        "type": "object",
        "properties": {
          "slotIdx": {
            "type": "integer",
            "description": "Slot index (0 or 1)",
            "example": 0
          },
          "playerID": {
            "type": "string",
            "description": "Player ID",
            "example": "91773e54cbb444e8"
          },
          "prevGameID": {
            "type": "string",
            "nullable": true,
            "description": "Previous game ID (for bracket connections)",
            "example": "W-12"
          },
          "score": {
            "type": "string",
            "description": "Player's score",
            "example": "0"
          },
          "isWinner": {
            "type": "integer",
            "description": "Whether this player won (0 or 1)",
            "example": 1
          },
          "slotState": {
            "type": "string",
            "description": "State of the slot (winner, loser, pending)",
            "example": "winner"
          }
        }
      },
      "Fight": {
        "type": "object",
        "properties": {
          "tournamentID": {
            "type": "string",
            "example": "nhrl_apr25_12lb"
          },
          "id": {
            "type": "string",
            "description": "Game ID",
            "example": "W-14"
          },
          "round": {
            "type": "integer",
            "example": 3
          },
          "cage": {
            "type": "string",
            "example": "Cage 1"
          },
          "player1": {
            "type": "string",
            "example": "Void"
          },
          "player1clean": {
            "type": "string",
            "example": "void"
          },
          "player2": {
            "type": "string",
            "example": "Cherri"
          },
          "player2clean": {
            "type": "string",
            "example": "cherri"
          },
          "player1wins": {
            "type": "integer",
            "example": 0
          },
          "player2wins": {
            "type": "integer",
            "example": 1
          },
          "winAnnotation": {
            "type": "string",
            "example": "JD"
          },
          "startTime": {
            "type": "integer",
            "example": 1743904123
          },
          "stopTime": {
            "type": "integer",
            "example": 1743904303
          },
          "matchLength": {
            "type": "integer",
            "example": 180
          },
          "tournamentName": {
            "type": "string",
            "example": "NHRL April 2025 12lb"
          },
          "WeightClass": {
            "type": "integer",
            "example": 12
          },
          "cams": {
            "type": "integer",
            "description": "Number of camera angles available",
            "example": 16
          }
        }
      },
      "FightStats": {
        "type": "object",
        "properties": {
          "gameID": { "type": "string", "example": "W-1" },
          "tournamentID": { "type": "string", "example": "nhrl_mar26_12lb" },
          "tournamentName": { "type": "string", "example": "NHRL March 2026 12lb" },
          "gameName": { "type": "string", "example": "W-1" },
          "weightClass": { "type": "integer", "example": 12 },
          "round": { "type": "integer", "example": 0 },
          "roundName": { "type": "string", "nullable": true, "example": "Round 1" },
          "cage": { "type": "string", "nullable": true, "example": "Cage 1" },
          "state": { "type": "string", "example": "done" },
          "scoreToWin": { "type": "integer", "example": 1 },
          "startTime": { "type": "integer", "description": "Unix timestamp", "example": 1743904123 },
          "stopTime": { "type": "integer", "description": "Unix timestamp", "example": 1743904303 },
          "matchDurationSeconds": { "type": "integer", "description": "Actual duration (stopTime - startTime)", "example": 180 },
          "timerDuration": { "type": "integer", "description": "Timer/clock duration from game system", "example": 180 },
          "countdown": { "type": "integer", "description": "Countdown timer value", "example": 0 },
          "wasPaused": { "type": "boolean", "description": "Whether the fight was paused", "example": false },
          "winMethod": { "type": "string", "nullable": true, "description": "Win annotation (KO, JD, TKO, etc.)", "example": "KO" },
          "judgesScorecard": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "bot": { "type": "string", "example": "Void" },
                "category": { "type": "string", "example": "weapon" },
                "rating": { "type": "string", "example": "full" }
              }
            }
          },
          "judgesStringRaw": { "type": "string", "nullable": true },
          "cameraCount": { "type": "integer", "example": 16 },
          "players": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "slotIdx": { "type": "integer", "example": 0 },
                "playerID": { "type": "string" },
                "name": { "type": "string", "example": "Void" },
                "cleanName": { "type": "string", "example": "void" },
                "seed": { "type": "integer", "example": 1 },
                "score": { "type": "string", "example": "1" },
                "isWinner": { "type": "integer", "example": 1 },
                "winsInTournament": { "type": "integer", "example": 3 },
                "lossesInTournament": { "type": "integer", "example": 1 },
                "unstickTime": { "type": "integer", "example": 0 },
                "showMotionTime": { "type": "integer", "example": 0 }
              }
            }
          }
        }
      },
      "BotStats": {
        "type": "object",
        "properties": {
          "botName": { "type": "string", "example": "Void" },
          "cleanName": { "type": "string", "example": "void" },
          "totalWins": { "type": "integer", "example": 15 },
          "totalLosses": { "type": "integer", "example": 3 },
          "totalFights": { "type": "integer", "example": 18 },
          "winRate": { "type": "number", "example": 0.8333 },
          "totalTournaments": { "type": "integer", "example": 4 },
          "weightClasses": { "type": "array", "items": { "type": "integer" }, "example": [12] },
          "winMethodBreakdown": {
            "type": "object",
            "additionalProperties": { "type": "integer" },
            "example": {"KO": 8, "JD": 5, "TKO": 2}
          },
          "durationStats": {
            "type": "object",
            "description": "All fight duration statistics grouped together",
            "properties": {
              "averageSeconds": { "type": "number", "description": "Mean fight duration", "example": 95.3 },
              "medianSeconds": { "type": "number", "description": "Median fight duration", "example": 88.0 },
              "totalFightTimeSeconds": { "type": "integer", "description": "Sum of all fight durations", "example": 1715 },
              "fightsMeasured": { "type": "integer", "description": "Number of fights with valid timing data", "example": 18 },
              "longestFight": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "gameID": { "type": "string" },
                  "tournamentID": { "type": "string" },
                  "tournamentName": { "type": "string" },
                  "opponent": { "type": "string", "nullable": true },
                  "durationSeconds": { "type": "integer" }
                }
              },
              "shortestFight": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "gameID": { "type": "string" },
                  "tournamentID": { "type": "string" },
                  "tournamentName": { "type": "string" },
                  "opponent": { "type": "string", "nullable": true },
                  "durationSeconds": { "type": "integer" }
                }
              }
            }
          },
          "avgMatchDurationSeconds": { "type": "number", "description": "Same as durationStats.averageSeconds (kept for backwards compatibility)", "example": 95.3 },
          "longestFight": {
            "type": "object",
            "nullable": true,
            "description": "Same as durationStats.longestFight (kept for backwards compatibility)",
            "properties": {
              "gameID": { "type": "string" },
              "tournamentID": { "type": "string" },
              "tournamentName": { "type": "string" },
              "opponent": { "type": "string", "nullable": true },
              "durationSeconds": { "type": "integer" }
            }
          },
          "shortestFight": {
            "type": "object",
            "nullable": true,
            "description": "Same as durationStats.shortestFight (kept for backwards compatibility)",
            "properties": {
              "gameID": { "type": "string" },
              "tournamentID": { "type": "string" },
              "tournamentName": { "type": "string" },
              "opponent": { "type": "string", "nullable": true },
              "durationSeconds": { "type": "integer" }
            }
          },
          "recentFights": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/BotFightSummary" }
          }
        }
      },
      "BotFightSummary": {
        "type": "object",
        "properties": {
          "gameID": { "type": "string" },
          "tournamentID": { "type": "string" },
          "tournamentName": { "type": "string" },
          "weightClass": { "type": "integer" },
          "opponent": { "type": "string", "nullable": true },
          "opponentClean": { "type": "string", "nullable": true },
          "won": { "type": "boolean" },
          "winMethod": { "type": "string", "nullable": true },
          "durationSeconds": { "type": "integer" },
          "wasPaused": { "type": "boolean" },
          "cage": { "type": "string", "nullable": true }
        }
      },
      "TournamentStats": {
        "type": "object",
        "properties": {
          "tournamentID": { "type": "string", "example": "nhrl_mar26_12lb" },
          "tournamentName": { "type": "string", "example": "NHRL March 2026 12lb" },
          "weightClass": { "type": "integer", "example": 12 },
          "numPlayers": { "type": "integer", "example": 30 },
          "totalFights": { "type": "integer", "example": 29 },
          "completedFights": { "type": "integer", "example": 29 },
          "avgMatchDurationSeconds": { "type": "number", "example": 98.5 },
          "winMethodBreakdown": {
            "type": "object",
            "additionalProperties": { "type": "integer" },
            "example": {"KO": 12, "JD": 10, "TKO": 5}
          },
          "totalPausedFights": { "type": "integer", "example": 3 },
          "longestFight": {
            "type": "object",
            "nullable": true,
            "properties": {
              "gameID": { "type": "string" },
              "player1": { "type": "string" },
              "player2": { "type": "string" },
              "durationSeconds": { "type": "integer" }
            }
          },
          "shortestFight": {
            "type": "object",
            "nullable": true,
            "properties": {
              "gameID": { "type": "string" },
              "player1": { "type": "string" },
              "player2": { "type": "string" },
              "durationSeconds": { "type": "integer" }
            }
          }
        }
      },
      "HeadToHead": {
        "type": "object",
        "properties": {
          "bot1": { "type": "string", "example": "void" },
          "bot2": { "type": "string", "example": "cherri" },
          "bot1Name": { "type": "string", "example": "Void" },
          "bot2Name": { "type": "string", "example": "Cherri" },
          "totalMeetings": { "type": "integer", "example": 2 },
          "bot1Wins": { "type": "integer", "example": 1 },
          "bot2Wins": { "type": "integer", "example": 1 },
          "draws": { "type": "integer", "example": 0 },
          "meetings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "gameID": { "type": "string" },
                "tournamentID": { "type": "string" },
                "tournamentName": { "type": "string" },
                "weightClass": { "type": "integer" },
                "winner": { "type": "string", "nullable": true },
                "winMethod": { "type": "string", "nullable": true },
                "bot1Score": { "type": "string" },
                "bot2Score": { "type": "string" },
                "durationSeconds": { "type": "integer" },
                "wasPaused": { "type": "boolean" },
                "cage": { "type": "string", "nullable": true }
              }
            }
          }
        }
      },
      "Leaderboard": {
        "type": "object",
        "properties": {
          "metric": { "type": "string", "example": "wins" },
          "weightClass": { "type": "integer", "nullable": true, "example": 12 },
          "entries": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/LeaderboardEntry" }
          }
        }
      },
      "LeaderboardEntry": {
        "type": "object",
        "properties": {
          "rank": { "type": "integer", "example": 1 },
          "botName": { "type": "string", "example": "Void" },
          "cleanName": { "type": "string", "example": "void" },
          "totalWins": { "type": "integer", "description": "Present for wins and winRate metrics" },
          "totalLosses": { "type": "integer", "description": "Present for wins and winRate metrics" },
          "tournaments": { "type": "integer", "description": "Present for wins and winRate metrics" },
          "winRate": { "type": "number", "description": "Present for winRate metric" },
          "totalFightsWon": { "type": "integer", "description": "Present for koRate metric" },
          "koWins": { "type": "integer", "description": "Present for koRate metric" },
          "koRate": { "type": "number", "description": "Present for koRate metric" },
          "totalFights": { "type": "integer", "description": "Present for avgMatchDuration metric" },
          "avgMatchDurationSeconds": { "type": "number", "description": "Present for avgMatchDuration metric" }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "description": "Total number of records",
            "example": 5000
          },
          "page": {
            "type": "integer",
            "description": "Current page number",
            "example": 1
          },
          "pages": {
            "type": "integer",
            "description": "Total number of pages",
            "example": 50
          },
          "limit": {
            "type": "integer",
            "description": "Number of records per page",
            "example": 100
          }
        }
      },
      "BotSummary": {
        "type": "object",
        "properties": {
          "cleanName": { "type": "string", "example": "void" },
          "name": { "type": "string", "example": "Void" },
          "totalWins": { "type": "integer", "example": 15 },
          "totalLosses": { "type": "integer", "example": 3 },
          "totalFights": { "type": "integer", "example": 18 },
          "tournaments": { "type": "integer", "example": 4 },
          "weightClasses": { "type": "array", "items": { "type": "integer" }, "example": [12] }
        }
      },
      "Clip": {
        "type": "object",
        "properties": {
          "publicToken": { "type": "string", "description": "Public share token, used as the clip ID", "example": "598ccdb2902db2d53f4d68293b7b3edc" },
          "name": { "type": "string", "example": "Massive uppercut KO" },
          "description": { "type": "string", "nullable": true },
          "gameID": { "type": "string", "nullable": true, "example": "W-5" },
          "tournamentID": { "type": "string", "nullable": true, "example": "nhrl_june25_30lb" },
          "tournamentName": { "type": "string", "nullable": true, "example": "NHRL June 2025 30lb" },
          "player1": { "type": "string", "nullable": true, "example": "Red Storm" },
          "player2": { "type": "string", "nullable": true, "example": "Killswitch" },
          "camera": { "type": "string", "example": "SkyCam-B" },
          "aspectRatio": { "type": "string", "example": "16:9" },
          "isMulticlip": { "type": "integer", "description": "1 when the clip is stitched from multiple segments", "example": 0 },
          "durationSeconds": { "type": "number", "nullable": true, "example": 16.71 },
          "speed": { "type": "number", "description": "Playback speed multiplier", "example": 1.0 },
          "videos": {
            "type": "object",
            "description": "Video URLs at each available resolution (null when not rendered)",
            "properties": {
              "original": { "type": "string", "nullable": true },
              "proxy720": { "type": "string", "nullable": true },
              "proxy360": { "type": "string", "nullable": true },
              "proxy72": { "type": "string", "nullable": true }
            }
          },
          "viewCount": { "type": "integer", "example": 1024 },
          "likeCount": { "type": "integer", "example": 42 },
          "robotTag": { "type": "string", "nullable": true, "description": "Clean name of the featured bot, when tagged" },
          "sourceType": { "type": "string", "example": "native" },
          "createdAt": { "type": "integer", "description": "Unix timestamp", "example": 1743904303 },
          "shareUrl": { "type": "string", "description": "Public share page for the clip" }
        }
      },
      "CageState": {
        "type": "object",
        "description": "Real-time state of one cage. player1/player2 describe the active match (or the last completed one when lastMatch is true).",
        "properties": {
          "cageNum": { "type": "integer", "example": 1 },
          "status": { "type": "string", "description": "Cage status string (e.g. Judge Review)", "example": "" },
          "lastMatch": { "type": "boolean", "description": "True when no match is active and the payload shows the last completed match" },
          "player1": { "type": "object", "description": "Slot 0 player joined with game, tournament, and round data" },
          "player2": { "type": "object", "description": "Slot 1 player joined with game, tournament, and round data" },
          "queue": { "type": "string", "description": "Comma-separated upcoming game IDs" },
          "tournamentID": { "type": "string", "example": "nhrl_apr25_12lb" },
          "notice": { "type": "string", "description": "Set when multiple brackets have an active match in the same cage" }
        }
      },
      "LiveStream": {
        "type": "object",
        "properties": {
          "streamId": { "type": "string", "example": "Cage-1-Blue-High" },
          "name": { "type": "string", "example": "Cage 1 Blue High" },
          "status": { "type": "string", "example": "broadcasting" },
          "group": { "type": "string", "example": "Cage 1" },
          "hlsUrl": { "type": "string", "description": "HLS playlist URL, playable in any HLS-capable player", "example": "https://live.nhrl.io:5443/LiveApp/streams/Cage-1-Blue-High.m3u8" }
        }
      },
      "ScheduledMatch": {
        "type": "object",
        "properties": {
          "game_id": { "type": "string", "example": "W-14" },
          "tournament_id": { "type": "string", "example": "nhrl_apr25_12lb" },
          "tournament_name": { "type": "string", "example": "NHRL April 2025 12lb" },
          "scheduled_time": { "type": "integer", "description": "Unix timestamp in milliseconds (0 when unscheduled)", "example": 1743904123000 },
          "red_team": { "type": "string", "example": "Void" },
          "blue_team": { "type": "string", "example": "Cherri" },
          "location": { "type": "string", "example": "Cage 1" },
          "status": { "type": "string", "enum": ["In Cage", "Load In", "Called", "Available", "Starting Soon", "Delayed"], "example": "In Cage" },
          "weight_class": { "type": "string", "example": "12lb" },
          "is_active": { "type": "integer", "example": 1 }
        }
      },
      "SearchResults": {
        "type": "object",
        "properties": {
          "q": { "type": "string", "example": "void cherri" },
          "bots": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string", "example": "Void" },
                "cleanName": { "type": "string", "example": "void" }
              }
            }
          },
          "tournaments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "tournamentID": { "type": "string" },
                "tournamentName": { "type": "string" },
                "weightClass": { "type": "integer" },
                "active": { "type": "integer" }
              }
            }
          },
          "fights": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "gameID": { "type": "string" },
                "tournamentID": { "type": "string" },
                "player1": { "type": "string" },
                "player2": { "type": "string" },
                "tournamentName": { "type": "string" },
                "weightClass": { "type": "integer" },
                "stopTime": { "type": "integer" }
              }
            }
          }
        }
      },
      "UsageStats": {
        "type": "object",
        "properties": {
          "windowDays": { "type": "integer", "example": 30 },
          "since": { "type": "string", "format": "date-time", "description": "Start of the reporting window" },
          "endpointFilter": { "type": "string", "nullable": true, "example": null },
          "summary": {
            "type": "object",
            "properties": {
              "requests": { "type": "integer", "example": 15234 },
              "uniqueClients": { "type": "integer", "description": "Distinct anonymous client hashes", "example": 87 },
              "avgResponseMs": { "type": "number", "example": 42.5 },
              "totalResponseBytes": { "type": "integer", "example": 104857600 },
              "errorCount": { "type": "integer", "example": 120 },
              "errorRate": { "type": "number", "example": 0.0079 }
            }
          },
          "byDay": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": { "type": "string", "example": "2026-07-06" },
                "requests": { "type": "integer" },
                "errors": { "type": "integer" },
                "avgResponseMs": { "type": "number" }
              }
            }
          },
          "byEndpoint": {
            "type": "array",
            "description": "Top 25 endpoints by request count",
            "items": {
              "type": "object",
              "properties": {
                "endpoint": { "type": "string", "example": "/fights/recent" },
                "requests": { "type": "integer" },
                "errors": { "type": "integer" },
                "avgResponseMs": { "type": "number" }
              }
            }
          },
          "byStatus": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "httpStatus": { "type": "integer", "example": 200 },
                "requests": { "type": "integer" }
              }
            }
          },
          "topClients": {
            "type": "array",
            "description": "Top 10 consumers by anonymous client hash (md5 of IP + user agent; no raw IPs stored)",
            "items": {
              "type": "object",
              "properties": {
                "clientHash": { "type": "string" },
                "requests": { "type": "integer" }
              }
            }
          },
          "topApiKeys": {
            "type": "array",
            "description": "Top 10 self-identified callers (X-API-Key header or ?key=)",
            "items": {
              "type": "object",
              "properties": {
                "apiKey": { "type": "string" },
                "requests": { "type": "integer" }
              }
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Tournaments",
      "description": "Operations related to tournaments"
    },
    {
      "name": "Fights",
      "description": "Operations related to fights/matches"
    },
    {
      "name": "Players",
      "description": "Operations related to players/robots"
    },
    {
      "name": "Video Feeds",
      "description": "Operations related to video recordings"
    },
    {
      "name": "Stats",
      "description": "Fight stats, bot career stats, tournament aggregates, head-to-head records, and leaderboards"
    },
    {
      "name": "Bots",
      "description": "Bot index, career profiles, and per-bot fight history"
    },
    {
      "name": "Clips",
      "description": "User-created highlight clips"
    },
    {
      "name": "Live",
      "description": "Real-time cage state, live streams, and the match schedule"
    },
    {
      "name": "Search",
      "description": "Full-site search across bots, tournaments, and fights"
    },
    {
      "name": "Usage",
      "description": "API usage stats and machine-readable documentation"
    }
  ]
} 