docs: comprehensive API documentation from source code
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
This commit is contained in:
474
docs/archive/API_QUICK_REFERENCE.md
Normal file
474
docs/archive/API_QUICK_REFERENCE.md
Normal file
@@ -0,0 +1,474 @@
|
||||
# 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 }
|
||||
}
|
||||
}
|
||||
|
||||
// Pong
|
||||
{ "type": "pong" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Live Voting
|
||||
|
||||
### 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=<hmac_signature>`
|
||||
- `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=<hmac_signature>`
|
||||
- `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=<hmac_signature>`
|
||||
- `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)
|
||||
|
||||
Reference in New Issue
Block a user