# Anarlog Webhooks

## When to Use This Skill

* How do I receive Anarlog webhooks?
* How do I verify the `x-anarlog-signature` header?
* How do I handle `meeting.completed` and `note.enhanced` events (push meeting notes and action items to a CRM, Slack, or a task tracker)?
* Why is my Anarlog webhook signature verification failing?
* Why can't I use the Standard Webhooks or Svix libraries with an Anarlog `whsec_` secret?

## Verification (core)

Anarlog signs the **raw request body** with HMAC-SHA256 and sends `x-anarlog-signature: sha256=<hex>`. The signing secret looks like `whsec_...` but this is **not** Standard Webhooks: the HMAC key is the **entire secret string, `whsec_` prefix included, as UTF-8**. Do not strip the prefix, do not base64-decode it, and do not use the `standardwebhooks` or `svix` libraries.

`x-anarlog-timestamp` (Unix seconds) is sent but is **not** part of the signed content.

Node (check for exactly 64 hex chars before `Buffer.from(x, 'hex')`, which silently truncates invalid hex):

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

function verify(rawBody, signatureHeader, secret) {
  if (typeof signatureHeader !== 'string' || !secret) return false;
  if (!signatureHeader.startsWith('sha256=')) return false;
  const received = signatureHeader.slice('sha256='.length);
  if (!/^[0-9a-f]{64}$/i.test(received)) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
  return crypto.timingSafeEqual(Buffer.from(received, 'hex'), expected);
}

```

Python (compare bytes so a non-ASCII header returns 401, not a 500):

```python
import hmac, hashlib

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

```

In Express use `express.raw({ type: '*/*' })` so the body stays a `Buffer` whatever the content type.

> **For complete handlers with route wiring, event dispatch, and tests**, see:
> 
> * [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/anarlog-webhooks/examples/express/)
> * [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/anarlog-webhooks/examples/nextjs/)
> * [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/anarlog-webhooks/examples/fastapi/)

## Common Event Types

| Event (`event` field) | Fires when |
| --- | --- |
| `meeting.completed` | A recording session finishes |
| `note.enhanced` | An AI summary is generated for a meeting |
| `webhook.test` | You click **Test** on the endpoint in Anarlog settings (one attempt, no retries) |

Envelope: `{ "id": "evt_...", "event": "...", "created_at": "...", "data": { "meeting": { id, title, note, summaries, participants, action_items }, "transcript_text": "..." } }`. Meeting events carry the full note and transcript text, so only point endpoints you trust at it.

## Important Headers

| Header | Description |
| --- | --- |
| `x-anarlog-signature` | `sha256=<hex>` HMAC-SHA256 of the raw body, keyed with the whole `whsec_...` secret |
| `x-anarlog-event` | Event name |
| `x-anarlog-delivery` | Unique delivery ID (use for idempotency, with the payload `id`) |
| `x-anarlog-timestamp` | Unix seconds. Not signed; informational only |

Deliveries time out after 10 seconds and retry after 5 and 30 seconds. Return `200` quickly and do the work asynchronously. Deliveries only run while the Anarlog desktop app is open; events that occur while it is closed are dropped, not queued.

## Environment Variables

```bash
ANARLOG_WEBHOOK_SECRET=whsec_your_signing_secret   # Full value including the whsec_ prefix

```

## Setup

In Anarlog go to **Settings → Developers → Webhooks**, enter an `https://` URL, click **Add webhook**, and copy the signing secret (shown once). Click **Test** to send a `webhook.test` delivery. See [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/anarlog-webhooks/references/setup.md).

When using Hookdeck, paste the **whole signing secret, including the `whsec_` prefix**, into the source's webhook secret. Anarlog uses it as a plain HMAC key.

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 anarlog --path /webhooks/anarlog

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/anarlog-webhooks/references/overview.md) - Anarlog webhook concepts and payload
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/anarlog-webhooks/references/setup.md) - Configuration guide
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/anarlog-webhooks/references/verification.md) - Signature verification details