WebSocket Protocol
Slotty Labs uses WebSocket connections for real-time game communication. All game actions, results, and state updates flow through the WebSocket.
Connection
wss://{host}/ws?token={jwt}&gameId={gameId}| Parameter | Description |
|---|---|
host | ws.slottylabs.com (production) or sandbox-ws.slottylabs.com (sandbox) |
token | Session JWT (from SSO exchange) |
gameId | Target game ID |
Connection Example
typescript
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.).
json
{
"type": "action",
"payload": {
"action": "spin",
"betPerLine": 1.00,
"activePaylines": 20
},
"requestId": "req-001"
}Game-specific action payloads:
json
{
"type": "action",
"payload": {
"action": "spin",
"betPerLine": 1.00,
"activePaylines": 20
},
"requestId": "req-001"
}json
{
"type": "action",
"payload": {
"action": "hit"
},
"requestId": "req-002"
}json
{
"type": "action",
"payload": {
"action": "guess",
"position": 3,
"character": "K"
},
"requestId": "req-003"
}json
{
"type": "action",
"payload": {
"action": "move",
"dx": 5.0,
"dy": -3.0
},
"requestId": "req-004"
}json
{
"type": "action",
"payload": {
"action": "launch",
"angle": 62.0,
"power": 1800
},
"requestId": "req-005"
}cashout
Cash out the current round (multi-step games only).
json
{
"type": "cashout",
"payload": {},
"requestId": "req-010"
}ping
Keep-alive ping. The server responds with pong.
json
{
"type": "ping",
"payload": {},
"requestId": "req-099"
}Server → Client Messages
action_result
Result of a game action.
json
{
"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.
json
{
"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).
json
{
"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.
json
{
"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.
json
{
"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.
json
{
"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.
json
{
"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
typescript
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
}
}