Solitaire WebSocket API v1.0.0

WebSocket API Documentation

Real-time event-driven communication for the Solitaire game. This API uses Socket.IO for reliable WebSocket connections with automatic reconnection. Each game supports a single active device - opening on a new device automatically disconnects the old one. All connections require JWT authentication.

Open in AsyncAPI Studio View JSON Specification

Authentication

JWT Bearer token authentication required for all WebSocket connections
How to Authenticate

Pass your JWT access token in the Socket.IO handshake auth object or as a query parameter:

// Option 1: Auth object (recommended) const socket = io('https://dev.api.skillbasedgames.myapp.com.ua/solitaire', { auth: { token: 'your_jwt_access_token' } }); // Option 2: Query parameter const socket = io('https://dev.api.skillbasedgames.myapp.com.ua/solitaire', { query: { token: 'your_jwt_access_token' } });
Token Errors:
- Missing token: Connection rejected with "No authentication token provided"
- Invalid token: Connection rejected with "Invalid access token"
- Expired token: Connection rejected with "Access token has expired"
- Inactive user: Connection rejected with "User account is inactive"

Game Ownership: Users can only access games they own. Attempting to join another user's game will result in a "Forbidden" error.

Server Information

Connection details and protocol information
Base URL
https://dev.api.skillbasedgames.myapp.com.ua
Namespace
/solitaire
Protocol
Socket.IO WebSocket

Client → Server Events

Events that clients can emit to the server
  • join_game Join an existing game session
  • leave_game Leave the current game session
  • make_move Execute a game move (move_card, draw_card, recycle_stock)
  • pause_game Pause the current game
  • resume_game Resume a paused game
  • undo_move Undo the last move
  • start_time Start the game timer
  • complete_game Complete the game (won or abandoned)
  • heartbeat Keep-alive ping

Server → Client Events

Events that the server emits to clients
  • connection_ack Connection acknowledgment
  • game_joined Full game state after joining
  • game_left Leave confirmation
  • game_state Full game state (sent on errors or hash mismatches)
  • game_state_lite Minimal state updates (timeLeft, pauseTimeLeft, autoCompleteReady)
  • device_replaced Notification when game is opened on another device
  • heartbeat_ack Heartbeat response
  • time_started Timer started confirmation
  • game_completed Game completion result
  • game_timed_out Server-initiated timeout notification (game or pause time exceeded)
  • game_force_closed Server-initiated notification when game mode is force closed by admin
  • error Error notifications

Client State Tracking

New fields for tracking client-side moves and synchronization
Optional Fields for Move Operations
{ moveId: string // Client-generated unique ID for move tracking isCached: boolean // Mark if move was executed from cached state gameHash: string // Current game state hash for validation timeLeft: number // Optional: Client's current time left (ms) }
moveId: Unique identifier generated by client to track moves. Server returns this in lastMoveId field. Enables idempotent moves - if a move with the same moveId is sent again (e.g., due to retry or network issues), the server will return the current state instead of throwing an error, making moves safe to retry.
isCached: Set to true if the move was executed against cached/offline state.
gameHash: Hash of current game state to detect synchronization issues.
timeLeft: Client's calculated time remaining for time drift detection.

Quick Start

Example code to get started with the WebSocket API
1. Connect to WebSocket (with authentication)
import { io } from 'socket.io-client'; const socket = io('https://dev.api.skillbasedgames.myapp.com.ua/solitaire', { transports: ['websocket'], reconnection: true, auth: { token: accessToken // JWT access token } });
2. Join a game
socket.emit('join_game', { gameId: 123 });
3. Listen for state updates
socket.on('game_state_lite', (data) => { console.log('Game state updated:', data.type); // Handle: 'move', 'undo', 'pause', 'resume' });
4. Make a move
socket.emit('make_move', { gameId: 123, gameHash: 'current_game_hash', moveType: 'move_card', from: { type: 'waste', index: 0 }, to: { type: 'foundation', index: 0 }, cardCount: 1, moveId: 'unique-move-id-123', // Optional: Track moves isCached: false // Optional: Mark cached moves });