# Guide to Anarlog Webhooks: Features and Best Practices

Anarlog webhooks push meeting data out of the Anarlog desktop app as soon as it exists: a recording finishes, or an AI summary is generated, and Anarlog sends the note, summaries, participants, action items and full transcript to your endpoint. If you use Anarlog to record and take notes on meetings, webhooks are how that content reaches a CRM, a task tracker or your own tools.

This guide covers how Anarlog webhooks are configured and signed, the events and payload, the delivery limits that come from running inside a desktop app, and the practices that keep an Anarlog receiver reliable.

## What are Anarlog webhooks?

Anarlog webhooks are built into the desktop app and available on every plan. You add an `https://` endpoint in the app's settings, and from then on the app sends a JSON `POST` to it for every event.

Because the app sends them, deliveries only happen while the app is open. Anarlog is explicit that events which happen while it's closed "are dropped, not queued for later delivery".

Each request carries an `x-anarlog-signature` header: `sha256=` followed by the hex HMAC-SHA256 of the raw body. The key is the endpoint's signing secret, which starts with `whsec_`. Despite that prefix, this is not Standard Webhooks. The whole string, prefix included, is the HMAC key, and nothing else is signed.

## Anarlog webhook features

| Feature | Details |
| --- | --- |
| Configuration | Desktop app, **Settings > Developers > Webhooks** |
| Plans | All |
| Subscription | Every endpoint receives all events |
| Endpoint limit | 64 per installation |
| Signature header | `x-anarlog-signature` |
| Scheme | HMAC-SHA256, lowercase hex, prefixed `sha256=` |
| Signed content | The raw request body, nothing else |
| Secret | `whsec_...` per endpoint, shown once, used whole as the key |
| Other headers | `x-anarlog-event`, `x-anarlog-delivery`, `x-anarlog-timestamp` (not signed) |
| Response window | 10 seconds for meeting events |
| Retries | After 5 seconds and after 30 seconds. `webhook.test` makes one attempt |
| Concurrency | At most 4 deliveries at a time |
| Availability | Only while the desktop app is open |

Deliveries come from each user's own computer, so there are no fixed source IPs to allow.

## Common events

| Event | Fires when |
| --- | --- |
| `meeting.completed` | A recording session finishes |
| `note.enhanced` | An AI summary is generated for a meeting |
| `webhook.test` | You click **Test** on an endpoint |

Every event uses the same envelope. This is Anarlog's sketch of it, with the values it leaves out shown as placeholders:

```json
{
  "id": "evt_...",
  "event": "note.enhanced",
  "created_at": "2026-07-28T09:00:00.000Z",
  "data": {
    "meeting": {
      "id": "...",
      "title": "...",
      "note": {},
      "summaries": [],
      "participants": [],
      "action_items": []
    },
    "transcript_text": "..."
  }
}

```

The event name is in both the body's `event` field and the `x-anarlog-event` header, so you can route without parsing the body. Anarlog doesn't publish a complete example, so send a test and log what arrives before you rely on the fields inside `meeting`.

Anarlog warns that these payloads "include the note, summaries, participants, action items, and full transcript text". Treat every endpoint as a place where meeting content is stored.

## Setting up Anarlog webhooks

1. In the Anarlog desktop app, open **Settings > Developers > Webhooks**.
2. Enter an `https://` URL and click **Add webhook**.
3. Copy the signing secret straight away. It starts with `whsec_` and is shown only once. Store it server-side, for example as `ANARLOG_WEBHOOK_SECRET`.
4. Click **Test** to send one `webhook.test` delivery.

If you receive Anarlog webhooks through Hookdeck, create a source with the Anarlog type and paste the whole signing secret, including the `whsec_` prefix, since Anarlog uses it as a plain HMAC key. Use the source URL as the endpoint in Anarlog.

## Securing Anarlog webhooks

Read `x-anarlog-signature`, check it starts with `sha256=`, compute the hex HMAC-SHA256 of the raw body with the full secret string as the key, and compare in constant time. Don't strip `whsec_` or base64-decode the secret, and don't use a Standard Webhooks or Svix library.

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

function verifyAnarlogSignature(rawBody, signatureHeader, secret) {
  if (!signatureHeader || !secret || !signatureHeader.startsWith('sha256=')) return false;
  // The whole secret, whsec_ included, is the key.
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const received = Buffer.from(signatureHeader.slice('sha256='.length).trim().toLowerCase(), 'utf8');
  const computed = Buffer.from(expected, 'utf8');
  return received.length === computed.length && crypto.timingSafeEqual(received, computed);
}

const app = express();

app.post('/webhooks/anarlog', express.raw({ type: '*/*' }), (req, res) => {
  const secret = process.env.ANARLOG_WEBHOOK_SECRET;
  if (!secret) return res.sendStatus(500); // fail closed

  if (!verifyAnarlogSignature(req.body, req.get('x-anarlog-signature'), secret)) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString('utf8'));
  // Optional: reject events whose signed created_at is older than you accept.
  // Deduplicate on event.id, then route on event.event.
  res.sendStatus(200);
});

