# WebSocket Sync API Real-time bidirectional sync API for live updates and notifications. **Endpoint**: `WS /ws/sync` **Auth**: Required (JWT token or Device authentication) ## Connection Connect to the WebSocket endpoint with authentication: ```javascript const ws = new WebSocket('wss://bookhoard.example/ws/sync?token=eyJhbGci...'); // Or with device authentication const ws = new WebSocket('wss://bookhoard.example/ws/sync?device_id=uuid&device_key=key'); ``` ## Message Format All messages are JSON: ```json { "type": "message_type", "data": { ... } } ``` ## Client→Server Messages ### Subscribe to Progress Updates ```json { "type": "subscribe", "data": { "topic": "progress", "device_id": "device-uuid" } } ``` ### Unsubscribe ```json { "type": "unsubscribe", "data": { "topic": "progress" } } ``` ### Heartbeat/Ping ```json { "type": "ping", "data": { "timestamp": "2026-02-08T10:00:00Z" } } ``` ## Server→Client Messages ### Progress Updated ```json { "type": "progress_updated", "data": { "media_item_id": "uuid", "device_id": "device-uuid", "percentage": 75.5, "position": 1234, "updated_at": "2026-02-08T10:00:00Z" } } ``` ### Conflict Detected ```json { "type": "conflict_detected", "data": { "conflict_id": "uuid", "media_item_id": "uuid", "severity": "high", "created_at": "2026-02-08T10:00:00Z" } } ``` ### Scan Progress ```json { "type": "scan_progress", "data": { "job_id": "uuid", "library_id": "uuid", "percentage": 45.5, "files_processed": 850, "total_files": 1523, "status": "in_progress" } } ``` ### Scan Complete ```json { "type": "scan_complete", "data": { "job_id": "uuid", "library_id": "uuid", "files_added": 125, "files_updated": 45, "files_failed": 3, "completed_at": "2026-02-08T10:00:00Z" } } ``` ### Device Connected ```json { "type": "device_connected", "data": { "device_id": "uuid", "device_name": "My Kobo", "connected_at": "2026-02-08T10:00:00Z" } } ``` ### Device Disconnected ```json { "type": "device_disconnected", "data": { "device_id": "uuid", "disconnected_at": "2026-02-08T10:00:00Z" } } ``` ### Pong Response ```json { "type": "pong", "data": { "timestamp": "2026-02-08T10:00:00Z" } } ``` ## Connection Lifecycle 1. **Connect** - WebSocket connection established 2. **Authenticate** - Send JWT token or device credentials via query string 3. **Subscribed** - Server confirms authentication and subscribes to relevant topics 4. **Messages** - Server pushes real-time updates 5. **Heartbeat** - Client sends ping every 30 seconds 6. **Disconnect** - Connection closed ## Authentication ### User Authentication ``` wss://bookhoard.example/ws/sync?token= ``` ### Device Authentication ``` wss://bookhoard.example/ws/sync?device_id=&device_key= ``` ## Error Messages ### Authentication Failed ```json { "type": "error", "data": { "code": "auth_failed", "message": "Invalid or expired token" } } ``` ### Subscription Failed ```json { "type": "error", "data": { "code": "subscription_failed", "message": "Cannot subscribe to topic" } } ``` ## Usage Example ```javascript const ws = new WebSocket('wss://bookhoard.example/ws/sync?token=eyJhbGci...'); ws.onopen = () => { console.log('Connected to sync WebSocket'); // Subscribe to progress updates ws.send(JSON.stringify({ type: 'subscribe', data: { topic: 'progress', device_id: 'device-uuid' } })); // Start heartbeat setInterval(() => { ws.send(JSON.stringify({ type: 'ping', data: { timestamp: new Date().toISOString() } })); }, 30000); }; ws.onmessage = (event) => { const message = JSON.parse(event.data); switch (message.type) { case 'progress_updated': console.log('Progress updated:', message.data); break; case 'conflict_detected': console.log('New conflict detected:', message.data); break; case 'scan_complete': console.log('Scan complete:', message.data); break; case 'pong': console.log('Pong received'); break; default: console.log('Unknown message type:', message.type); } }; ws.onerror = (error) => { console.error('WebSocket error:', error); }; ws.onclose = () => { console.log('WebSocket connection closed'); }; ``` ## Topics | Topic | Description | Events | |-------|-------------|--------| | progress | Reading progress updates | progress_updated | | conflicts | Sync conflict events | conflict_detected, conflict_resolved | | scanner | Library scan events | scan_progress, scan_complete | | devices | Device connection events | device_connected, device_disconnected | | queue | Sync queue events | queue_item_added, queue_item_processed | ## Best Practices 1. **Heartbeat**: Send ping every 30 seconds to keep connection alive 2. **Reconnect**: Implement exponential backoff for reconnection 3. **Message Queue**: Queue messages when disconnected and replay on reconnect 4. **Error Handling**: Handle all error types gracefully 5. **Cleanup**: Unsubscribe from topics when no longer needed