{
  "openapi": "3.1.0",
  "info": {
    "title": "My Music Bingo API",
    "version": "1.0.0",
    "description": "Create a music bingo game: send songs, receive player cards and a shareable link. Stateless, no authentication."
  },
  "servers": [
    {
      "url": "https://mymusicbingo.eu"
    }
  ],
  "paths": {
    "/api/bingo": {
      "post": {
        "operationId": "createMusicBingo",
        "summary": "Generate music bingo cards and a shareable link",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BingoRequest"
              },
              "example": {
                "event": "80s party",
                "songs": [
                  "Take On Me - a-ha",
                  "Africa - Toto",
                  "Billie Jean - Michael Jackson",
                  "Girls Just Want to Have Fun - Cyndi Lauper"
                ],
                "rows": 2,
                "cols": 2,
                "free": false,
                "cards": 2,
                "lang": "en"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cards, printable PDF URL and link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BingoResponse"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (for example, not enough songs for the grid)"
          }
        }
      },
      "get": {
        "operationId": "createMusicBingoGet",
        "summary": "Same as POST, with query parameters (songs separated by | or new lines)",
        "parameters": [
          {
            "name": "songs",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rows",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 2,
              "maximum": 9,
              "default": 5
            }
          },
          {
            "name": "cols",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 2,
              "maximum": 9,
              "default": 5
            }
          },
          {
            "name": "free",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "cards",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 60,
              "default": 1
            }
          },
          {
            "name": "event",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "ca",
                "es",
                "fr"
              ],
              "default": "en"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "markdown"
              ],
              "default": "json"
            }
          },
          {
            "name": "seed",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cards and link"
          },
          "422": {
            "description": "Invalid input"
          }
        }
      }
    },
    "/api/bingo-pdf": {
      "get": {
        "operationId": "getMusicBingoPdf",
        "summary": "Printable PDF of the cards (use the pdf_url returned by /api/bingo)",
        "parameters": [
          {
            "name": "d",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Compressed game payload, as included in pdf_url."
          },
          {
            "name": "n",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 60,
              "default": 1
            }
          },
          {
            "name": "seed",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PDF file",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid d"
          },
          "422": {
            "description": "Invalid values"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Song": {
        "oneOf": [
          {
            "type": "string",
            "description": "\"Title - Artist\""
          },
          {
            "type": "object",
            "properties": {
              "title": {
                "type": "string"
              },
              "artist": {
                "type": "string"
              }
            },
            "required": [
              "title"
            ]
          }
        ]
      },
      "BingoRequest": {
        "type": "object",
        "required": [
          "songs"
        ],
        "properties": {
          "songs": {
            "type": "array",
            "maxItems": 300,
            "items": {
              "$ref": "#/components/schemas/Song"
            },
            "description": "At least rows*cols songs (minus 1 when free is true)."
          },
          "rows": {
            "type": "integer",
            "minimum": 2,
            "maximum": 9,
            "default": 5
          },
          "cols": {
            "type": "integer",
            "minimum": 2,
            "maximum": 9,
            "default": 5
          },
          "free": {
            "type": "boolean",
            "default": true,
            "description": "Free space at the centre."
          },
          "cards": {
            "type": "integer",
            "minimum": 0,
            "maximum": 60,
            "default": 1,
            "description": "Number of individual cards to return. 0 returns only the link."
          },
          "event": {
            "type": "string",
            "description": "Game name shown on the cards."
          },
          "lang": {
            "type": "string",
            "enum": [
              "en",
              "ca",
              "es",
              "fr"
            ],
            "default": "en"
          },
          "seed": {
            "type": "integer",
            "description": "Same songs, settings and seed give the same cards. Random if omitted."
          }
        }
      },
      "Cell": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "artist": {
            "type": "string"
          },
          "free": {
            "type": "boolean"
          },
          "label": {
            "type": "string"
          }
        }
      },
      "BingoResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "event": {
            "type": "string"
          },
          "link": {
            "type": "string",
            "format": "uri",
            "description": "Share with players: each person gets their own random card on their phone."
          },
          "cards": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "number": {
                  "type": "integer"
                },
                "grid": {
                  "type": "array",
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Cell"
                    }
                  }
                }
              }
            }
          },
          "play_order": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "artist": {
                  "type": "string"
                }
              }
            }
          },
          "settings": {
            "type": "object"
          },
          "pdf_url": {
            "type": "string",
            "format": "uri",
            "description": "Printable A4 PDF, one card per page, with exactly the cards in \"cards\". Present when cards >= 1."
          },
          "seed": {
            "type": "integer"
          }
        }
      }
    }
  }
}