# Guide to Digital Samba Webhooks: Features and Best Practices

Digital Samba webhooks connect your backend to what happens inside your video rooms. When a participant joins or leaves, a session starts or ends, a recording is ready, or someone asks a question in Q&A, Digital Samba sends the event to your server, so you can track attendance, download recordings, post transcripts or bill for usage without polling the API.

This guide covers how Digital Samba webhooks are created, the events and payload they carry, how to authenticate them with the `authorization_header` secret, and the practices that keep a receiver reliable when the delivery details are undocumented.

## What are Digital Samba webhooks?

A Digital Samba webhook is a subscription you create through the REST API. It has an `endpoint` URL, an `events` array, an optional `name` and an optional `authorization_header`. When one of the selected events happens, Digital Samba makes an HTTP `POST` to the endpoint with a JSON body.

Digital Samba doesn't sign webhooks. The `authorization_header` property is a secret that only your backend and Digital Samba know, and Digital Samba sends it on every request as `Authorization: Bearer <secret>`. The field is optional, but without it a request carries nothing you can check, and Digital Samba itself calls it highly recommended.

## Digital Samba webhook features

| Feature | Details |
| --- | --- |
| Configuration | REST API: `POST /api/v1/webhooks` to create, `PATCH /api/v1/webhooks/:id` to edit, plus list and delete |
| Method and format | `POST`, JSON |
| Event field | `eventType` in the body |
| Envelope | `eventType`, `sessionId`, `roomId`, `timestamp`, `data` |
| Timestamp | Unix seconds, when the event happened |
| Authentication | Optional `authorization_header` secret, sent as `Authorization: Bearer <secret>` |
| Signature | None |
| Documented events | 39, with more to be added |
| Retries and timeout | Not documented |
| Source IPs | Not published |

## Events

Digital Samba lists 39 events. Grouped by area:

| Area | Events |
| --- | --- |
| Participants | `participant_joined`, `participant_left`, `participant_invited`, `participant_hand_raised`, `participant_hand_lowered`, `participant_waiting_joined`, `participant_waiting_left`, `participant_avatar_uploaded`, `participant_avatar_deleted` |
| Sessions | `session_started`, `session_ended`, `session_summary_ready`, `session_transcript_ready` |
| Recordings | `recording_started`, `recording_stopped`, `recording_ready`, `recording_deleted` |
| Library | `file_added_to_library`, `file_renamed`, `file_deleted_from_library`, `file_processing_failed`, `folder_added_to_library`, `folder_renamed`, `folder_deleted_from_library` |
| Q&A | `question_asked`, `question_answered`, `question_updated`, `question_dismissed`, `question_reopened`, `question_deleted`, `answer_updated`, `answer_deleted` |
| Phone participants | `phone_participant_muted`, `phone_participant_unmuted`, `phone_participant_asked_to_unmute` |
| Rooms and roles | `room_locked`, `room_unlocked`, `room_deleted`, `role_deleted` |

The only payload Digital Samba documents is `participant_joined`:

```json
{
  "eventType": "participant_joined",
  "sessionId": "4fcc375c-e0ce-4f52-9b35-3a034042ab5a",
  "roomId": "7f8c8eac-6643-47db-8d93-e7633e6e53ec",
  "timestamp": 1675790387,
  "data": {
    "participantId": "8b085f23-9a2a-465d-bdbb-73a233c7df90",
    "name": "John Smith",
    "role": "moderator"
  }
}

```

The envelope is the same for every event; `data` changes with `eventType`. Capture one of each event you subscribe to before you write the parser for it.

