Webhooks
Push Bookbag events to your own endpoints in real time. Covers the event catalog, the signed payload format, HMAC-SHA256 signature verification, and delivery behavior.
View as MarkdownWebhooks let Bookbag push events to your own systems the moment they happen. Register a URL, choose which events to subscribe to, and Bookbag POSTs a signed JSON payload to that URL whenever a matching event fires — no polling required.
Webhooks are configured per workspace by an admin. Each webhook has a URL, a list of subscribed events, and a signing secret. The secret is shown when you create the webhook — store it safely.
Create a webhook
- 1Add an endpoint URLIn your workspace integrations, add a webhook with the HTTPS URL Bookbag should POST to.
- 2Choose eventsSubscribe to specific events, or use the wildcard
*to receive everything. - 3Save the signing secretBookbag generates a secret (or you can supply one). Use it to verify that incoming requests really came from Bookbag.
Events
Subscribe to the events your integration cares about. Subscribing to * delivers all events.
| Event | Fires when |
|---|---|
conversation.replied | The agent posts a reply in a conversation. |
conversation.ended | A conversation is closed or resolved. |
lead.created | A lead is captured via the collect-leads action. |
action.run | An action (custom action, escalation, etc.) executes. |
Start narrow — subscribe only to the events you'll act on. You can always widen the subscription later.
Payload format
Every delivery is a POST with a JSON body containing the event name, a data object, and a millisecond timestamp:
{
"event": "conversation.replied",
"data": {
"conversation_id": "conv_123",
"agent_id": 42,
"message": "Your order shipped today."
},
"ts": 1733155200000
}Verifying signatures
Each request includes an X-Bookbag-Signature header so you can confirm it came from Bookbag and wasn't tampered with. The value is sha256= followed by the HMAC-SHA256 of the raw request body, keyed with your webhook secret.
Compute the HMAC over the exact bytes you received, before any JSON parsing or re-serialization. Re-stringifying the body can change whitespace and break the signature. Always compare using a constant-time equality check.
import crypto from 'node:crypto';
function verify(rawBody, header, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const a = Buffer.from(header || '');
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Express: use the raw body, not the parsed object.
app.post('/bookbag-webhook', express.raw({ type: 'application/json' }), (req, res) => {
if (!verify(req.body, req.get('X-Bookbag-Signature'), process.env.BOOKBAG_WEBHOOK_SECRET)) {
return res.status(401).end();
}
const payload = JSON.parse(req.body.toString());
// handle payload.event ...
res.status(200).end();
});Delivery behavior
- Respond 2xx quickly. Acknowledge receipt fast and do heavy work asynchronously so deliveries don't time out.
- Be idempotent. Design handlers so a duplicate delivery is harmless.
- HTTPS only. Endpoints must be reachable public HTTPS URLs.
For event listeners on the front end (reacting to widget events in the browser) and the deeper developer webhook guide, see the developer docs.