5.9 KiB
NowChess Tournament API
Swiss-system bot tournaments. Bots are paired by score each round; all bots play every round (no eliminations). Game moves flow through the existing board and bot endpoints — the tournament module only orchestrates pairings, standings, and lifecycle.
Base path
/api/tournament
Routing: /api/tournament → nowchess-tournament-active:8086
Authentication
All endpoints require a valid JWT (Authorization: Bearer <token>).
Bot-facing streaming endpoints additionally require the token's subject to match the registered botId.
Data models
Tournament
{
"id": "t7kXq2",
"name": "Friday Night Bots",
"status": "created | started | finished",
"rounds": 5,
"currentRound": 2,
"timeControl": {
"limitSeconds": 300,
"incrementSeconds": 3
},
"createdBy": "userId",
"createdAt": "2026-05-13T18:00:00Z",
"startedAt": "2026-05-13T18:05:00Z",
"finishedAt": null
}
Standing
{
"rank": 1,
"botId": "bot_abc",
"botName": "StockfishClone",
"points": 3.5,
"wins": 3,
"draws": 1,
"losses": 0,
"buchholz": 9.0
}
Tiebreaker: Buchholz score (sum of opponents' points).
Pairing
{
"round": 2,
"whiteBot": "bot_abc",
"blackBot": "bot_xyz",
"gameId": "j0nPtcjl",
"result": "white | black | draw | ongoing"
}
TournamentEvent (SSE)
{ "type": "tournamentStarted", "tournamentId": "t7kXq2" }
{ "type": "roundStarted", "tournamentId": "t7kXq2", "round": 2 }
{ "type": "pairingReady", "tournamentId": "t7kXq2", "round": 2, "gameId": "j0nPtcjl", "color": "white" }
{ "type": "roundFinished", "tournamentId": "t7kXq2", "round": 2 }
{ "type": "tournamentFinished","tournamentId": "t7kXq2" }
Endpoints
Tournament lifecycle
Create tournament
POST /api/tournament
Body:
{
"name": "Friday Night Bots",
"rounds": 5,
"timeControl": {
"limitSeconds": 300,
"incrementSeconds": 3
}
}
Response 201 Created:
{ "id": "t7kXq2" }
The creator becomes the tournament director. Only the director can start and delete the tournament.
Get tournament
GET /api/tournament/{tournamentId}
Response 200 OK: Tournament object.
List tournaments
GET /api/tournament
Query params:
| Param | Type | Default |
|---|---|---|
status |
created|started|finished |
(all) |
limit |
integer (max 50) | 20 |
offset |
integer | 0 |
Response 200 OK:
{
"tournaments": [ /* Tournament[] */ ],
"total": 42
}
Start tournament
POST /api/tournament/{tournamentId}/start
Requires at least 2 registered bots. Computes round 1 pairings (random for round 1; score-based from round 2). Creates one game per pairing via POST /api/board/game.
Response 200 OK: updated Tournament object.
Delete tournament
DELETE /api/tournament/{tournamentId}
Only allowed while status == "created". Response 204 No Content.
Bot registration
Register bot
POST /api/tournament/{tournamentId}/bots
Registers a bot for the tournament. Must be called before the tournament starts.
The token subject must match the bot being registered.
Body:
{ "botId": "bot_abc" }
Response 200 OK:
{ "botId": "bot_abc", "tournamentId": "t7kXq2" }
Unregister bot
DELETE /api/tournament/{tournamentId}/bots/{botId}
Only allowed while status == "created". Response 204 No Content.
List registered bots
GET /api/tournament/{tournamentId}/bots
Response 200 OK:
{
"bots": [
{ "botId": "bot_abc", "botName": "StockfishClone" }
]
}
Standings and pairings
Get standings
GET /api/tournament/{tournamentId}/standings
Response 200 OK:
{ "standings": [ /* Standing[] */ ] }
Get pairings for a round
GET /api/tournament/{tournamentId}/rounds/{round}/pairings
Response 200 OK:
{ "pairings": [ /* Pairing[] */ ] }
Bot streaming
Stream tournament events
GET /api/tournament/{tournamentId}/stream
Headers: Accept: text/event-stream
Server-Sent Events stream scoped to this tournament. The bot receives pairingReady events when it is assigned a game, at which point it should connect to the existing bot game stream:
GET /bot/stream/game/{gameId} (existing endpoint)
POST /bot/game/{gameId}/move/{uci} (existing endpoint)
The tournament module never sends moves — bots do that themselves through the existing bot endpoints.
Typical bot flow
1. POST /api/tournament # director creates tournament
2. POST /api/tournament/{id}/bots # each bot registers
3. POST /api/tournament/{id}/start # director starts
4. GET /api/tournament/{id}/stream (SSE) # each bot opens stream
-- per round --
5. receive: pairingReady { gameId, color }
6. GET /bot/stream/game/{gameId} # existing endpoint
7. POST /bot/game/{gameId}/move/{uci} # existing endpoint, repeated
-- game ends --
8. receive: roundFinished
9. GET /api/tournament/{id}/standings # optional, inspect scores
-- repeat 5–9 for each round --
10. receive: tournamentFinished
11. GET /api/tournament/{id}/standings # final ranking
Error responses
| Status | Meaning |
|---|---|
| 400 | Invalid request body or parameters |
| 401 | Missing or invalid JWT |
| 403 | Action not allowed (wrong director, wrong bot, etc.) |
| 404 | Tournament or bot not found |
| 409 | Tournament already started / bot already registered |