> **Inspect Digital Samba webhooks as they arrive.** Create a webhook with a [Hookdeck Inspect](https://inspect.hookdeck.com) URL as its `endpoint` to see the exact headers and the `data` shape of each event, with no account or setup required.

## Setting up Digital Samba webhooks

Generate a long random secret first, for example with `openssl rand -hex 32`. Then create the webhook with your team ID and developer key:

```bash
curl --request POST \
  --url https://api.digitalsamba.com/api/v1/webhooks \
  --user YOUR_TEAM_ID:YOUR_DEVELOPER_KEY \
  --header "Content-Type: application/json" \
  --data '{
    "endpoint": "https://example.com/webhooks/digital-samba",
    "events": ["participant_joined", "participant_left", "recording_ready"],
    "name": "Production webhook",
    "authorization_header": "YOUR_LONG_RANDOM_SECRET"
  }'

```

The response includes the webhook's `id`. To add or change the secret later, send `PATCH /api/v1/webhooks/:id` with a new `authorization_header`. Note that the API returns `authorization_header` in plain text in its responses, including the list endpoint, so anyone with your developer key can read it.

**With Hookdeck.** Digital Samba is a supported source in Hookdeck. Its setup note reads: "Set authorization_header on the webhook through the Digital Samba REST API, then paste the same secret." Create a source with the **Digital Samba** type and use its URL as the webhook `endpoint`. In **Authorization Secret**, paste only the secret you set as `authorization_header`, without `Bearer `. Digital Samba sends `Bearer <secret>`, and the source checks that header. The source accepts `POST` only, which is what Digital Samba sends. A request with a missing or wrong secret still gets a `200` from Hookdeck, but Hookdeck marks it as failed verification and doesn't deliver it to your service.

For local development, `hookdeck listen 3000 digital-samba --path /webhooks/digital-samba` gives you a public URL that forwards to your machine.

## Securing Digital Samba webhooks

Check that the `Authorization` header uses the `Bearer` scheme, then compare the token with your stored secret in constant time.

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

const SECRET = process.env.DIGITAL_SAMBA_WEBHOOK_SECRET; // the bare authorization_header value

function verifyBearer(header) {
  if (!SECRET || typeof header !== 'string') return false;
  const match = /^bearer (.+)$/i.exec(header);
  if (!match) return false;
  const a = Buffer.from(match[1], 'utf8');
  const b = Buffer.from(SECRET, 'utf8');
  // timingSafeEqual throws on unequal lengths, so check first.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

const app = express();

app.post('/webhooks/digital-samba', express.raw({ type: '*/*' }), (req, res) => {
  if (!verifyBearer(req.get('authorization'))) {
    return res.sendStatus(401);
  }

  let event;
  try {
    event = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  if (event.eventType === 'recording_ready') {
    // Queue a job to fetch the recording for event.roomId / event.sessionId.
  }

  res.sendStatus(200);
});

```

The same check in Python:

```python
import hmac
import os

SECRET = os.environ.get("DIGITAL_SAMBA_WEBHOOK_SECRET", "")

def verify_digital_samba_request(headers) -> bool:
    if not SECRET:
        return False  # never accept when unconfigured
    header = headers.get("Authorization", "")
    scheme, _, token = header.partition(" ")
    if scheme.lower() != "bearer" or not token:
        return False
    # Compare bytes: compare_digest raises TypeError on a non-ASCII str.
    return hmac.compare_digest(token.encode("utf-8"), SECRET.encode("utf-8"))

```

The Bearer secret is static. It authenticates the sender but doesn't cover the body, so a modified payload still passes, and the body's `timestamp` isn't protected, so a captured request can be replayed. Use an HTTPS endpoint and a long random secret, and rotate it with `PATCH` if it might have leaked.

> **Make Digital Samba webhooks production-ready.** [Hookdeck Event Gateway](/event-gateway) checks the Bearer secret on every request, stores each event durably, and gives you retries and replay that Digital Samba doesn't document.

## Digital Samba webhook limitations and pain points

### Delivery behaviour is undocumented

**The Problem:** You don't know how long Digital Samba waits for a response, whether it retries, or how often.

**Why It Happens:** The webhook pages describe creation and the payload only. Retries, timeouts and expected status codes aren't covered.

**Workarounds:**

* Respond with a `200` as quickly as possible and process afterwards, so a slow handler never causes a failure.
* Treat a missed `recording_ready` or `session_ended` as possible, and reconcile with the REST API on a schedule.

**How Hookdeck Can Help:** Hookdeck accepts each request as it arrives and stores it before your service sees it. Delivery to your service then follows [retries](/docs/retries) you configure, and failures raise [issues](/docs/issues), so an outage on your side doesn't depend on Digital Samba's undocumented behaviour.

### Only one payload is documented

**The Problem:** You have to guess the `data` shape for 38 of the 39 events.

**Why It Happens:** The docs show one example, `participant_joined`.

**Workarounds:**

* Point a test webhook at a request inspector and trigger each event you need in a test room.
* Parse `data` defensively and log unknown fields rather than failing.

**How Hookdeck Can Help:** Hookdeck stores every request with its body, so you can capture real events once and replay them against your handler while you build it.

### The secret is readable through the API

**The Problem:** Your webhook secret is as exposed as your developer key.

**Why It Happens:** Create, edit and list responses return `authorization_header` in plain text.

**Workarounds:**

* Restrict who holds the developer key, and don't log API responses that list webhooks.
* Rotate the secret with `PATCH` if the developer key leaks.

**How Hookdeck Can Help:** Hookdeck checks the secret for you and records failed-verification requests, so a sudden spike of rejected requests shows up in your logs.

## Best practices

### Always set `authorization_header`

It's optional in the API, but a webhook without it sends nothing to verify, and Hookdeck's source can't authenticate it.

### Subscribe only to what you use

The `events` array decides what you receive. Narrow it to the events your service acts on, and add new ones deliberately as Digital Samba adds them.

### Deduplicate deliveries

Digital Samba doesn't say whether it retries, so plan for the same event arriving twice. It doesn't send a delivery ID either. Build a key from `eventType`, `sessionId`, `timestamp` and an ID in `data`, such as `participantId`. See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

### Acknowledge fast and process asynchronously

Download recordings and transcripts in a background job, not in the request handler. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

## Conclusion

Digital Samba webhooks are created through the REST API and send a JSON `POST` with `eventType`, `sessionId`, `roomId`, `timestamp` and `data`. They aren't signed, so set an `authorization_header` secret, check `Authorization: Bearer <secret>` in constant time, and plan for undocumented retries and payloads by acknowledging fast, deduplicating and capturing real events before you parse them.

[Hookdeck Event Gateway](https://hookdeck.com) checks the secret, stores every event, and retries and replays delivery to your service on your terms.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Digital Samba webhooks reliably in minutes.