# Webhook guide

> Receive real-time event callbacks from Bookbag: lead.created, conversation.escalated, conversation.replied, action.run, campaign.sent. How to register, the payload shape, and verifying the HMAC signature.

Webhooks let Bookbag push events to your server the moment they happen — a new lead, an escalation, a campaign send — so you can sync a CRM, alert a channel, or trigger automation. This guide covers the developer workflow; for the dashboard setup screen see [Integrations: Webhooks](/docs/integrations/webhooks).

## Register an endpoint

A webhook is a workspace-level URL plus the list of events it subscribes to and a signing secret. Create one in the dashboard, or via the management endpoint:

| Field | Type | Notes |
| --- | --- | --- |
| url | string | Your HTTPS endpoint. Required. |
| events | array | Event names to subscribe to, or `["*"]` for all. Defaults to all. |
| secret | string | Signing secret. If you omit it, Bookbag generates one and returns it. |

```bash
curl https://app.bookbag.ai/webhooks \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/hooks/bookbag", "events": ["lead.created", "conversation.escalated"] }'
```

> **TIP:** Save the returned `secret` — you need it to verify signatures, and it is how you know a request really came from Bookbag.

## Events

Bookbag fires these events. Subscribe to the ones you need, or `*` for all.

| Event | Fires when | Key payload fields |
| --- | --- | --- |
| lead.created | A lead is captured by an agent. | `leadId`, `agentId`, `fields` |
| conversation.escalated | A conversation is handed off to a human / help desk. | `conversationId`, `target`, `transcript` |
| conversation.replied | The agent posts a reply in a conversation. | `conversationId` and reply context |
| action.run | An agent action/tool runs. | action run details |
| campaign.sent | An outbound campaign is sent. | `campaignId`, `agentId`, `recipients`, `message`, `channel` |

## Payload shape

Every delivery is a `POST` with a JSON body of the form `{ event, data, ts }`. `event` is the event name, `data` is the event-specific payload, and `ts` is the send time in epoch milliseconds.

```json
{
  "event": "lead.created",
  "data": {
    "leadId": 42,
    "agentId": 123,
    "fields": { "email": "sam@acme.com", "name": "Sam" }
  },
  "ts": 1717286400000
}
```

## Verify the signature

When a secret is set, Bookbag signs each delivery and sends the signature in the `X-Bookbag-Signature` header as `sha256=<hex>`. The signature is an **HMAC-SHA256 of the raw request body** keyed with your secret. Recompute it on your side and compare with a constant-time check — reject the request if it does not match.

```javascript
import crypto from "node:crypto";

// Read the RAW body — verify before any JSON middleware reparses it.
app.post("/hooks/bookbag",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const raw = req.body; // Buffer of the raw bytes
    const header = req.get("X-Bookbag-Signature") || "";
    const expected = "sha256=" + crypto
      .createHmac("sha256", process.env.BOOKBAG_WEBHOOK_SECRET)
      .update(raw)
      .digest("hex");

    const ok =
      header.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));
    if (!ok) return res.status(401).end();

    const { event, data } = JSON.parse(raw.toString("utf8"));
    // ... handle the event ...
    res.status(200).end();
  }
);
```

> **VERIFY AGAINST THE RAW BODY:** Compute the HMAC over the exact bytes received, before any framework re-serializes the JSON. Re-stringifying can reorder keys or change whitespace and break the signature.

## Best practices

- **Respond fast.** Return `2xx` quickly and process asynchronously; deliveries are best-effort.
- **Be idempotent.** Treat handlers as at-least-once; de-duplicate on the natural id in `data` (e.g. `leadId`).
- **Always verify.** Reject any request whose signature does not match your secret.
- **Use HTTPS.** Bookbag only delivers to reachable, safe URLs.

- [Integrations: Webhooks](/docs/integrations/webhooks) — The dashboard setup screen and management.
- [Get leads (v1)](/docs/api/v1/leads) — The lead.created source endpoint.
