Skip to content

Webhook Event Reference

ZKProva sends webhook notifications when verification events occur. Configure webhooks to receive real-time updates in your application.

Event Types

Event Trigger
verification.completed ZKP proof verification succeeded (all claims verified)
verification.failed ZKP proof verification failed (one or more claims failed)
credential.revoked A member revoked one of their credentials

Payload Schema

All webhook payloads are JSON with this envelope:

{
  "event": "<event_type>",
  "timestamp": "2026-02-28T14:30:00Z",
  "delivery_id": "550e8400-e29b-41d4-a716-446655440000",
  "data": { ... }
}

verification.completed

{
  "event": "verification.completed",
  "member_did": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
  "claims_verified": {
    "income_range": true,
    "membership_status": true
  },
  "all_verified": true,
  "verification_id": "550e8400-e29b-41d4-a716-446655440000",
  "failed_step": null,
  "claim_results": [
    {"claim_type": "income_range", "passed": true, "reason": "Proof verified"},
    {"claim_type": "membership_status", "passed": true, "reason": "Proof verified"}
  ]
}

verification.failed

{
  "event": "verification.failed",
  "member_did": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
  "claims_verified": {
    "income_range": false
  },
  "all_verified": false,
  "verification_id": "",
  "failed_step": "ZKP_VERIFY",
  "claim_results": [
    {"claim_type": "income_range", "passed": false, "reason": "Groth16 proof invalid"}
  ]
}

credential.revoked

{
  "event": "credential.revoked",
  "credential_id": "550e8400-e29b-41d4-a716-446655440000",
  "claim_type": "income_range",
  "member_did": "did:key:z6Mk...",
  "revoked_at": "2026-02-28T14:30:00Z"
}

Signature Verification

Every webhook request includes an HMAC-SHA256 signature in the X-ZKProva-Signature header:

X-ZKProva-Signature: sha256=5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e

Verify the signature by computing HMAC-SHA256 of the raw request body using your webhook secret.

Node.js

const crypto = require('crypto');

function verifyWebhookSignature(body, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express middleware
app.post('/webhooks/zkprova', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-zkprova-signature'];
  if (!verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  console.log(`Received ${event.event}`);
  res.sendStatus(200);
});

Python

import hashlib
import hmac

def verify_webhook_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Retry Policy

Failed deliveries (non-2xx response or timeout) are retried automatically:

Attempt Delay Cumulative
1 Immediate 0s
2 30 seconds 30s
3 5 minutes ~5.5 min
4 1 hour ~1 hr 5 min

After 4 failed attempts, the delivery moves to the dead-letter queue.

Dead-Letter Queue

Failed webhooks that exhaust all retries are held in a dead-letter queue. Admins can replay them:

curl -X POST https://api.zkprova.com/api/v1/admin/webhooks/dlq/replay \
  -H "Authorization: Bearer <admin_jwt>" \
  -H "Content-Type: application/json" \
  -d '{"delivery_ids": ["<delivery_id>"]}'

View dead-letter entries:

curl https://api.zkprova.com/api/v1/admin/webhooks/dlq \
  -H "Authorization: Bearer <admin_jwt>"

Webhook CRUD

Register a Webhook

curl -X POST https://api.zkprova.com/api/v1/webhooks \
  -H "X-API-Key: zkp_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/zkprova",
    "events": ["verification.completed", "verification.failed"]
  }'

List Webhooks

curl https://api.zkprova.com/api/v1/webhooks \
  -H "X-API-Key: zkp_live_abc123"

Delete a Webhook

curl -X DELETE https://api.zkprova.com/api/v1/webhooks/<webhook_id> \
  -H "X-API-Key: zkp_live_abc123"