```

The same check in Python:

```python
import hashlib
import hmac

def verify_anarlog_signature(raw_body: bytes, signature_header, secret: str) -> bool:
    if not signature_header or not secret or not signature_header.startswith("sha256="):
        return False
    received = signature_header[len("sha256="):].strip().lower().encode("utf-8")
    expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest().encode("utf-8")
    return hmac.compare_digest(received, expected)

# In FastAPI: raw_body = await request.body()
# verify_anarlog_signature(raw_body, request.headers.get("x-anarlog-signature"),
#                          os.environ["ANARLOG_WEBHOOK_SECRET"])

```

`x-anarlog-timestamp` is not covered by the signature, so anyone replaying a request can change it. For replay protection, Anarlog suggests checking the signed `created_at` in the body and storing each payload `id`. If you check `created_at`, remember that a delivery retried or replayed later, by Anarlog or by a gateway in front of you, carries the original value.

> **Make Anarlog webhooks production-ready.** [Hookdeck Event Gateway](/event-gateway) verifies the `x-anarlog-signature` header with your whole `whsec_` secret, answers within Anarlog's 10-second window, and durably queues every meeting event before your service sees it.

## Anarlog webhook limitations and pain points

### A `whsec_` secret that isn't Standard Webhooks

**The Problem:** Verification fails on every delivery, even with the correct secret.

**Why It Happens:** The `whsec_` prefix suggests Standard Webhooks, whose libraries strip it, base64-decode the rest and expect `webhook-*` headers. Anarlog uses the full string as a plain HMAC key over the body alone.

**Workarounds:**

* Use a plain HMAC-SHA256 with the whole secret, as above.
* Read `x-anarlog-signature`, not `webhook-signature`.

**How Hookdeck Can Help:** Hookdeck's Anarlog source uses the secret exactly as you paste it. Hookdeck answers Anarlog with a `200` either way, then records a request that fails the check as failed verification, so it never reaches your destination.

### Closing the app loses events

**The Problem:** A meeting that finished while the app was closed never reaches your systems.

**Why It Happens:** The desktop app is the sender. Anarlog doesn't queue events for later, so anything that happens while the app is closed is dropped.

**Workarounds:**

* Keep the app running on machines whose meetings you need to capture.
* Reconcile periodically through Anarlog's CLI, local MCP server or Cloud API, which Anarlog offers for reading meeting data.

**How Hookdeck Can Help:** Hookdeck can't send events the app never produced, but once a delivery reaches Hookdeck it is stored, so your own outages no longer add to the losses.

### Three attempts in 35 seconds

**The Problem:** A brief deploy or restart on your side loses meeting events.

**Why It Happens:** Anarlog waits 10 seconds, then retries after 5 seconds and again after 30 seconds. After that the event is gone, and the test event gets no retries at all.

**Workarounds:**

* Return `200` straight after verification and do the work in the background.
* Run more than one instance of your receiver so a deploy doesn't take it offline.

**How Hookdeck Can Help:** Hookdeck accepts each delivery as it arrives, well inside Anarlog's window, and then delivers to your service with [retries](/docs/retries) you configure, so a short outage costs latency rather than meeting notes.

### Every endpoint gets everything

**The Problem:** An endpoint that only needs action items receives full transcripts too.

**Why It Happens:** New endpoints are subscribed to all events, and every meeting event carries the whole meeting, including `transcript_text`.

**Workarounds:**

* Point Anarlog only at endpoints you trust with the full content.
* Strip what each consumer doesn't need before passing events on.

**How Hookdeck Can Help:** A Hookdeck [transformation](/docs/transformations) can remove `transcript_text` or other fields before delivery, and [filters](/docs/filters) on `x-anarlog-event` send each event type only where it's needed.

## Best practices

### Verify against the raw body

The signature covers the exact bytes the app sent. In Express, use `express.raw({ type: '*/*' })`. In Next.js, use `await request.text()`. In FastAPI, use `await request.body()`.

### Deduplicate on the payload `id`

Retries resend the same event. Store each `id` before processing and skip any you have already handled, as Anarlog recommends. `x-anarlog-delivery` identifies the delivery, while `id` identifies the event. See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

### Handle both meeting events

`meeting.completed` fires when recording stops, and `note.enhanced` fires when the AI summary exists. If you need the summary, act on `note.enhanced`. If you need speed, act on `meeting.completed` and update the record when the summary arrives.

### Acknowledge fast and process asynchronously

Anarlog gives you 10 seconds. Verify, store, return `200`, and push to your CRM afterwards. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

## Conclusion

Anarlog signs each delivery with `x-anarlog-signature: sha256=<hex>`, an HMAC-SHA256 of the raw body keyed with the whole `whsec_` secret. Verify it with a plain HMAC rather than a Standard Webhooks library, deduplicate on the payload `id`, treat every endpoint as holding full meeting content, and plan around a desktop sender that drops events while it's closed and stops retrying after 35 seconds.

[Hookdeck Event Gateway](https://hookdeck.com) sits in front of your receiver, verifies every Anarlog signature, and holds each meeting event until your service has processed it.

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