{
  "openapi": "3.1.0",
  "info": {
    "title": "NOOB-TOWN Arcade Topscore API",
    "version": "1.0.0",
    "description": "Public read endpoints and a rate-limited submit endpoint for NOOB-TOWN Arcade game topscores."
  },
  "servers": [
    {
      "url": "https://noob-town.de"
    }
  ],
  "paths": {
    "/api/topscores": {
      "get": {
        "summary": "List current top score per game",
        "description": "Returns an object keyed by game id with the highest known score for each game.",
        "responses": {
          "200": {
            "description": "Top scores by game id",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "integer",
                    "minimum": 0
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topscore": {
      "get": {
        "summary": "Get the top score for one game",
        "parameters": [
          {
            "name": "game",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]{2,32}$"
            },
            "description": "Stable game id from the public game catalog."
          }
        ],
        "responses": {
          "200": {
            "description": "Top score details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "score": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0
                    },
                    "updated_at": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "score",
                    "updated_at"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid game id"
          }
        }
      },
      "post": {
        "summary": "Submit a score for one game",
        "description": "Accepts JSON only. Submissions are validated, capped per game and rate-limited by a daily IP hash.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "game": {
                    "type": "string",
                    "pattern": "^[a-z0-9-]{2,32}$"
                  },
                  "score": {
                    "type": "number",
                    "minimum": 0
                  }
                },
                "required": [
                  "game",
                  "score"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Submission accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "isNewRecord": {
                      "type": "boolean"
                    },
                    "topScore": {
                      "type": "integer",
                      "minimum": 0
                    }
                  },
                  "required": [
                    "ok",
                    "isNewRecord",
                    "topScore"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid game or score"
          },
          "413": {
            "description": "Payload too large"
          },
          "415": {
            "description": "Unsupported media type"
          },
          "429": {
            "description": "Too many submissions"
          }
        }
      }
    }
  }
}