Files
jackboxpartypack-gamepicker/docs/api/guides/webhooks-and-events.md
cottongin 0d0d20161b docs: update remaining docs with vote tracking API changes
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
2026-03-15 19:21:35 -04:00

7.2 KiB
Raw Blame History

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 — Youre 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; theres 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} — Change name, url, secret, events, or enabled
  • 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:

  1. Finds all enabled webhooks subscribed to that event
  2. Sends an async HTTP POST to each webhook URL
  3. 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/json
  • X-Webhook-Event: game.added
  • X-Webhook-Signature: sha256=<hmac> — Use your secret to 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. Youll need:

  1. A webhook created in the Game Picker API pointing to your server
  2. A small server that receives the webhook and forwards to Discords 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 servers URL (e.g. https://my-bot.example.com/webhooks/jackbox), set events to ["game.added"], and use the same secret in your servers WEBHOOK_SECRET.


Cross-references