Reference

Webhook payloads

The shape of every webhook event Bedrock can send, with examples and signature verification details.

Request format

Every webhook is a JSON POST. Bedrock sends two headers and the payload:

  • X-Bedrock-Event — the event name (e.g. review.completed)
  • X-Bedrock-Signaturesha256=<hex>, an HMAC-SHA256 of the raw request body using your firm webhook secret
  • The body is the payload itself, not an envelope. The shape varies by event type.

Signature

The signature header has the form sha256=<hex>. Compute HMAC-SHA256 over the raw request bytes using your firm's webhook secret and compare the hex digest in constant time.

ts
import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(rawBody: Buffer, header: string, secret: string) {
  const [scheme, provided] = header.split('=');
  if (scheme !== 'sha256' || !provided) return false;

  const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(provided, 'hex');
  const b = Buffer.from(expected, 'hex');
  return a.length === b.length && timingSafeEqual(a, b);
}

Event types

Each event delivers the canonical resource as the payload. The shapes below come directly from the schemas in /openapi.json.

review.completed

Header: X-Bedrock-Event: review.completed. Body: the completed Job resource.

json
{
  "id": "job_01HX4...",
  "firmId": "firm_01HW1...",
  "documentType": "SUITABILITY_REPORT",
  "clientReference": "client-12345",
  "documentReference": "report-2026-04-07-001",
  "status": "COMPLETED",
  "outcome": "APPROVED",
  "priority": "STANDARD",
  "assignedTo": "user_01HW2...",
  "createdAt": "2026-04-07T12:30:01.000Z",
  "updatedAt": "2026-04-07T12:35:01.000Z"
}

document.approved / document.modified / document.rejected

Header: X-Bedrock-Event: document.approved (or document.modified / document.rejected). Body: the outcome LedgerRecord. Subscribe to these instead of the deprecated certificate.issued if you want to know when a review's certificate is on its way — once cert-gen finishes, fetch the PDF via GET /v1/ledger/records/{id}/certificate using the record id from this payload.

json
{
  "id": "rec_01HW2...",
  "firmId": "firm_01HW1...",
  "eventType": "DOCUMENT_APPROVED",
  "reviewJobId": "job_01HX4...",
  "documentMetadata": {
    "documentReference": "SR-2026-0142",
    "documentType": "SUITABILITY_REPORT",
    "outcome": "APPROVED",
    "reviewerName": "James Hargreaves"
  },
  "timestamp": "2026-04-07T12:35:02.000Z"
}

sla.breached

Header: X-Bedrock-Event: sla.breached. Body: details of the breached job.

json
{
  "reviewJobId": "job_01HX4...",
  "firmId": "firm_01HW1...",
  "deadline": "2026-04-07T17:00:00.000Z",
  "overdueBySeconds": 3600
}

Delivery semantics

  • At-least-once delivery
  • Retries with exponential backoff for any non-2xx response, up to 24 hours
  • Dedupe by a stable identifier from the payload (e.g. id, recordId, or reviewJobId)

See also