# Digital Samba Webhooks

Digital Samba (embedded video conferencing) sends a JSON webhook when something happens in a room: a participant joins or leaves, a session starts or ends, a recording or summary becomes ready. Webhooks are **not signed**. If you set an `authorization_header` secret on the webhook, every POST carries it as `Authorization: Bearer <secret>`, and your endpoint authenticates the request by comparing it with the secret you stored.

## When to Use This Skill

* How do I receive Digital Samba webhooks?
* How do I verify the `Authorization: Bearer` secret on a Digital Samba webhook?
* Does Digital Samba sign webhook payloads? (No: it sends a static bearer secret)
* How do I handle `participant_joined`, `session_ended` or `recording_ready` events?
* Why does my Digital Samba webhook endpoint return 401?

## Setup

Set authorization_header on the webhook through the Digital Samba REST API, then paste the same secret.

`authorization_header` is optional and is set when creating or editing the webhook through the REST API. It is highly recommended: without it the request carries nothing to check. See [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/digital-samba-webhooks/references/setup.md).

## Verification (core)

```javascript
const crypto = require('crypto');

function verifyDigitalSambaRequest(headers, secret = process.env.DIGITAL_SAMBA_WEBHOOK_SECRET) {
  if (!secret) return false;                                        // fail closed
  const expected = /^bearer /i.test(secret) ? secret.slice(7) : secret; // never trim the secret
  const header = headers['authorization'];
  if (!expected || typeof header !== 'string' || !/^bearer /i.test(header)) return false;

  const received = Buffer.from(header.slice(7), 'latin1');
  const wanted = Buffer.from(expected, 'utf8');
  if (received.length !== wanted.length) return false;              // timingSafeEqual throws otherwise
  return crypto.timingSafeEqual(received, wanted);
}

```

* Header: `Authorization: Bearer <secret>`. Store the **bare** secret in `DIGITAL_SAMBA_WEBHOOK_SECRET`; if the configured value itself starts with `Bearer `, strip it. The `Bearer` scheme matches in any case.
* Missing header, wrong scheme or mismatch: `401`. Body that isn't a JSON object: `400`. Handled: `200`.
* Python: `hmac.compare_digest` on **bytes** (encode both sides; `str` raises on non-ASCII).
* Read the body raw (`express.raw({ type: '*/*' })`) so any content type works, authenticate first, parse after.
* **A static shared credential authenticates the sender, not the body, and gives no replay protection** (no signature, no timestamp). Use HTTPS, make handlers idempotent, and rotate the `authorization_header` secret if it leaks.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/digital-samba-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/digital-samba-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/digital-samba-webhooks/examples/fastapi/).

## Event Types

The payload is `{ eventType, sessionId, roomId, timestamp, data }` (`timestamp` in Unix seconds). Dispatch on `eventType`. The examples handle:

| Event | Sent When |
| --- | --- |
| `participant_joined` | A participant joins a room |
| `participant_left` | A participant leaves a room |
| `session_started` | A session starts |
| `session_ended` | A session ends |
| `recording_started` | A recording starts |
| `recording_stopped` | A recording stops |
| `recording_ready` | A recording is ready |
| `recording_deleted` | A recording is deleted |
| `session_summary_ready` | A session summary is ready |
| `session_transcript_ready` | A session transcript is ready |

Digital Samba defines more (library files and folders, Q&A, hand raise, room lock, waiting room, phone participants). See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/digital-samba-webhooks/references/overview.md); unknown events are acknowledged with `200`.

## Environment Variables

```bash
DIGITAL_SAMBA_WEBHOOK_SECRET=your-authorization-header-secret   # the bare secret, without "Bearer "

```

## Local Development

Digital Samba can't reach `localhost`. Tunnel with the Hookdeck CLI (no account required):

```bash
npx hookdeck-cli listen 3000 digital-samba --path /webhooks/digital-samba

```

Use the printed URL as the webhook endpoint when you create the webhook.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/digital-samba-webhooks/references/overview.md) - What Digital Samba webhooks are, events and payload shape
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/digital-samba-webhooks/references/setup.md) - Create the webhook and set authorization_header
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/digital-samba-webhooks/references/verification.md) - Bearer check details, gotchas, debugging 401s
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/digital-samba-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests