# Webhooks Guide

> Set up event-driven integrations that react to Synento session events.

Set up event-driven integrations that react to Synento session events.

## Overview

Synento automatically emits webhook events for key lifecycle moments:
- `session.created` — a new session is created via API
- `session.started` — the first participant joins a session
- `session.ended` — all participants leave (last one departs)
- `recording.ready` — stream recording is fully persisted and replayable
- `export.completed` — MP4 export finishes successfully

## Setting Up a Webhook Receiver

### 1. Create the webhook endpoint on your server

```javascript
// Example: Express.js handler
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-synento-signature'] ?? '';

  // Verify HMAC-SHA256 over the raw body, exactly as received
  const crypto = require('crypto');
  const expected = crypto.createHmac('sha256', YOUR_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');

  if (signature.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature');
  }

  const payload = JSON.parse(req.body.toString());

  // Dispatch based on event type
  switch (payload.type) {
    case 'session.created':
      console.log('New session:', payload.session_id);
      break;
    case 'session.started':
      console.log('Session started:', payload.session_id);
      break;
    case 'session.ended':
      console.log('Session ended:', payload.session_id);
      break;
    case 'recording.ready':
      console.log('Recording ready:', payload.session_id);
      break;
    case 'export.completed':
      console.log('Export completed:', payload.session_id);
      break;
  }

  res.status(200).send('OK');
});
```

### 2. Register the webhook with Synento

```bash
curl -X POST https://api.synento.com/v1/webhooks \
  -H "Authorization: Bearer sk_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhook",
    "events": ["session.created", "session.started", "session.ended"]
  }'
```

### 3. List your webhooks to verify

```bash
curl https://api.synento.com/v1/webhooks \
  -H "Authorization: Bearer sk_live_xxxxx"
```

## Webhook Payload Structure

Each webhook event has the following structure:

```json
{
  "id": "<event-uuid>",
  "type": "session.started",
  "timestamp": "2025-01-01T12:00:00.000Z",
  "project_id": "<project-uuid>",
  "session_id": "<session-uuid>"
}
```

## Signature Verification

Every webhook includes an `X-Synento-Signature` header containing an HMAC-SHA256 digest of the raw request body, signed with your webhook's secret key.

Always verify this signature before processing — **never trust the payload without verification**.

The digest is lowercase hex. Compute it over the raw body bytes exactly as
they arrived — not over JSON you have parsed and re-serialised, which will not
match — and compare in constant time, as the receiver example above does.

## Event Delivery & Retry

Synento delivers each webhook with the following reliability guarantees:

| Aspect | Detail |
|--------|--------|
| Delivery model | Best-effort POST to your endpoint |
| Timeout | 5 seconds per attempt (AbortController) |
| Retry | A failed delivery (network error, timeout or non-2xx) is retried by a background job with exponential backoff, about 2 then 4 minutes apart. Pending retries are stored durably and survive a server restart. |
| Delivery log | Every attempt is recorded; see the Webhooks page in the dashboard |
| Ordering | Events for the same session are generally ordered but not guaranteed |

## Monitoring & Debugging

When testing locally:
1. Use ngrok or similar to expose your localhost endpoint
2. Register the public URL as the webhook target
3. Create a session and check your local logs for incoming webhooks

Once verified, switch to a stable production URL.

## Best Practices

1. **Always verify signatures** — unverified webhooks could be spoofed
2. **Respond quickly (200 OK)** — process the payload asynchronously; don't block on long operations
3. **Log all incoming events** — use logs to audit and debug event flows
4. **Handle idempotency** — if an event is delivered twice, your handler should handle it gracefully
5. **Use `session.started` + `session.ended`** — together they give you accurate session duration
