Update README.md, docs/api/README.md, session-lifecycle guide, webhooks-and-events guide, and archive docs (BOT_INTEGRATION, API_QUICK_REFERENCE) with new endpoints and vote.received event. Made-with: Cursor
7.2 KiB
Webhooks and Events
A narrative guide to the Jackbox Game Picker event notification system: webhooks (HTTP callbacks) and WebSocket (persistent real-time connections). Both deliver event data about session and game activity.
1. Two Notification Systems
The API offers two complementary ways to receive event notifications:
| System | Model | Best for |
|---|---|---|
| Webhooks | HTTP POST callbacks to your URL | Server-to-server, external integrations |
| WebSocket | Persistent bidirectional connection | Real-time UIs, dashboards, live tools |
Both systems emit the same kinds of events (e.g. game.added) but differ in how they deliver them.
2. When to Use Which
Use Webhooks when:
- Server-to-server — Discord bots, Slack, logging pipelines, external APIs
- Stateless — Your endpoint receives a POST, processes it, and returns. No long-lived connection
- Behind firewalls — Your server can receive HTTP but may not hold open WebSocket connections
- Async delivery — You’re fine with HTTP round-trip latency and want delivery logged and auditable
Use WebSocket when:
- Real-time UI — Dashboards, admin panels, live session viewers
- Instant updates — You need push-style notifications with minimal latency
- Persistent connection — Your app keeps a live connection and subscribes to specific sessions
- Best-effort is fine — WebSocket is push-only; there’s no built-in delivery log for events
3. Webhook Setup
Webhooks are registered via the REST API. See Webhooks endpoints for full CRUD details.
Create a Webhook
POST /api/webhooks with:
name— Display name (e.g."Discord Bot")url— Callback URL (must be a valid HTTP/HTTPS URL)secret— Shared secret for signing payloads (HMAC-SHA256)events— Array of event types that trigger this webhook (e.g.["game.added"])
Example:
curl -X POST "http://localhost:5000/api/webhooks" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Discord Bot",
"url": "https://my-server.com/webhooks/jackbox",
"secret": "mysecret123",
"events": ["game.added"]
}'
The events array defines which events fire this webhook. Currently, the codebase triggers webhooks for game.added when a game is added to a session. The triggerWebhook function in backend/utils/webhooks.js is invoked from sessions.js on that event.
Update, Enable/Disable, Delete
- Update:
PATCH /api/webhooks/{id}— Changename,url,secret,events, orenabled - Disable:
PATCH /api/webhooks/{id}with"enabled": false— Stops delivery without deleting config - Delete:
DELETE /api/webhooks/{id}— Removes webhook and its logs
4. Webhook Delivery
How it works
When an event occurs (e.g. a game is added), the server:
- Finds all enabled webhooks subscribed to that event
- Sends an async HTTP POST to each webhook URL
- Logs each delivery attempt in
webhook_logs(status, error, payload)
Payload format
Each POST body is JSON:
{
"event": "game.added",
"timestamp": "2026-03-15T20:30:00.000Z",
"data": {
"session": { "id": 3, "is_active": true, "games_played": 2 },
"game": {
"id": 42,
"title": "Quiplash 3",
"pack_name": "Jackbox Party Pack 7",
"min_players": 3,
"max_players": 8,
"manually_added": false
}
}
}
Headers include:
Content-Type: application/jsonX-Webhook-Event: game.addedX-Webhook-Signature: sha256=<hmac>— Use yoursecretto verify the payload
View delivery logs
GET /api/webhooks/{id}/logs returns recent delivery attempts (status, error message, payload).
Test a webhook
POST /api/webhooks/test/{id} sends a dummy game.added event to the webhook URL. Delivery runs asynchronously; check logs for status.
5. WebSocket Events
The WebSocket server runs at /api/sessions/live on the same host and port as the HTTP API. See WebSocket protocol for connection, authentication, and subscription details.
Event types and audience
| Event | Broadcast to | Triggered by |
|---|---|---|
session.started |
All authenticated clients | POST /api/sessions |
game.added |
Session subscribers | POST /api/sessions/{id}/games |
session.ended |
Session subscribers | POST /api/sessions/{id}/close |
player-count.updated |
Session subscribers | PATCH /api/sessions/{sessionId}/games/{sessionGameId}/player-count |
vote.received |
Session subscribers | POST /api/votes/live (live votes only, not chat-import) |
session.started goes to every authenticated client. The others go only to clients that have subscribed to the relevant session via { "type": "subscribe", "sessionId": 3 }.
Envelope format
All events use this envelope:
{
"type": "<event-type>",
"timestamp": "2026-03-15T20:30:00.000Z",
"data": { ... }
}
data contains event-specific fields (session, game, player count, etc.) as described in WebSocket protocol.
6. Comparison
| Feature | Webhooks | WebSocket |
|---|---|---|
| Connection | Stateless HTTP | Persistent |
| Auth | Secret in config | JWT per connection |
| Events | game.added |
session.started, game.added, session.ended, player-count.updated, vote.received |
| Latency | Higher (HTTP round trip) | Lower (push) |
| Reliability | Logged, auditable | Best-effort |
7. Example: Discord Bot
Use a webhook to post game additions to a Discord channel. You’ll need:
- A webhook created in the Game Picker API pointing to your server
- A small server that receives the webhook and forwards to Discord’s Incoming Webhook
Webhook receiver (Node.js):
const crypto = require('crypto');
app.post('/webhooks/jackbox', express.json(), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = JSON.stringify(req.body);
// Verify HMAC-SHA256 using your webhook secret
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (signature !== expected) {
return res.status(401).send('Invalid signature');
}
if (req.body.event === 'game.added') {
const { session, game } = req.body.data;
const discordPayload = {
content: `🎮 **${game.title}** added to session #${session.id} (${game.min_players}-${game.max_players} players)`
};
fetch(process.env.DISCORD_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(discordPayload)
}).catch(err => console.error('Discord post failed:', err));
}
res.status(200).send('OK');
});
Register the Game Picker webhook with your server’s URL (e.g. https://my-bot.example.com/webhooks/jackbox), set events to ["game.added"], and use the same secret in your server’s WEBHOOK_SECRET.
Cross-references
- Webhooks endpoints — Full CRUD, request/response schemas, errors
- WebSocket protocol — Connection, auth, subscriptions, event payloads