Webhooks
Webhooks let VeriProof push real-time notifications to external systems when governance events occur — a session anomaly is detected, a rule violation fires, a compliance evidence pack is ready for download, etc.
Creating a webhook:
- Go to Settings → Webhooks and click Add webhook.
- Enter a name, the destination URL (HTTPS only), and select the events to subscribe to.
- Optionally, enter a secret — VeriProof will sign each request body with HMAC-SHA256 using this secret, allowing your receiver to verify authenticity.
- Click Test delivery to send a sample payload to your endpoint.
Available events (common):
session.completed— fired when a session finishes processingsession.anomaly_detected— fired when an anomaly is detectedcompliance.rule_violation— fired when a governance rule violation is detectedcompliance.evidence_pack_generated— fired when an evidence pack is readycompliance.erasure_completed— fired when a GDPR erasure is completehealth.escalation— fired when a health escalation threshold is crossed
Retries: VeriProof retries failed deliveries (non-2xx response or timeout) up to 3 times with exponential backoff (5s, 25s, 125s).
Webhooks
Outbound webhooks let Veriproof push event notifications to your own systems in real time. When an event matches a registered webhook, the platform sends an HTTPS POST request to your endpoint with a signed JSON payload.
Webhook management requires the CustomerAdmin role. All other roles can view registered webhooks.
Event Types
Session events
| Event type | Fired when |
|---|---|
session.completed | A session finishes processing |
session.anomaly_detected | An anomaly is detected in a session |
session.outcome_tagged | A session outcome tag is added or updated |
Health events
| Event type | Fired when |
|---|---|
health.status_changed | An application’s health status changes |
health.stage_changed | An application advances or regresses a lifecycle stage |
health.escalation | A health escalation threshold is crossed |
application.health_gate_triggered | A health gate evaluation produces an actionable result |
Compliance events
| Event type | Fired when |
|---|---|
compliance.evidence_pack_generated | A compliance evidence pack is ready for download |
compliance.erasure_completed | A GDPR erasure request has been cryptographically completed |
compliance.rule_violation | A governance rule violation is detected in a session |
Attestation events
| Event type | Fired when |
|---|---|
attestation.anchored | A session Merkle root is anchored to the Solana blockchain |
Application lifecycle events
| Event type | Fired when |
|---|---|
application.created | An application is created |
application.promoted | An application is promoted to a new environment |
application.approved | An application promotion is approved |
application.retired | An application is retired |
Digest events
| Event type | Fired when |
|---|---|
digest.generated | A periodic digest is generated and dispatched |
Creating a Webhook
Open Webhook Settings
In the Customer Portal, navigate to Settings → Webhooks and click Add webhook.
Enter the endpoint URL
Provide the HTTPS URL of your endpoint. Plain HTTP endpoints are rejected — TLS is required.
Select events
Choose which event types this webhook should receive. You can register multiple webhooks, each subscribed to different events, or a single webhook that receives all events.
(Optional) Add a secret
Enter a signing secret. Veriproof will use this secret to compute an HMAC-SHA256 signature of the request body and include it in the X-Veriproof-Signature header. This allows your endpoint to verify the payload is genuine.
Save
Click Save webhook. The endpoint is immediately active.
Webhook Payload Format
All webhooks use the same envelope:
{
"id": "evt_01J7...",
"type": "session.completed",
"timestamp": "2026-03-15T14:30:00Z",
"apiVersion": "2026-02",
"customerId": "cust_abc123",
"application": {
"id": "app_xyz789",
"name": "loan-decisioning"
},
"data": {
// Event-specific fields
}
}Example: session.completed
{
"id": "evt_01J7abcdef",
"type": "session.completed",
"timestamp": "2026-03-15T14:30:00Z",
"apiVersion": "2026-02",
"customerId": "cust_abc123",
"application": {
"id": "app_xyz789",
"name": "loan-decisioning"
},
"data": {
"sessionId": "sess_01H9...",
"outcome": "success",
"durationMs": 1240,
"riskLevel": "medium",
"stepCount": 5,
"occurredAt": "2026-03-15T14:30:00Z"
}
}Example: compliance.erasure_completed
{
"id": "evt_01J7ghijkl",
"type": "compliance.erasure_completed",
"timestamp": "2026-03-15T02:01:05Z",
"apiVersion": "2026-02",
"customerId": "cust_abc123",
"application": {
"id": "",
"name": ""
},
"data": {
"dataSubjectId": "ds_01H5...",
"sessionsErased": 42,
"occurredAt": "2026-03-15T02:01:05Z"
}
}Verifying Signatures
If you set a signing secret, every delivery includes the X-Veriproof-Signature header:
X-Veriproof-Signature: sha256=a3f5c9...To verify the signature in your endpoint handler:
import hmac
import hashlib
def verify_signature(payload_bytes: bytes, signature_header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode("utf-8"),
payload_bytes,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)import { createHmac, timingSafeEqual } from "crypto";
function verifySignature(payload: Buffer, header: string, secret: string): boolean {
const expected = "sha256=" + createHmac("sha256", secret).update(payload).digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}Always use a constant-time comparison function (e.g. hmac.compare_digest in Python, timingSafeEqual in Node.js) when checking signatures. String equality operators (==) are vulnerable to timing attacks.
Delivery and Retry Policy
Veriproof delivers webhooks with the following guarantees:
| Parameter | Value |
|---|---|
| Timeout | 10 seconds per delivery attempt |
| Retry attempts | 3 (on non-2xx response or connection failure) |
| Retry backoff | Exponential: 5s, 25s, 125s |
| Failure handling | After 3 failures the event is marked failed |
Your endpoint must return a 2xx status code within the timeout to acknowledge delivery. Returning any other status code (including 3xx redirects) causes a retry.
Idempotency
Each event has a unique id field. If your endpoint receives the same event ID more than once (due to retries), it should process it idempotently. Store the id and skip re-processing if already handled.
Delivery Logs
Every webhook delivery attempt is logged and visible in the Customer Portal:
- Navigate to Settings → Webhooks.
- Click on a webhook endpoint.
- Click Delivery logs.
The log shows every attempt for the last 7 days, including the HTTP response status, latency, and request/response body.
API
GET /v1/webhooks/{id}/delivery-logsTesting a Webhook
Use the Test button in the portal to send a sample session.completed payload to your endpoint and verify it responds correctly, without waiting for a real event.
POST /v1/webhooks/{id}/testManaging Webhooks via API
GET /v1/webhooks # List all webhooks
POST /v1/webhooks # Create a webhook
PUT /v1/webhooks/{id} # Update a webhook
DELETE /v1/webhooks/{id} # Delete a webhook
POST /v1/webhooks/{id}/test # Send a test delivery
GET /v1/webhooks/{id}/delivery-logs # View delivery historyNotification Channels (Slack / Teams)
For human-readable alert notifications in Slack or Microsoft Teams instead of raw webhook payloads, see Notification Channels. These are configured separately under Settings → Notification Channels and use the same underlying event system with a pre-formatted message template.