Webhooks
Webhooks deliver platform events to operator-registered HTTPS endpoints. Each webhook has a rotating HMAC secret used to sign outbound payloads.
Register
POST /v1/webhooks
Authorization: Bearer <admin-or-tenant-key>
Content-Type: application/json
{
"tenant_id": "acme",
"url": "https://hooks.example.com/creb",
"event_types": ["bundle.sealed", "exception.opened", "rule.updated"]
}Response includes secret exactly once. Store it immediately — re-fetching is impossible.
Signature verification
Every delivery carries two headers:
X-Creb-Webhook-IdX-Creb-Signature=v1=<hex-hmac-sha256(secret, body)>
Node example
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(secret: string, header: string, body: Buffer): boolean {
const [version, hex] = header.split("=");
if (version !== "v1" || !hex) return false;
const expected = createHmac("sha256", secret).update(body).digest("hex");
return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(hex, "hex"));
}Python example
import hmac, hashlib
def verify(secret: str, header: str, body: bytes) -> bool:
version, _, hex_sig = header.partition("=")
if version != "v1" or not hex_sig:
return False
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, hex_sig)Retries
Failed deliveries (non-2xx, or timeout > 10s) are retried with exponential backoff: 30s, 1m, 5m, 30m, 2h, 12h. After 6 failed attempts the delivery is marked FAILED and surfaced in GET /v1/webhooks/{id}/deliveries.
Rotation
Rotate a secret atomically with POST /v1/webhooks/{id}/rotate-secret. The response includes the new secret; the old secret keeps working for 15 minutes to bridge consumers that need to update config.
Event catalogue (initial)
event.ingestedevent.duplicateddocument.ingesteddocument.quarantinedbundle.requestedbundle.sealedbundle.failedrule.createdrule.updatedexception.openedexception.resolvedlegal-hold.createdlegal-hold.released
