The Spades Online WebSocket server authenticates every connection at the HTTP upgrade stage — no anonymous connections are accepted. Once connected, all communication uses a consistent JSON envelope:Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Antonelli-Tech-Solutions/spades/llms.txt
Use this file to discover all available pages before exploring further.
{ "type": "<TYPE>", "payload": { ... } }. Clients subscribe to specific rooms (table rooms or the lobby channel) to receive the events relevant to them.
Connecting
URL:ws://hostname:port/ — use wss:// in production.
The server reads the session token from the x-session-id upgrade header. If the header is missing or the session is invalid, the server responds with HTTP 401 and closes the socket immediately. Browser clients, which cannot set custom headers on a WebSocket upgrade, pass the token as a sessionId URL query parameter instead (see the browser example below).
Required header (non-browser clients):
Message Format
All messages — in both directions — are JSON objects using the same envelope:type field is always a string constant. The payload field is an object; for messages with no meaningful data it is an empty object {}.
Subscribing to Table Rooms
Subscribe to a table room to receive all real-time game and table events for a specific table.Receive the JOINED acknowledgement
The server verifies that the authenticated player is seated at or observing the table, then responds with:If the player is not seated or observing, the server sends
JOIN_DENIED instead — see the JOIN_DENIED section below.Receive table events
From this point on, all events broadcast to
table:{tableId} are delivered to this connection. See the Events reference for the full list.Subscribing to the Lobby
Subscribe to the lobby channel to receive real-time notifications when public tables are created, updated, or removed.Receive lobby events
The server delivers
TABLE_CREATED, TABLE_UPDATED, and TABLE_REMOVED events for public tables. Friends-only and private tables are never broadcast on the public lobby channel — friends-only events are delivered to the host’s friends’ personal notification channels instead. See Lobby events for routing details.JOIN_DENIED
If aJOIN request is rejected, the server sends a JOIN_DENIED message instead of JOINED:
| Reason | Meaning |
|---|---|
not_seated_or_observing | The authenticated player is neither seated at nor observing the table. |
table_not_found | No table with the given tableId exists. |
error | An internal error occurred (e.g. Redis unavailable). |
Personal Notification Channel
Each authenticated connection is automatically subscribed to a personal Redis pub/sub channel —
player:{playerId}:notify — at the time of connection. No client action is required. This channel delivers friend requests, in-app invites, kick notifications, and friends-only table events directly to the target player across all server instances. The subscription is cleaned up automatically when the connection closes.Heartbeat
The server sends a WebSocket ping every 30 seconds. Clients must respond with a pong frame; standard WebSocket clients (browsers, thews Node.js library) handle this automatically. If no pong is received within 10 seconds of the ping, the connection is terminated.
The web client (gameSocket.js / createGameSocket) implements exponential-backoff reconnection: on an unexpected disconnect it retries up to 5 times with delays of 1 s, 2 s, 4 s, 8 s, and 16 s (capped at 30 s).
Client Messages Reference
| Type | Payload | Description |
|---|---|---|
JOIN | { tableId } | Subscribe to table room events. Acknowledged with JOINED or JOIN_DENIED. |
LEAVE | { tableId } | Unsubscribe from a table room. Acknowledged with LEFT. |
JOIN_LOBBY | {} | Subscribe to lobby events. Acknowledged with JOINED_LOBBY. |
LEAVE_LOBBY | {} | Unsubscribe from lobby events. Acknowledged with LEFT_LOBBY. |