WebSocket Protocol
Status: roadmap — current transport is HTTPS REST
This protocol is specified and partially implemented, but it is not what production games use today: all game actions, results, and state updates currently flow over HTTPS REST calls (see the API Reference). ws.slottylabs.com is not yet live. This page documents the planned protocol.
Slotty Labs plans WebSocket connections for real-time game communication. When enabled, game actions, results, and state updates will flow through the WebSocket.
Connection
wss://{host}/ws?token={jwt}&gameId={gameId}| Parameter | Description |
|---|---|
host | ws.slottylabs.com (environment derived from your API key) |
token | Session JWT (from SSO exchange) |
gameId | Target game ID |
Connection Example
const ws = new WebSocket(
`wss://ws.slottylabs.com/ws?token=${sessionToken}&gameId=slotty-slots`
);
ws.onopen = () => {
console.log('Connected to game server');
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
handleMessage(message);
};
ws.onclose = (event) => {
console.log(`Disconnected: ${event.code} ${event.reason}`);
if (!event.wasClean) {
attemptReconnect();
}
};Client → Server Messages
action
Send a game action (bet, move, guess, etc.).
{
"type": "action",
"payload": {
"action": "spin",
"betPerLine": 1.00,
"activePaylines": 20
},
"requestId": "req-001"
}Game-specific action payloads:
{
"type": "action",
"payload": {
"action": "spin",
"betPerLine": 1.00,
"activePaylines": 20
},
"requestId": "req-001"
}{
"type": "action",
"payload": {
"action": "hit"
},
"requestId": "req-002"
}{
"type": "action",
"payload": {
"action": "guess",
"position": 3,
"character": "K"
},
"requestId": "req-003"
}{
"type": "action",
"payload": {
"action": "move",
"dx": 5.0,
"dy": -3.0
},
"requestId": "req-004"
}{
"type": "action",
"payload": {
"action": "launch",
"angle": 62.0,
"power": 1800
},
"requestId": "req-005"
}cashout
Cash out the current round (multi-step games only).
{
"type": "cashout",
"payload": {},
"requestId": "req-010"
}ping
Keep-alive ping. The server responds with pong.
{
"type": "ping",
"payload": {},
"requestId": "req-099"
}Server → Client Messages
action_result
Result of a game action.
{
"type": "action_result",
"payload": {
"roundId": "round-abc123",
"status": "completed",
"outcome": {
"reelPositions": [3, 7, 12, 1, 9],
"paylines": [
{ "line": 5, "symbols": ["cherry", "cherry", "cherry"], "win": 4.0 }
],
"totalWin": 4.0,
"freeSpinsTriggered": false
},
"balance": {
"before": 100.00,
"after": 84.00,
"currency": "USD"
},
"provablyFair": {
"serverSeedHash": "a1b2c3d4...",
"clientSeed": "f0e1d2c3...",
"nonce": 42
}
},
"requestId": "req-001",
"timestamp": 1719849600000
}cashout_result
Result of a cash-out request.
{
"type": "cashout_result",
"payload": {
"roundId": "round-abc123",
"status": "cashed_out",
"multiplier": 3.7,
"winAmount": 37.00,
"balance": {
"before": 90.00,
"after": 127.00,
"currency": "USD"
}
},
"requestId": "req-010",
"timestamp": 1719849600000
}balance_update
Player balance changed (from another source, e.g., deposit).
{
"type": "balance_update",
"payload": {
"balance": 500.00,
"currency": "USD",
"reason": "deposit",
"amount": 100.00
},
"timestamp": 1719849600000
}round_recovered
Sent on reconnect if the player has an active round.
{
"type": "round_recovered",
"payload": {
"roundId": "round-abc123",
"gameId": "safe-smash",
"state": {
"hitCount": 4,
"accumulatedMultiplier": 3.7,
"canCashOut": true
},
"balance": {
"current": 90.00,
"currency": "USD"
}
},
"timestamp": 1719849600000
}session_warning
Session time or reality check warning.
{
"type": "session_warning",
"payload": {
"warningType": "reality_check",
"sessionDuration": 3600,
"totalWagered": 500.00,
"netResult": -45.50,
"nextAction": "acknowledge",
"expiresIn": 60
},
"timestamp": 1719849600000
}error
An error occurred processing a request.
{
"type": "error",
"payload": {
"code": "30007",
"message": "Insufficient balance",
"details": {
"required": 20.00,
"available": 15.50
}
},
"requestId": "req-001",
"timestamp": 1719849600000
}pong
Response to a ping.
{
"type": "pong",
"payload": {
"serverTime": 1719849600000
},
"requestId": "req-099",
"timestamp": 1719849600000
}Reconnection Flow
If the WebSocket connection drops, follow this reconnection procedure:
- Detect disconnect —
ws.onclosefires withwasClean: false - Wait — Start with a 2-second delay
- Reconnect — Open a new WebSocket with the same session token
- Receive recovery — Server sends
round_recoveredif a round was in progress - Resume play — Continue from the recovered state
Reconnection Parameters
| Setting | Value |
|---|---|
| Initial delay | 2 seconds |
| Max attempts | 5 |
| Backoff | No backoff (fixed 2s intervals) |
| Session validity | Token remains valid for its original TTL |
Reconnection Example
class GameConnection {
private ws: WebSocket | null = null;
private reconnectAttempts = 0;
private maxAttempts = 5;
private reconnectDelay = 2000; // 2 seconds
connect(token: string, gameId: string) {
this.ws = new WebSocket(
`wss://ws.slottylabs.com/ws?token=${token}&gameId=${gameId}`
);
this.ws.onopen = () => {
this.reconnectAttempts = 0;
console.log('Connected');
};
this.ws.onclose = (event) => {
if (!event.wasClean && this.reconnectAttempts < this.maxAttempts) {
this.reconnectAttempts++;
console.log(`Reconnecting (attempt ${this.reconnectAttempts}/${this.maxAttempts})...`);
setTimeout(() => this.connect(token, gameId), this.reconnectDelay);
}
};
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'round_recovered') {
// Restore game state from recovered data
this.handleRoundRecovery(message.payload);
}
};
}
private handleRoundRecovery(state: any) {
console.log(`Recovered round ${state.roundId}`, state.state);
// Update UI with recovered state
}
}