Skip to content

WebSocket API

CheckAI provides a full-featured WebSocket API at /ws that mirrors every REST endpoint and adds real-time event broadcasting.

Connection

bash
ws://localhost:8080/ws

Or over TLS:

bash
wss://your-host/ws

Message Format

All client-to-server messages are JSON objects with an "action" field. Server responses include a "type" field to distinguish responses from events.

Client → Server

json
{
  "action": "create_game",
  "request_id": "abc123"
}

The request_id field is optional but recommended for correlating responses.

Available Actions

Game Management

ActionExtra FieldsDescription
create_gameCreate a new game
list_gamesList all games
get_gamegame_idGet game state
delete_gamegame_idDelete a game

Gameplay

ActionExtra FieldsDescription
submit_movegame_id, from, to, promotion?Submit a move
submit_actiongame_id, action_type, reason?Submit a special action
get_legal_movesgame_idGet legal moves
get_boardgame_idGet ASCII board

Subscriptions

ActionExtra FieldsDescription
subscribegame_idSubscribe to real-time events for a game
unsubscribegame_idUnsubscribe from a game

Archive

ActionExtra FieldsDescription
list_archivedList all archived games
get_archivedgame_idGet an archived game
replay_archivedgame_id, move_number?Replay an archived game

Storage

ActionExtra FieldsDescription
get_storage_statsGet storage statistics

Server → Client Messages

Response

Sent in reply to a client action:

json
{
  "type": "response",
  "action": "submit_move",
  "request_id": "abc123",
  "success": true,
  "data": { ... }
}

Event

Pushed to all subscribers of a game when something changes:

json
{
  "type": "event",
  "event": "game_updated",
  "game_id": "550e8400-...",
  "data": { ... }
}

Event types include:

EventDescription
game_updatedA move was made or state changed
game_deletedA game was deleted

Example (JavaScript)

javascript
const ws = new WebSocket("ws://localhost:8080/ws");

ws.onopen = () => {
  // Create a game
  ws.send(JSON.stringify({
    action: "create_game",
    request_id: "1"
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type === "response" && msg.action === "create_game") {
    const gameId = msg.data.game_id;

    // Subscribe to real-time events
    ws.send(JSON.stringify({
      action: "subscribe",
      game_id: gameId
    }));

    // Make a move
    ws.send(JSON.stringify({
      action: "submit_move",
      game_id: gameId,
      from: "e2",
      to: "e4",
      request_id: "2"
    }));
  }

  if (msg.type === "event") {
    console.log("Game event:", msg.event, msg.data);
  }
};

ws.onerror = (err) => console.error("WebSocket error:", err);
ws.onclose = () => console.log("WebSocket closed");

Example (Python)

python
import json
import websocket

def on_message(ws, message):
    msg = json.loads(message)
    if msg["type"] == "response" and msg["action"] == "create_game":
        game_id = msg["data"]["game_id"]
        ws.send(json.dumps({
            "action": "subscribe",
            "game_id": game_id
        }))
        ws.send(json.dumps({
            "action": "submit_move",
            "game_id": game_id,
            "from": "e2",
            "to": "e4"
        }))
    elif msg["type"] == "event":
        print(f"Event: {msg['event']}", msg["data"])

ws = websocket.WebSocketApp(
    "ws://localhost:8080/ws",
    on_message=on_message,
    on_open=lambda ws: ws.send(json.dumps({
        "action": "create_game",
        "request_id": "1"
    }))
)
ws.run_forever()

Reconnection Strategy

The server does not send ping/pong heartbeats. Clients should implement their own reconnection logic:

  1. On unexpected close, wait before reconnecting using exponential backoff (start at 1 s, double on each failure, cap at 30 s).
  2. On successful reconnection, reset the delay to 1 s.
  3. Re-subscribe to any games the client was previously watching.

The built-in Web UI implements this automatically. For custom clients, a simple pattern:

javascript
let delay = 1000;
const MAX_DELAY = 30000;

function connect() {
  const ws = new WebSocket("ws://localhost:8080/ws");
  ws.onopen = () => { delay = 1000; /* resubscribe */ };
  ws.onclose = () => {
    setTimeout(connect, delay);
    delay = Math.min(delay * 2, MAX_DELAY);
  };
}
connect();

Error Handling

If a client sends an invalid action or references a non-existent game, the server responds with "success": false:

json
{
  "type": "response",
  "action": "get_game",
  "request_id": "5",
  "success": false,
  "error": "Game not found"
}

Released under the MIT License.