Webhooks
Webhooks push events to your HTTPS endpoint as they happen — no polling required. Available on Growth, Scale, and Enterprise plans.
All webhook management endpoints require a JWT session token (Authorization: Bearer <session-token>).
Event types
Section titled “Event types”| Event | Fired when |
|---|---|
record.created | A provenance record is signed and stored |
verify.attempted | Any verification is run against your records |
verify.tamper_detected | A verification returns tampered: true |
quota.warning | Signing usage crosses 80% of your plan limit |
quota.limit | Free tier limit hit — signing blocked (402) |
generator.revoked | One of your generators is revoked |
Payload shape
Section titled “Payload shape”Every webhook POST has this body:
{ "id": "550e8400-e29b-41d4-a716-446655440000", "event": "record.created", "timestamp": "2026-06-11T14:23:01Z", "org_id": "org_abc123", "data": { }}id is the delivery ID — use it for idempotency.
Verifying signatures
Section titled “Verifying signatures”Every request includes an X-Certivu-Signature header:
X-Certivu-Signature: t=1749654181,v1=abc123...Verify it on your end:
import crypto from "node:crypto";
function verifyCertivuSignature(rawBody, header, secret) { const [tPart, v1Part] = header.split(","); const ts = tPart.split("=")[1]; const expected = crypto .createHmac("sha256", secret) .update(`${ts}.${rawBody}`) .digest("hex"); if (expected !== v1Part.split("=")[1]) return false; // invalid signature if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // replay attack return true;}The 5-minute tolerance (300s) protects against replay attacks. Reject requests outside that window.
GET /v1/webhooks
Section titled “GET /v1/webhooks”List all registered endpoints for your org, plus plan eligibility.
GET /v1/webhooksAuthorization: Bearer <session-token>{ "endpoints": [ { "webhook_id": "uuid", "url": "https://example.com/certivu-hook", "events": ["record.created", "verify.tamper_detected"], "status": "active", "failure_count": 0, "created_at": "2026-06-11T10:00:00Z" } ], "plan": "growth", "webhooks_enabled": true}webhooks_enabled is false for Free and Starter plans. The endpoint always returns 200 — check this field rather than looking for a 402.
POST /v1/webhooks
Section titled “POST /v1/webhooks”Create a new endpoint. Growth+ only. Maximum 10 endpoints per org.
POST /v1/webhooksAuthorization: Bearer <session-token>Content-Type: application/json{ "url": "https://example.com/certivu-hook", "events": ["record.created", "verify.tamper_detected", "quota.warning"]}Response (201):
{ "webhook_id": "uuid", "url": "https://example.com/certivu-hook", "events": ["record.created", "verify.tamper_detected", "quota.warning"], "status": "active", "failure_count": 0, "created_at": "2026-06-11T10:00:00Z", "secret": "a3f2c8b1e7d4..."}The secret is returned once and cannot be retrieved again. Store it immediately.
PATCH /v1/webhooks/:id
Section titled “PATCH /v1/webhooks/:id”Update URL, events, or status. Re-enabling (status: "active") resets the failure counter.
{ "status": "active" }DELETE /v1/webhooks/:id
Section titled “DELETE /v1/webhooks/:id”Permanently delete an endpoint and its delivery log.
GET /v1/webhooks/:id/deliveries
Section titled “GET /v1/webhooks/:id/deliveries”Last 50 deliveries, sorted newest first. Payload is excluded from list responses.
{ "deliveries": [ { "delivery_id": "uuid", "webhook_id": "uuid", "event": "record.created", "status": "delivered", "status_code": 200, "response_body": "ok", "attempts": 1, "created_at": "2026-06-11T14:23:01Z" } ]}Delivery records are auto-deleted after 30 days via MongoDB TTL.
POST /v1/webhooks/:id/deliveries/:deliveryId/retry
Section titled “POST /v1/webhooks/:id/deliveries/:deliveryId/retry”Synchronously re-attempt a failed delivery. Growth+ only. The endpoint must be active.
{ "success": true }Returns 409 if the delivery already succeeded or the endpoint is disabled.
Auto-disable behavior
Section titled “Auto-disable behavior”After 5 consecutive delivery failures, Certivu automatically sets the endpoint status to disabled. Re-enable it via PATCH /v1/webhooks/:id — this also resets the failure counter to 0.
A delivery fails if:
- The HTTP response is not 2xx
- The request times out (10 second limit)
- A network error occurs