openapi: 3.0.3 info: title: NowChess API description: | REST API for the NowChess application. Designed to feel familiar to users of the [lichess API](https://lichess.org/api). ## Authentication Most endpoints require a Bearer token: ``` Authorization: Bearer ``` Authentication is reserved for future implementation — endpoints are currently open unless noted otherwise. ## Move notation Moves are expressed in **UCI notation**: `{from}{to}[promotion]` - Normal move: `e2e4` - Capture: `d5e6` - Promotion: `e7e8q` (q=queen, r=rook, b=bishop, n=knight) - Castling: `e1g1` (kingside white), `e1c1` (queenside white) ## Streaming Endpoints that support streaming return **NDJSON** (newline-delimited JSON). Request them with: ``` Accept: application/x-ndjson ``` Each line of the response is a complete JSON object. Empty lines are keep-alive heartbeats. ## Rate limiting Requests that exceed the rate limit receive `429 Too Many Requests`. Honour the `Retry-After` response header and wait before retrying. version: 1.0.0 contact: name: NowChess license: name: MIT servers: - url: http://localhost:8080 description: Local development server tags: - name: game description: Create and manage chess games - name: move description: Make moves and navigate game history - name: draw description: Draw offers and claims - name: import description: Load a game from FEN or PGN - name: export description: Export a game as FEN or PGN paths: # --------------------------------------------------------------------------- # Game lifecycle # --------------------------------------------------------------------------- /api/board/game: post: operationId: createGame tags: [game] summary: Create a new game description: | Creates a new chess game starting from the initial position. Returns the full game state including the generated `gameId`. security: - bearerAuth: [] requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/CreateGameRequest' responses: '201': description: Game created content: application/json: schema: $ref: '#/components/schemas/GameFull' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '429': $ref: '#/components/responses/TooManyRequests' /api/board/game/{gameId}: get: operationId: getGame tags: [game] summary: Get game state description: Returns the full current state of a game. security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/gameId' responses: '200': description: Current game state content: application/json: schema: $ref: '#/components/schemas/GameFull' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/TooManyRequests' /api/board/game/{gameId}/stream: get: operationId: streamGame tags: [game] summary: Stream game events description: | Opens a persistent NDJSON stream for a game. The first object sent is a `gameFull` event containing the complete game state. Subsequent objects are `gameState` events sent whenever the game changes (move made, draw offered, game over, etc.). Empty lines are heartbeats to keep the connection alive. Connect with: ``` Accept: application/x-ndjson ``` security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/gameId' responses: '200': description: NDJSON event stream content: application/x-ndjson: schema: oneOf: - $ref: '#/components/schemas/GameFullEvent' - $ref: '#/components/schemas/GameStateEvent' - $ref: '#/components/schemas/ErrorEvent' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/TooManyRequests' /api/board/game/{gameId}/resign: post: operationId: resignGame tags: [game] summary: Resign the game description: The active player resigns. The game ends immediately. security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/gameId' responses: '200': description: Resignation accepted content: application/json: schema: $ref: '#/components/schemas/OkResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/TooManyRequests' # --------------------------------------------------------------------------- # Move-making # --------------------------------------------------------------------------- /api/board/game/{gameId}/move/{uci}: post: operationId: makeMove tags: [move] summary: Make a move description: | Submit a move in UCI notation. The move must be legal for the side currently to move. For promotion moves include the target piece as the fifth character: `e7e8q`, `a2a1r`, etc. If the move results in a pawn reaching the back rank and no promotion character is supplied, the game enters `promotionPending` status and the move is not yet applied — resubmit with the promotion character. security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/gameId' - name: uci in: path required: true description: Move in UCI notation (e.g. `e2e4`, `e7e8q`) schema: type: string pattern: '^[a-h][1-8][a-h][1-8][qrbn]?$' example: e2e4 responses: '200': description: Move applied — returns updated game state content: application/json: schema: $ref: '#/components/schemas/GameState' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/TooManyRequests' /api/board/game/{gameId}/moves: get: operationId: getLegalMoves tags: [move] summary: Get legal moves description: | Returns all legal moves for the side currently to move. Optionally filter to moves originating from a single square. security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/gameId' - name: square in: query required: false description: Filter to moves from this square (e.g. `e2`) schema: type: string pattern: '^[a-h][1-8]$' example: e2 responses: '200': description: List of legal moves content: application/json: schema: $ref: '#/components/schemas/LegalMovesResponse' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/TooManyRequests' /api/board/game/{gameId}/undo: post: operationId: undoMove tags: [move] summary: Undo the last move description: Reverts the most recent move. Returns the updated game state. security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/gameId' responses: '200': description: Move undone content: application/json: schema: $ref: '#/components/schemas/GameState' '400': description: No moves to undo content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/TooManyRequests' /api/board/game/{gameId}/redo: post: operationId: redoMove tags: [move] summary: Redo a previously undone move description: Re-applies the next move in the undo stack. Returns the updated game state. security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/gameId' responses: '200': description: Move redone content: application/json: schema: $ref: '#/components/schemas/GameState' '400': description: No moves to redo content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/TooManyRequests' # --------------------------------------------------------------------------- # Draw handling # --------------------------------------------------------------------------- /api/board/game/{gameId}/draw/{action}: post: operationId: drawAction tags: [draw] summary: Offer, accept, decline, or claim a draw description: | Perform a draw-related action: | Action | Description | |-----------|-------------| | `offer` | Offer a draw to the opponent | | `accept` | Accept the opponent's draw offer | | `decline` | Decline the opponent's draw offer | | `claim` | Claim a draw under the fifty-move rule (only valid when `status` is `fiftyMoveAvailable`) | security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/gameId' - name: action in: path required: true schema: type: string enum: [offer, accept, decline, claim] responses: '200': description: Action accepted content: application/json: schema: $ref: '#/components/schemas/OkResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/TooManyRequests' # --------------------------------------------------------------------------- # Import # --------------------------------------------------------------------------- /api/board/game/import/fen: post: operationId: importFen tags: [import] summary: Load a position from FEN description: | Creates a new game from a FEN string. The game starts at the position described by the FEN; move history prior to that position is not available. security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ImportFenRequest' responses: '201': description: Game created from FEN content: application/json: schema: $ref: '#/components/schemas/GameFull' '400': $ref: '#/components/responses/BadRequest' '429': $ref: '#/components/responses/TooManyRequests' /api/board/game/import/pgn: post: operationId: importPgn tags: [import] summary: Load a game from PGN description: | Creates a new game by replaying all moves in a PGN string. The game starts at the position after the final move in the PGN; undo is available for every replayed move. security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ImportPgnRequest' responses: '201': description: Game created from PGN content: application/json: schema: $ref: '#/components/schemas/GameFull' '400': $ref: '#/components/responses/BadRequest' '429': $ref: '#/components/responses/TooManyRequests' # --------------------------------------------------------------------------- # Export # --------------------------------------------------------------------------- /api/board/game/{gameId}/export/fen: get: operationId: exportFen tags: [export] summary: Export current position as FEN description: Returns the FEN string representing the current board position. security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/gameId' responses: '200': description: FEN string content: text/plain: schema: type: string example: rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1 '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/TooManyRequests' /api/board/game/{gameId}/export/pgn: get: operationId: exportPgn tags: [export] summary: Export game as PGN description: Returns the full PGN for the game including headers and move text. security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/gameId' responses: '200': description: PGN text content: application/x-chess-pgn: schema: type: string example: | [Event "NowChess game"] [White "Player1"] [Black "Player2"] [Result "*"] 1. e4 e5 2. Nf3 * '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/TooManyRequests' # ============================================================================= # Components # ============================================================================= components: securitySchemes: bearerAuth: type: http scheme: bearer description: 'Personal access token — `Authorization: Bearer `' parameters: gameId: name: gameId in: path required: true description: 8-character alphanumeric game ID (e.g. `Qa7FJNk2`) schema: type: string pattern: '^[A-Za-z0-9]{8}$' example: Qa7FJNk2 responses: BadRequest: description: Invalid input content: application/json: schema: $ref: '#/components/schemas/ApiError' Unauthorized: description: Missing or invalid authentication token content: application/json: schema: $ref: '#/components/schemas/ApiError' NotFound: description: Game not found content: application/json: schema: $ref: '#/components/schemas/ApiError' TooManyRequests: description: Rate limit exceeded — see `Retry-After` header headers: Retry-After: description: Seconds to wait before retrying schema: type: integer content: application/json: schema: $ref: '#/components/schemas/ApiError' schemas: # ------------------------------------------------------------------------- # Requests # ------------------------------------------------------------------------- CreateGameRequest: type: object description: Parameters for creating a new game. All fields are optional. properties: white: $ref: '#/components/schemas/PlayerInfo' black: $ref: '#/components/schemas/PlayerInfo' ImportFenRequest: type: object required: [fen] properties: fen: type: string description: Complete FEN string (6 fields) example: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 white: $ref: '#/components/schemas/PlayerInfo' black: $ref: '#/components/schemas/PlayerInfo' ImportPgnRequest: type: object required: [pgn] properties: pgn: type: string description: PGN text (headers and move list) example: "1. e4 e5 2. Nf3 Nc6 *" # ------------------------------------------------------------------------- # Game state # ------------------------------------------------------------------------- GameFull: type: object description: Complete game information including players and current state. required: [gameId, white, black, state] properties: gameId: type: string description: Unique 8-character game identifier example: Qa7FJNk2 white: $ref: '#/components/schemas/PlayerInfo' black: $ref: '#/components/schemas/PlayerInfo' state: $ref: '#/components/schemas/GameState' GameState: type: object description: | The current game state. Included in `GameFull` and returned by move endpoints and stream events. required: [fen, pgn, turn, status, moves, undoAvailable, redoAvailable] properties: fen: type: string description: FEN string for the current position example: rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1 pgn: type: string description: PGN move text for the full game so far example: "1. e4" turn: type: string enum: [white, black] description: The side to move status: $ref: '#/components/schemas/GameStatus' winner: type: string enum: [white, black] description: Set when `status` is `checkmate` or `resign` nullable: true moves: type: array description: All moves played so far, in UCI notation items: type: string example: [e2e4, e7e5, g1f3] undoAvailable: type: boolean description: Whether `POST /undo` is currently valid redoAvailable: type: boolean description: Whether `POST /redo` is currently valid GameStatus: type: string description: | Current game status: | Value | Meaning | |-------|---------| | `started` | Game in progress, no special condition | | `check` | Side to move is in check | | `checkmate` | Side to move is checkmated — game over | | `stalemate` | Side to move has no legal moves, not in check — game over (draw) | | `resign` | A player resigned — game over | | `draw` | Draw agreed or claimed — game over | | `drawOffered` | Waiting for the opponent to accept or decline a draw offer | | `fiftyMoveAvailable` | Fifty-move rule threshold reached; active player may claim draw | | `promotionPending` | A pawn reached the back rank; awaiting promotion piece selection | | `insufficientMaterial` | Neither side has enough pieces to deliver checkmate — game over (draw) | enum: - started - check - checkmate - stalemate - resign - draw - drawOffered - fiftyMoveAvailable - promotionPending - insufficientMaterial # ------------------------------------------------------------------------- # Moves # ------------------------------------------------------------------------- LegalMovesResponse: type: object required: [moves] properties: moves: type: array items: $ref: '#/components/schemas/LegalMove' LegalMove: type: object required: [from, to, uci, moveType] properties: from: type: string description: Origin square in algebraic notation example: e2 to: type: string description: Destination square in algebraic notation example: e4 uci: type: string description: Full move in UCI notation example: e2e4 moveType: $ref: '#/components/schemas/MoveType' promotion: type: string enum: [queen, rook, bishop, knight] description: Target piece for promotion moves nullable: true MoveType: type: string description: Classification of the move enum: - normal - capture - castleKingside - castleQueenside - enPassant - promotion # ------------------------------------------------------------------------- # Streaming events # ------------------------------------------------------------------------- GameFullEvent: type: object description: | First event on a game stream. Contains the complete game snapshot. required: [type, game] properties: type: type: string enum: [gameFull] game: $ref: '#/components/schemas/GameFull' GameStateEvent: type: object description: | Emitted on a game stream whenever the game state changes (move played, draw offered, game over, etc.). required: [type, state] properties: type: type: string enum: [gameState] state: $ref: '#/components/schemas/GameState' ErrorEvent: type: object description: Emitted on a game stream when an error occurs. required: [type, error] properties: type: type: string enum: [error] error: $ref: '#/components/schemas/ApiError' # ------------------------------------------------------------------------- # Shared types # ------------------------------------------------------------------------- PlayerInfo: type: object required: [id, displayName] properties: id: type: string description: Unique player identifier example: player1 displayName: type: string description: Human-readable display name example: Alice OkResponse: type: object required: [ok] properties: ok: type: boolean enum: [true] ApiError: type: object required: [code, message] properties: code: type: string description: Machine-readable error code example: INVALID_MOVE message: type: string description: Human-readable error description example: e2e5 is not a legal move field: type: string description: Request field that caused the error, if applicable example: uci nullable: true