# API Quick Reference Quick reference for Live Voting, WebSocket, and Webhook endpoints. ## Base URL ``` http://localhost:5000/api ``` ## Authentication All endpoints require JWT authentication: ``` Authorization: Bearer YOUR_JWT_TOKEN ``` Get token via: ```bash POST /api/auth/login Body: { "key": "YOUR_ADMIN_KEY" } ``` --- ## WebSocket Events ### Connect to WebSocket ``` ws://localhost:5000/api/sessions/live ``` **Message Protocol**: ```json // Authenticate { "type": "auth", "token": "YOUR_JWT_TOKEN" } // Subscribe to session { "type": "subscribe", "sessionId": 123 } // Unsubscribe { "type": "unsubscribe", "sessionId": 123 } // Heartbeat { "type": "ping" } ``` **Server Events**: ```json // Auth success { "type": "auth_success", "message": "..." } // Subscribed { "type": "subscribed", "sessionId": 123 } // Session started { "type": "session.started", "timestamp": "2025-11-01T...", "data": { "session": { "id": 123, "is_active": 1, "created_at": "...", "notes": "..." } } } // Game added { "type": "game.added", "timestamp": "2025-11-01T...", "data": { "session": { "id": 123, "is_active": true, "games_played": 5 }, "game": { "id": 45, "title": "Fibbage 4", ... } } } // Session ended { "type": "session.ended", "timestamp": "2025-11-01T...", "data": { "session": { "id": 123, "is_active": 0, "games_played": 5 } } } // Vote received (live votes only, not chat-import) { "type": "vote.received", "timestamp": "2025-11-01T...", "data": { "sessionId": 123, "game": { "id": 42, "title": "Quiplash 3", "pack_name": "Party Pack 7" }, "vote": { "username": "viewer123", "type": "up", "timestamp": "2025-11-01T20:30:00Z" }, "totals": { "upvotes": 14, "downvotes": 3, "popularity_score": 11 } } } // Pong { "type": "pong" } ``` --- ## Votes ### Get Vote History ```http GET /api/votes?session_id=5&game_id=42&username=viewer123&vote_type=up&page=1&limit=50 ``` **Response (200)**: ```json { "votes": [ { "id": 891, "session_id": 5, "game_id": 42, "game_title": "Quiplash 3", "pack_name": "Party Pack 7", "username": "viewer123", "vote_type": "up", "timestamp": "2025-11-01T20:30:00.000Z", "created_at": "2025-11-01T20:30:01.000Z" } ], "pagination": { "page": 1, "limit": 50, "total": 237, "total_pages": 5 } } ``` All query parameters are optional. No authentication required. ### Submit Live Vote ```http POST /api/votes/live ``` **Request Body**: ```json { "username": "string", "vote": "up" | "down", "timestamp": "2025-11-01T20:30:00Z" } ``` **Success Response (200)**: ```json { "success": true, "message": "Vote recorded successfully", "session": { "id": 123, "games_played": 5 }, "game": { "id": 45, "title": "Fibbage 4", "upvotes": 46, "downvotes": 3, "popularity_score": 43 }, "vote": { "username": "TestUser", "type": "up", "timestamp": "2025-11-01T20:30:00Z" } } ``` **Error Responses**: - `400` - Invalid payload - `404` - No active session or timestamp doesn't match any game - `409` - Duplicate vote (within 1 second) --- ## Webhooks ### List Webhooks ```http GET /api/webhooks ``` **Response**: ```json [ { "id": 1, "name": "Kosmi Bot", "url": "http://bot-url/webhook", "events": ["game.added"], "enabled": true, "created_at": "2025-11-01T20:00:00Z" } ] ``` ### Get Single Webhook ```http GET /api/webhooks/:id ``` ### Create Webhook ```http POST /api/webhooks ``` **Request Body**: ```json { "name": "Kosmi Bot", "url": "http://bot-url/webhook", "secret": "your_shared_secret", "events": ["game.added"] } ``` **Response (201)**: ```json { "id": 1, "name": "Kosmi Bot", "url": "http://bot-url/webhook", "events": ["game.added"], "enabled": true, "created_at": "2025-11-01T20:00:00Z", "message": "Webhook created successfully" } ``` ### Update Webhook ```http PATCH /api/webhooks/:id ``` **Request Body** (all fields optional): ```json { "name": "Updated Name", "url": "http://new-url/webhook", "secret": "new_secret", "events": ["game.added"], "enabled": false } ``` ### Delete Webhook ```http DELETE /api/webhooks/:id ``` **Response (200)**: ```json { "message": "Webhook deleted successfully", "webhookId": 1 } ``` ### Test Webhook ```http POST /api/webhooks/test/:id ``` Sends a test `game.added` event to verify webhook is working. **Response (200)**: ```json { "message": "Test webhook sent", "note": "Check webhook_logs table for delivery status" } ``` ### Get Webhook Logs ```http GET /api/webhooks/:id/logs?limit=50 ``` **Response**: ```json [ { "id": 1, "webhook_id": 1, "event_type": "game.added", "payload": { /* full payload */ }, "response_status": 200, "error_message": null, "created_at": "2025-11-01T20:30:00Z" } ] ``` --- ## Webhook Payloads ### Event: `session.started` Sent when a new session is created. **Headers**: - `Content-Type: application/json` - `X-Webhook-Signature: sha256=` - `X-Webhook-Event: session.started` - `User-Agent: Jackbox-Game-Picker-Webhook/1.0` **Payload**: ```json { "event": "session.started", "timestamp": "2025-11-01T20:00:00Z", "data": { "session": { "id": 123, "is_active": 1, "created_at": "2025-11-01T20:00:00Z", "notes": "Friday Game Night" } } } ``` ### Event: `game.added` Sent when a game is added to an active session. **Headers**: - `Content-Type: application/json` - `X-Webhook-Signature: sha256=` - `X-Webhook-Event: game.added` - `User-Agent: Jackbox-Game-Picker-Webhook/1.0` **Payload**: ```json { "event": "game.added", "timestamp": "2025-11-01T20:30:00Z", "data": { "session": { "id": 123, "is_active": true, "games_played": 5 }, "game": { "id": 45, "title": "Fibbage 4", "pack_name": "The Jackbox Party Pack 9", "min_players": 2, "max_players": 8, "manually_added": false } } } ``` ### Event: `session.ended` Sent when a session is closed/ended. **Headers**: - `Content-Type: application/json` - `X-Webhook-Signature: sha256=` - `X-Webhook-Event: session.ended` - `User-Agent: Jackbox-Game-Picker-Webhook/1.0` **Payload**: ```json { "event": "session.ended", "timestamp": "2025-11-01T20:30:00Z", "data": { "session": { "id": 123, "is_active": 0, "games_played": 5 } } } ``` --- ## cURL Examples ### Submit Vote ```bash curl -X POST "http://localhost:5000/api/votes/live" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "username": "TestUser", "vote": "up", "timestamp": "2025-11-01T20:30:00Z" }' ``` ### Create Webhook ```bash curl -X POST "http://localhost:5000/api/webhooks" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Kosmi Bot", "url": "http://localhost:3001/webhook/jackbox", "secret": "test_secret_123", "events": ["game.added"] }' ``` ### Test Webhook ```bash curl -X POST "http://localhost:5000/api/webhooks/test/1" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` ### View Webhook Logs ```bash curl -X GET "http://localhost:5000/api/webhooks/1/logs?limit=10" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` --- ## Webhook Signature Verification **Node.js Example**: ```javascript const crypto = require('crypto'); function verifyWebhookSignature(signature, payload, secret) { if (!signature || !signature.startsWith('sha256=')) { return false; } const expectedSignature = 'sha256=' + crypto .createHmac('sha256', secret) .update(JSON.stringify(payload)) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); } // In your webhook endpoint: app.post('/webhook/jackbox', (req, res) => { const signature = req.headers['x-webhook-signature']; const secret = process.env.WEBHOOK_SECRET; if (!verifyWebhookSignature(signature, req.body, secret)) { return res.status(401).send('Invalid signature'); } // Process webhook... res.status(200).send('OK'); }); ``` --- ## Error Codes | Code | Meaning | |------|---------| | 200 | Success | | 201 | Created | | 400 | Bad Request - Invalid payload | | 401 | Unauthorized - Invalid JWT or signature | | 404 | Not Found - Resource doesn't exist | | 409 | Conflict - Duplicate vote | | 500 | Internal Server Error | --- ## Rate Limiting Currently no rate limiting is implemented. Consider implementing rate limiting in production: - Per IP address - Per JWT token - Per webhook endpoint --- ## Best Practices 1. **Always verify webhook signatures** before processing 2. **Use HTTPS** for webhook URLs in production 3. **Store secrets securely** in environment variables 4. **Respond quickly** to webhooks (< 5 seconds) 5. **Log webhook activity** for debugging 6. **Handle retries gracefully** if implementing retry logic 7. **Validate timestamps** to prevent replay attacks --- For detailed documentation, see [BOT_INTEGRATION.md](BOT_INTEGRATION.md)