Replace existing docs with fresh documentation built entirely from source code analysis. OpenAPI 3.1 spec as source of truth, plus human-readable Markdown with curl examples, response samples, and workflow guides. - OpenAPI 3.1 spec covering all 42 endpoints (validated against source) - 7 endpoint reference docs (auth, games, sessions, picker, stats, votes, webhooks) - WebSocket protocol documentation (auth, subscriptions, 4 event types) - 4 guide documents (getting started, session lifecycle, voting, webhooks) - API README with overview, auth docs, and quick reference table - Old docs archived to docs/archive/ Made-with: Cursor
8.0 KiB
8.0 KiB
WebSocket Subscription Guide
📋 Overview
This guide explains how WebSocket subscriptions work in the Jackbox Game Picker and which events require subscriptions.
🔌 Connection & Authentication
1. Connect to WebSocket
const ws = new WebSocket('ws://localhost:5000/api/sessions/live');
2. Authenticate
ws.send(JSON.stringify({
type: 'auth',
token: 'YOUR_JWT_TOKEN'
}));
3. Wait for Auth Success
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
if (msg.type === 'auth_success') {
console.log('Authenticated!');
// Now you can subscribe to sessions
}
});
📨 Event Types & Subscription Requirements
| Event Type | Requires Subscription? | Broadcast To | When to Subscribe |
|---|---|---|---|
session.started |
❌ NO | All authenticated clients | N/A - Automatic |
game.added |
✅ YES | Subscribed clients only | After session.started |
vote.received |
✅ YES | Subscribed clients only | After session.started |
session.ended |
✅ YES | Subscribed clients only | After session.started |
🎯 Subscription Strategy
Strategy 1: Auto-Subscribe to New Sessions (Recommended for Bots)
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
// After authentication
if (msg.type === 'auth_success') {
console.log('✅ Authenticated');
}
// Auto-subscribe to new sessions
if (msg.type === 'session.started') {
const sessionId = msg.data.session.id;
console.log(`🎮 New session ${sessionId} started!`);
// Subscribe to this session
ws.send(JSON.stringify({
type: 'subscribe',
sessionId: sessionId
}));
}
// Now you'll receive game.added, vote.received, and session.ended
if (msg.type === 'game.added') {
console.log(`🎲 Game: ${msg.data.game.title}`);
}
if (msg.type === 'session.ended') {
console.log(`🌙 Session ended! ${msg.data.session.games_played} games played`);
}
});
Strategy 2: Subscribe to Active Session on Connect
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
if (msg.type === 'auth_success') {
console.log('✅ Authenticated');
// Fetch active session from API
fetch('http://localhost:5000/api/sessions/active')
.then(res => res.json())
.then(session => {
if (session && session.id) {
// Subscribe to active session
ws.send(JSON.stringify({
type: 'subscribe',
sessionId: session.id
}));
}
});
}
});
Strategy 3: Subscribe to Specific Session
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
if (msg.type === 'auth_success') {
console.log('✅ Authenticated');
// Subscribe to specific session
const sessionId = 17;
ws.send(JSON.stringify({
type: 'subscribe',
sessionId: sessionId
}));
}
if (msg.type === 'subscribed') {
console.log(`✅ Subscribed to session ${msg.sessionId}`);
}
});
🔄 Complete Bot Flow
const WebSocket = require('ws');
class JackboxBot {
constructor(token) {
this.token = token;
this.ws = null;
this.currentSessionId = null;
}
connect() {
this.ws = new WebSocket('ws://localhost:5000/api/sessions/live');
this.ws.on('open', () => {
console.log('🔌 Connected to WebSocket');
this.authenticate();
});
this.ws.on('message', (data) => {
this.handleMessage(JSON.parse(data.toString()));
});
}
authenticate() {
this.ws.send(JSON.stringify({
type: 'auth',
token: this.token
}));
}
handleMessage(msg) {
switch (msg.type) {
case 'auth_success':
console.log('✅ Authenticated');
// Don't subscribe yet - wait for session.started
break;
case 'session.started':
this.currentSessionId = msg.data.session.id;
console.log(`🎮 Session ${this.currentSessionId} started!`);
// Auto-subscribe to this session
this.ws.send(JSON.stringify({
type: 'subscribe',
sessionId: this.currentSessionId
}));
// Announce to users
this.announce(`Game Night has started! Session #${this.currentSessionId}`);
break;
case 'subscribed':
console.log(`✅ Subscribed to session ${msg.sessionId}`);
break;
case 'game.added':
console.log(`🎲 Game: ${msg.data.game.title}`);
this.announce(`Now playing: ${msg.data.game.title}`);
break;
case 'vote.received':
console.log(`🗳️ Vote: ${msg.data.vote.type}`);
break;
case 'session.ended':
console.log(`🌙 Session ${msg.data.session.id} ended`);
this.announce(`Game Night ended! ${msg.data.session.games_played} games played`);
this.currentSessionId = null;
break;
}
}
announce(message) {
// Send to IRC/Discord/Kosmi/etc
console.log(`📢 ${message}`);
}
}
// Usage
const bot = new JackboxBot('YOUR_JWT_TOKEN');
bot.connect();
📊 Subscription Lifecycle
1. Connect to WebSocket
↓
2. Send auth message
↓
3. Receive auth_success
↓
4. Wait for session.started (no subscription needed)
↓
5. Receive session.started
↓
6. Send subscribe message with sessionId
↓
7. Receive subscribed confirmation
↓
8. Now receive: game.added, vote.received, session.ended
↓
9. Receive session.ended
↓
10. Wait for next session.started (repeat from step 4)
🔍 Debugging
Check What You're Subscribed To
The WebSocket manager tracks subscriptions. Check backend logs:
[WebSocket] Client subscribed to session 17
[WebSocket] Client unsubscribed from session 17
Verify Event Reception
session.started - Should receive immediately after authentication (no subscription needed):
[WebSocket] Broadcasted session.started to 2 authenticated client(s)
game.added, vote.received, session.ended - Only after subscribing:
[WebSocket] Broadcasted game.added to 1 client(s) for session 17
Common Issues
-
Not receiving session.started:
- ✅ Are you authenticated?
- ✅ Is your WebSocket connection open?
- ✅ Check backend logs for broadcast confirmation
-
Not receiving game.added:
- ✅ Did you subscribe to the session?
- ✅ Did you receive
subscribedconfirmation? - ✅ Is the session ID correct?
-
Not receiving session.ended:
- ✅ Are you still subscribed to the session?
- ✅ Did the session actually close?
- ✅ Check backend logs
🎯 Best Practices
- Auto-subscribe to new sessions when you receive
session.started - Don't subscribe before session.started - there's nothing to subscribe to yet
- Handle reconnections - re-authenticate and re-subscribe on reconnect
- Use polling as fallback - poll
/api/sessions/activeevery 30s as backup - Unsubscribe when done - clean up subscriptions when you're done with a session
- Validate session IDs - make sure the session exists before subscribing
📝 Summary
❌ No Subscription Required
session.started- Broadcast to all authenticated clients
✅ Subscription Required
game.added- Only to subscribed clientsvote.received- Only to subscribed clientssession.ended- Only to subscribed clients
🎯 Recommended Flow
- Authenticate
- Wait for
session.started(automatic) - Subscribe to the session
- Receive
game.added,vote.received,session.ended - Repeat from step 2 for next session
🔗 Related Documentation
- SESSION_START_WEBSOCKET.md - session.started event details
- SESSION_END_WEBSOCKET.md - session.ended event details
- API_QUICK_REFERENCE.md - API reference
- BOT_INTEGRATION.md - Bot integration guide