Webhooks
Vaultbase ships outbound webhooks — when records change (or your hook code says so), it POSTs JSON to URLs you configure. Each delivery is HMAC-SHA-256 signed, retried on failure, and logged for audit.
Subscribe to events
Section titled “Subscribe to events”A webhook lists which events it cares about. Patterns:
| Pattern | Matches |
|---|---|
posts.create | exact |
posts.* | every event under posts |
* | everything |
Built-in events fired automatically:
<collection>.create— after a record is created<collection>.update— after a record is updated<collection>.delete— after a record is deleted
Custom events fired from hooks/routes/cron:
await ctx.helpers.webhooks.dispatch("billing.invoice_paid", { customer: ctx.record.customer_id, amount: ctx.record.amount,});Delivery payload
Section titled “Delivery payload”{ "id": "<uuid>", "event": "posts.create", "timestamp": 1735000000, "data": { "record": { "id": "...", "title": "...", ... } }}Headers on every request:
| Header | Notes |
|---|---|
X-Vaultbase-Event | event label, e.g. posts.create |
X-Vaultbase-Delivery | unique delivery id (also visible in admin) |
X-Vaultbase-Timestamp | unix-seconds when the request was prepared |
X-Vaultbase-Signature | sha256=<hex> of HMAC over <timestamp>.<body> |
Plus any custom headers you configured (Authorization, X-API-Key, …).
Verifying deliveries (receiver side)
Section titled “Verifying deliveries (receiver side)”const expected = "sha256=" + hmacSha256(secret, `${ts}.${rawBody}`);if (!constantTimeEqual(expected, header)) reject();if (Date.now() / 1000 - parseInt(ts) > 300) reject(); // replay windowAlways compare in constant time, and reject deliveries with a stale timestamp (5-minute window is a sane default).
Retry & dead-letter
Section titled “Retry & dead-letter”Each webhook configures:
- Retry max — attempts before giving up (default 3)
- Retry backoff —
exponential(1s, 2s, 4s, …) orfixed - Retry delay (ms) — base delay
- Timeout (ms) — per-attempt request timeout (default 30 s)
Failure progression: pending → pending (retry scheduled) → … → dead.
A 2xx response marks the delivery succeeded and the row is finalised.
Anything else (network error, 4xx, 5xx) increments the attempt counter
and reschedules — until retry_max, after which the row goes to dead.
The admin UI shows recent deliveries per webhook: status badge, attempt count, response code, timestamps. Click a row for the full payload and response body (truncated to 2 KB).
SSRF guard
Section titled “SSRF guard”Outbound URLs pass through the same egress CIDR filter as
helpers.http. Default-deny RFC1918 / loopback / link-local. Override
via Hook egress settings.
If a webhook’s URL resolves into a denied range, the delivery goes
directly to dead with error: "egress blocked: …" — no retry, no
network call.
REST API
Section titled “REST API”GET /api/v1/admin/webhooks — listGET /api/v1/admin/webhooks/:id — read onePOST /api/v1/admin/webhooks — create (URL must be http(s)://)PATCH /api/v1/admin/webhooks/:id — updateDELETE /api/v1/admin/webhooks/:id — deletePOST /api/v1/admin/webhooks/:id/test — fire a synthetic eventGET /api/v1/admin/webhooks/:id/deliveries — recent deliveriesAll admin-auth gated. Mutations are picked up by the audit log under
webhooks.* actions.
Admin UI
Section titled “Admin UI”/_/webhooks — two-pane editor:
- List — name, status dot, URL preview
- Editor — name, URL, event chips, HMAC secret (regenerate), custom headers, retry/timeout settings
- Recent deliveries — table with click-through to full payload + response. Fire test button enqueues a synthetic event scoped to this webhook only (won’t disturb other subscribers).
Limits
Section titled “Limits”- In-process dispatcher — fires every 2 seconds, claim limit 50 deliveries per tick. Sufficient for most loads; for very high event volumes, run vaultbase in cluster mode (SO_REUSEPORT) so multiple workers share the dispatcher load.
- No webhook receives more than
retry_max + 1total attempts. - Response body is captured to the delivery log up to 2048 bytes.
- The dispatcher does not preserve cross-event order — slow consumers
- retries can shuffle the receive order.