{
  "openapi": "3.1.0",
  "info": {
    "title": "MudVerse API",
    "version": "1.0.0",
    "description": "Read-only access to MudVerse game listings, rankings, reviews, connection details, tags, MSSP snapshots, and player history."
  },
  "servers": [
    {
      "url": "https://www.mudverse.com/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    { "name": "Games", "description": "Game discovery and details" },
    { "name": "MSSP", "description": "Latest MUD Server Status Protocol data" },
    { "name": "Statistics", "description": "Historical player-count samples" },
    { "name": "Taxonomy", "description": "Filterable MudVerse tag groups" }
  ],
  "paths": {
    "/games": {
      "get": {
        "operationId": "listGames",
        "summary": "List and search games",
        "description": "Search, filter, sort, and paginate active games. Archived listings are excluded from results and pagination totals. Web-only listings are not connection-tested, so connection_type=web_only cannot be combined with online_recently=1 or sort=recently_online.",
        "tags": ["Games"],
        "parameters": [
          { "$ref": "#/components/parameters/Search" },
          { "$ref": "#/components/parameters/Sort" },
          { "$ref": "#/components/parameters/ConnectionType" },
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/PerPage" },
          { "$ref": "#/components/parameters/HasReviews" },
          { "$ref": "#/components/parameters/RecentlyUpdated" },
          { "$ref": "#/components/parameters/OnlineRecently" },
          { "$ref": "#/components/parameters/GameSizeTags" },
          { "$ref": "#/components/parameters/RoleplayingTags" },
          { "$ref": "#/components/parameters/CodebaseTags" },
          { "$ref": "#/components/parameters/PlayCountTags" },
          { "$ref": "#/components/parameters/LocationTags" },
          { "$ref": "#/components/parameters/ThemeTags" },
          { "$ref": "#/components/parameters/TypeTags" },
          { "$ref": "#/components/parameters/LanguageTags" },
          { "$ref": "#/components/parameters/PlayerKillingTags" }
        ],
        "responses": {
          "200": {
            "description": "A page of matching game summaries.",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" },
              "X-Request-ID": { "$ref": "#/components/headers/RequestId" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GameCollection" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "422": { "$ref": "#/components/responses/InvalidFilter" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/games/{gameId}": {
      "get": {
        "operationId": "getGame",
        "summary": "Get game details",
        "description": "Retrieve a game by MudVerse listing ID. Archived listings remain available and identify their state in the status object.",
        "tags": ["Games"],
        "parameters": [{ "$ref": "#/components/parameters/GameId" }],
        "responses": {
          "200": {
            "description": "The requested game.",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" },
              "X-Request-ID": { "$ref": "#/components/headers/RequestId" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": { "data": { "$ref": "#/components/schemas/GameDetail" } }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/games/{gameId}/mssp": {
      "get": {
        "operationId": "getLatestMssp",
        "summary": "Get the latest MSSP snapshot",
        "tags": ["MSSP"],
        "parameters": [{ "$ref": "#/components/parameters/GameId" }],
        "responses": {
          "200": {
            "description": "The latest snapshot, or null when the game has no MSSP data.",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" },
              "X-Request-ID": { "$ref": "#/components/headers/RequestId" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MsspResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/games/{gameId}/players": {
      "get": {
        "operationId": "getPlayerHistory",
        "summary": "Get player-count history",
        "tags": ["Statistics"],
        "parameters": [
          { "$ref": "#/components/parameters/GameId" },
          {
            "name": "granularity",
            "in": "query",
            "description": "Daily rounded averages or individual crawler samples.",
            "schema": { "type": "string", "enum": ["daily", "raw"], "default": "daily" }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Inclusive UTC start date. Defaults to a 30-day range ending on `to`.",
            "schema": { "type": "string", "format": "date" }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Inclusive UTC end date. Defaults to the current UTC date.",
            "schema": { "type": "string", "format": "date" }
          }
        ],
        "responses": {
          "200": {
            "description": "Chronological player-count samples.",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" },
              "X-Request-ID": { "$ref": "#/components/headers/RequestId" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PlayerHistoryResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "422": { "$ref": "#/components/responses/InvalidRange" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/tags": {
      "get": {
        "operationId": "listTags",
        "summary": "List tag groups and values",
        "tags": ["Taxonomy"],
        "responses": {
          "200": {
            "description": "The current MudVerse tag taxonomy.",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" },
              "X-Request-ID": { "$ref": "#/components/headers/RequestId" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": {
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/TagGroup" } }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key generated from the API tab on a MudVerse account."
      }
    },
    "parameters": {
      "GameId": {
        "name": "gameId",
        "in": "path",
        "required": true,
        "description": "MudVerse game listing ID.",
        "schema": { "type": "integer", "minimum": 1 }
      },
      "Search": {
        "name": "q",
        "in": "query",
        "description": "Text to search for in game listings.",
        "schema": { "type": "string" }
      },
      "Sort": {
        "name": "sort",
        "in": "query",
        "description": "Result ordering. Unknown values fall back to last_updated. recently_online cannot be used with connection_type=web_only.",
        "schema": {
          "type": "string",
          "enum": ["top_voted", "newest", "oldest", "last_updated", "recently_online", "most_reviewed"],
          "default": "last_updated"
        }
      },
      "ConnectionType": {
        "name": "connection_type",
        "in": "query",
        "description": "Connection model filter. all applies no filter; web_only returns website-only games; mud_client returns games that support MUD clients.",
        "schema": {
          "type": "string",
          "enum": ["all", "web_only", "mud_client"],
          "default": "all"
        }
      },
      "Page": {
        "name": "page",
        "in": "query",
        "description": "One-based page number.",
        "schema": { "type": "integer", "minimum": 1, "default": 1 }
      },
      "PerPage": {
        "name": "per_page",
        "in": "query",
        "description": "Number of games per page.",
        "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 }
      },
      "HasReviews": {
        "name": "has_reviews",
        "in": "query",
        "description": "Set to 1 to require at least one review.",
        "schema": { "type": "integer", "enum": [1] }
      },
      "RecentlyUpdated": {
        "name": "recently_updated",
        "in": "query",
        "description": "Set to 1 for listings updated in the last 30 days.",
        "schema": { "type": "integer", "enum": [1] }
      },
      "OnlineRecently": {
        "name": "online_recently",
        "in": "query",
        "description": "Set to 1 for games reached successfully in the last 48 hours. Cannot be used with connection_type=web_only.",
        "schema": { "type": "integer", "enum": [1] }
      },
      "GameSizeTags": { "name": "game_size_tag_id[]", "in": "query", "description": "Repeat this bracketed parameter for one or more game-size tag IDs.", "style": "form", "explode": true, "schema": { "$ref": "#/components/schemas/TagIdFilter" } },
      "RoleplayingTags": { "name": "rp_status_tag_id[]", "in": "query", "description": "Repeat this bracketed parameter for one or more roleplaying-status tag IDs.", "style": "form", "explode": true, "schema": { "$ref": "#/components/schemas/TagIdFilter" } },
      "CodebaseTags": { "name": "codebase_tag_id[]", "in": "query", "description": "Repeat this bracketed parameter for one or more codebase tag IDs.", "style": "form", "explode": true, "schema": { "$ref": "#/components/schemas/TagIdFilter" } },
      "PlayCountTags": { "name": "playcount_tag_id[]", "in": "query", "description": "Repeat this bracketed parameter for one or more play-count tag IDs.", "style": "form", "explode": true, "schema": { "$ref": "#/components/schemas/TagIdFilter" } },
      "LocationTags": { "name": "location_tag_id[]", "in": "query", "description": "Repeat this bracketed parameter for one or more location tag IDs.", "style": "form", "explode": true, "schema": { "$ref": "#/components/schemas/TagIdFilter" } },
      "ThemeTags": { "name": "theme_tag_id[]", "in": "query", "description": "Repeat this bracketed parameter for one or more theme tag IDs.", "style": "form", "explode": true, "schema": { "$ref": "#/components/schemas/TagIdFilter" } },
      "TypeTags": { "name": "type_tag_id[]", "in": "query", "description": "Repeat this bracketed parameter for one or more game-type tag IDs.", "style": "form", "explode": true, "schema": { "$ref": "#/components/schemas/TagIdFilter" } },
      "LanguageTags": { "name": "language_tag_id[]", "in": "query", "description": "Repeat this bracketed parameter for one or more language tag IDs.", "style": "form", "explode": true, "schema": { "$ref": "#/components/schemas/TagIdFilter" } },
      "PlayerKillingTags": { "name": "player_killing_tag_id[]", "in": "query", "description": "Repeat this bracketed parameter for one or more player-killing policy tag IDs.", "style": "form", "explode": true, "schema": { "$ref": "#/components/schemas/TagIdFilter" } }
    },
    "headers": {
      "RateLimit": {
        "description": "Maximum requests allowed in the active rate-limit window.",
        "schema": { "type": "integer" }
      },
      "RateLimitRemaining": {
        "description": "Requests remaining in the active rate-limit window.",
        "schema": { "type": "integer" }
      },
      "RateLimitReset": {
        "description": "Unix timestamp when the active rate-limit window resets.",
        "schema": { "type": "integer" }
      },
      "RequestId": {
        "description": "Request identifier for troubleshooting.",
        "schema": { "type": "string" }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "The Bearer API key is missing, malformed, invalid, or revoked.",
        "headers": {
          "WWW-Authenticate": {
            "description": "Bearer authentication challenge.",
            "schema": { "type": "string", "const": "Bearer realm=\"MudVerse API\"" }
          }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
      },
      "NotFound": {
        "description": "The route or game was not found.",
        "headers": {
          "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimit" },
          "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
          "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" },
          "X-Request-ID": { "$ref": "#/components/headers/RequestId" }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
      },
      "MethodNotAllowed": {
        "description": "Only GET is supported.",
        "headers": {
          "Allow": { "schema": { "type": "string", "const": "GET" } },
          "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimit" },
          "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
          "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" },
          "X-Request-ID": { "$ref": "#/components/headers/RequestId" }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
      },
      "InvalidRange": {
        "description": "The player-history granularity or date range is invalid.",
        "headers": {
          "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimit" },
          "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
          "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" },
          "X-Request-ID": { "$ref": "#/components/headers/RequestId" }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
      },
      "InvalidFilter": {
        "description": "A game collection filter value or combination is invalid.",
        "headers": {
          "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimit" },
          "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
          "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" },
          "X-Request-ID": { "$ref": "#/components/headers/RequestId" }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
      },
      "RateLimited": {
        "description": "The per-minute or daily rate limit was exceeded.",
        "headers": {
          "Retry-After": { "description": "Seconds until the binding limit resets.", "schema": { "type": "integer" } },
          "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimit" },
          "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
          "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" },
          "X-Request-ID": { "$ref": "#/components/headers/RequestId" }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
      },
      "InternalError": {
        "description": "An unexpected server error occurred.",
        "headers": {
          "X-Request-ID": { "$ref": "#/components/headers/RequestId" }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
      }
    },
    "schemas": {
      "TagIdFilter": {
        "type": "array",
        "description": "One or more positive tag IDs, encoded using a repeated bracketed query parameter.",
        "minItems": 1,
        "uniqueItems": true,
        "items": { "type": "integer", "minimum": 1 }
      },
      "NullableString": { "type": ["string", "null"] },
      "NullableInteger": { "type": ["integer", "null"] },
      "NullableNumber": { "type": ["number", "null"] },
      "NullableTimestamp": { "type": ["string", "null"], "format": "date-time" },
      "TagReference": {
        "type": "object",
        "required": ["id", "name"],
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" }
        }
      },
      "GameUrls": {
        "type": "object",
        "required": ["self", "mudverse", "website", "banner", "play"],
        "properties": {
          "self": { "type": "string", "format": "uri" },
          "mudverse": { "type": "string", "format": "uri" },
          "website": { "$ref": "#/components/schemas/NullableString" },
          "banner": { "type": "string", "format": "uri" },
          "play": { "$ref": "#/components/schemas/NullableString" },
          "mssp": { "type": "string", "format": "uri" },
          "players": { "type": "string", "format": "uri" }
        }
      },
      "Connection": {
        "type": "object",
        "required": ["host", "port", "tls_port"],
        "properties": {
          "host": { "$ref": "#/components/schemas/NullableString" },
          "port": { "$ref": "#/components/schemas/NullableInteger" },
          "tls_port": { "$ref": "#/components/schemas/NullableInteger" },
          "mssp_port": { "$ref": "#/components/schemas/NullableInteger" }
        }
      },
      "Ranking": {
        "type": "object",
        "required": ["rank", "monthly_votes"],
        "properties": {
          "rank": { "$ref": "#/components/schemas/NullableInteger" },
          "monthly_votes": { "type": "integer", "minimum": 0 }
        }
      },
      "ReviewSummary": {
        "type": "object",
        "required": ["count", "average_rating", "rating_count"],
        "properties": {
          "count": { "type": "integer", "minimum": 0 },
          "average_rating": { "$ref": "#/components/schemas/NullableNumber" },
          "rating_count": { "type": "integer", "minimum": 0 }
        }
      },
      "GameTags": {
        "type": "object",
        "required": ["categories", "custom"],
        "properties": {
          "categories": {
            "type": "object",
            "additionalProperties": { "$ref": "#/components/schemas/TagReference" }
          },
          "custom": { "type": "array", "items": { "$ref": "#/components/schemas/TagReference" } }
        }
      },
      "GameDates": {
        "type": "object",
        "required": ["created", "listed", "updated"],
        "properties": {
          "created": { "$ref": "#/components/schemas/NullableTimestamp" },
          "listed": { "$ref": "#/components/schemas/NullableTimestamp" },
          "updated": { "$ref": "#/components/schemas/NullableTimestamp" }
        }
      },
      "GameStatus": {
        "type": "object",
        "required": ["archived", "archive_reason", "web_only", "last_crawled", "last_successful_connect", "confirmed_online", "latest_players", "mssp_collected_at"],
        "properties": {
          "archived": { "type": "boolean", "description": "True when the listing is archived and excluded from game collections." },
          "archive_reason": { "type": ["string", "null"], "enum": ["non_connectivity", null], "description": "Why the listing is archived, or null for active listings." },
          "web_only": { "type": "boolean", "description": "True when players must use the game's website instead of a MUD client connection." },
          "last_crawled": { "$ref": "#/components/schemas/NullableTimestamp" },
          "last_successful_connect": { "$ref": "#/components/schemas/NullableTimestamp" },
          "confirmed_online": { "type": "boolean" },
          "latest_players": { "$ref": "#/components/schemas/NullableInteger" },
          "mssp_collected_at": { "$ref": "#/components/schemas/NullableTimestamp" }
        }
      },
      "GameSummary": {
        "type": "object",
        "required": ["id", "name", "intro", "urls", "connection", "ranking", "reviews", "tags", "dates", "status"],
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "intro": { "type": "string" },
          "urls": { "$ref": "#/components/schemas/GameUrls" },
          "connection": { "$ref": "#/components/schemas/Connection" },
          "ranking": { "$ref": "#/components/schemas/Ranking" },
          "reviews": { "$ref": "#/components/schemas/ReviewSummary" },
          "tags": { "$ref": "#/components/schemas/GameTags" },
          "dates": { "$ref": "#/components/schemas/GameDates" },
          "status": { "$ref": "#/components/schemas/GameStatus" }
        }
      },
      "GameDetailUrls": {
        "allOf": [
          { "$ref": "#/components/schemas/GameUrls" },
          {
            "type": "object",
            "required": ["mssp", "players"],
            "properties": {
              "mssp": { "type": "string", "format": "uri" },
              "players": { "type": "string", "format": "uri" }
            }
          }
        ]
      },
      "GameDetailConnection": {
        "allOf": [
          { "$ref": "#/components/schemas/Connection" },
          {
            "type": "object",
            "required": ["mssp_port"],
            "properties": {
              "mssp_port": { "$ref": "#/components/schemas/NullableInteger" }
            }
          }
        ]
      },
      "GameDetail": {
        "allOf": [
          { "$ref": "#/components/schemas/GameSummary" },
          {
            "type": "object",
            "required": ["description", "watcher_count", "urls", "connection"],
            "properties": {
              "description": { "type": "string" },
              "watcher_count": { "type": "integer", "minimum": 0 },
              "urls": { "$ref": "#/components/schemas/GameDetailUrls" },
              "connection": { "$ref": "#/components/schemas/GameDetailConnection" }
            }
          }
        ]
      },
      "PaginationMeta": {
        "type": "object",
        "required": ["page", "per_page", "total"],
        "properties": {
          "page": { "type": "integer", "minimum": 1 },
          "per_page": { "type": "integer", "minimum": 1, "maximum": 50 },
          "total": { "type": "integer", "minimum": 0 }
        }
      },
      "PaginationLinks": {
        "type": "object",
        "required": ["self"],
        "properties": {
          "self": { "type": "string", "format": "uri" },
          "next": { "type": "string", "format": "uri" },
          "previous": { "type": "string", "format": "uri" }
        }
      },
      "GameCollection": {
        "type": "object",
        "required": ["data", "meta", "links"],
        "properties": {
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/GameSummary" } },
          "meta": { "$ref": "#/components/schemas/PaginationMeta" },
          "links": { "$ref": "#/components/schemas/PaginationLinks" }
        }
      },
      "MsspSnapshot": {
        "type": "object",
        "required": ["collected_at", "values"],
        "properties": {
          "collected_at": { "$ref": "#/components/schemas/NullableTimestamp" },
          "values": {
            "type": ["object", "null"],
            "description": "Normalized MSSP keys and values. Available keys vary by game.",
            "additionalProperties": true
          }
        }
      },
      "MsspResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": { "oneOf": [{ "$ref": "#/components/schemas/MsspSnapshot" }, { "type": "null" }] },
          "meta": {
            "type": "object",
            "required": ["game_id", "status"],
            "properties": {
              "game_id": { "type": "integer" },
              "status": { "type": "string", "enum": ["available", "mssp_not_available"] }
            }
          }
        }
      },
      "PlayerSample": {
        "type": "object",
        "required": ["recorded_at", "players"],
        "properties": {
          "recorded_at": { "type": "string", "format": "date-time" },
          "players": { "type": "integer", "minimum": 0 }
        }
      },
      "PlayerHistoryResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/PlayerSample" } },
          "meta": {
            "type": "object",
            "required": ["game_id", "granularity", "from", "to"],
            "properties": {
              "game_id": { "type": "integer" },
              "granularity": { "type": "string", "enum": ["daily", "raw"] },
              "from": { "type": "string", "format": "date" },
              "to": { "type": "string", "format": "date" }
            }
          }
        }
      },
      "TagGroup": {
        "type": "object",
        "required": ["id", "name", "values"],
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "values": { "type": "array", "items": { "$ref": "#/components/schemas/TagReference" } }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["error", "request_id"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "authentication_required",
                  "invalid_api_key",
                  "route_not_found",
                  "game_not_found",
                  "method_not_allowed",
                  "invalid_filter",
                  "invalid_date_range",
                  "rate_limit_exceeded",
                  "internal_error"
                ]
              },
              "message": { "type": "string" },
              "details": true
            }
          },
          "request_id": { "type": "string" }
        }
      }
    }
  }
}
