Files
jackboxpartypack-gamepicker/docs/archive/API_QUICK_REFERENCE.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

9.0 KiB

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:

POST /api/auth/login
Body: { "key": "YOUR_ADMIN_KEY" }

WebSocket Events

Connect to WebSocket

ws://localhost:5000/api/sessions/live

Message Protocol:

// Authenticate
{ "type": "auth", "token": "YOUR_JWT_TOKEN" }

// Subscribe to session
{ "type": "subscribe", "sessionId": 123 }

// Unsubscribe
{ "type": "unsubscribe", "sessionId": 123 }

// Heartbeat
{ "type": "ping" }

Server Events:

// 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

GET /api/votes?session_id=5&game_id=42&username=viewer123&vote_type=up&page=1&limit=50

Response (200):

{
  "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

POST /api/votes/live

Request Body:

{
  "username": "string",
  "vote": "up" | "down",
  "timestamp": "2025-11-01T20:30:00Z"
}

Success Response (200):

{
  "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

GET /api/webhooks

Response:

[
  {
    "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

GET /api/webhooks/:id

Create Webhook

POST /api/webhooks

Request Body:

{
  "name": "Kosmi Bot",
  "url": "http://bot-url/webhook",
  "secret": "your_shared_secret",
  "events": ["game.added"]
}

Response (201):

{
  "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

PATCH /api/webhooks/:id

Request Body (all fields optional):

{
  "name": "Updated Name",
  "url": "http://new-url/webhook",
  "secret": "new_secret",
  "events": ["game.added"],
  "enabled": false
}

Delete Webhook

DELETE /api/webhooks/:id

Response (200):

{
  "message": "Webhook deleted successfully",
  "webhookId": 1
}

Test Webhook

POST /api/webhooks/test/:id

Sends a test game.added event to verify webhook is working.

Response (200):

{
  "message": "Test webhook sent",
  "note": "Check webhook_logs table for delivery status"
}

Get Webhook Logs

GET /api/webhooks/:id/logs?limit=50

Response:

[
  {
    "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=<hmac_signature>
  • X-Webhook-Event: session.started
  • User-Agent: Jackbox-Game-Picker-Webhook/1.0

Payload:

{
  "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=<hmac_signature>
  • X-Webhook-Event: game.added
  • User-Agent: Jackbox-Game-Picker-Webhook/1.0

Payload:

{
  "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=<hmac_signature>
  • X-Webhook-Event: session.ended
  • User-Agent: Jackbox-Game-Picker-Webhook/1.0

Payload:

{
  "event": "session.ended",
  "timestamp": "2025-11-01T20:30:00Z",
  "data": {
    "session": {
      "id": 123,
      "is_active": 0,
      "games_played": 5
    }
  }
}

cURL Examples

Submit Vote

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

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

curl -X POST "http://localhost:5000/api/webhooks/test/1" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

View Webhook Logs

curl -X GET "http://localhost:5000/api/webhooks/1/logs?limit=10" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Webhook Signature Verification

Node.js Example:

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