# 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=<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)

