vouchID Logo

vouchIDDocs

Documentation is subject to change as we continue to harden the product.

Webhooks

Receive real-time notifications when events occur in your vouchID integration. Webhooks allow your application to react immediately to verification completions, compliance actions, and monitoring alerts.

One Endpoint, All Events

Configure a single webhook URL per application. All event types are sent to the same endpoint, and you filter by the eventType field in the payload.

Setting Up Webhooks

Configure your webhook URL in the Developer Console under your application settings:

1. Set Webhook URL

Enter your HTTPS endpoint in the application settings

2. Generate Secret

Click "New Secret" to generate your signing key

3. Verify Signatures

Validate HMAC signatures to ensure authenticity

Event Types

vouchID sends webhooks for the following events:

Event TypeDescription
verification.completedA user has completed age or identity verification
monitoring.alert.createdA monitoring alert was generated (sanctions hit, PEP match, etc.)
case.createdA compliance case was created (manual or automatic)
case.resolvedA compliance case was resolved by a compliance officer
case.status_changedA case status changed (OPEN → IN_REVIEW → ESCALATED, etc.)
vid.fraud_status_changedA user was added to or removed from the fraud list

Payload Format

All webhook payloads follow a consistent structure:

FieldDescription
apiVersionAPI version for payload format (currently 2024-01-01)
eventIdUnique identifier for deduplication (use this to avoid processing duplicates)
eventTypeThe type of event (see Event Types above)
timestampISO 8601 timestamp when the event occurred
dataEvent-specific data (varies by event type)

Verifying Webhook Signatures

Always Verify Signatures

Always verify the webhook signature before processing. This ensures the request came from vouchID and hasn't been tampered with.

Each webhook request includes two headers for signature verification:

HeaderDescription
X-VouchID-SignatureHMAC-SHA256 signature (prefixed with sha256=)
X-VouchID-TimestampISO 8601 timestamp when the webhook was sent (used in signature)

Signature Verification Steps

  1. 1. Extract the X-VouchID-Timestamp header
  2. 2. Validate the timestamp is within ±5 minutes of current time (replay protection)
  3. 3. Read the raw request body as a string
  4. 4. Compute: HMAC_SHA256(secret, "{timestamp}.{rawBody}")
  5. 5. Compare with the signature from X-VouchID-Signature (strip the sha256= prefix)
const crypto = require('crypto');

// Middleware to verify webhook signatures
function verifyWebhookSignature(req, res, next) {
  const signature = req.headers['x-vouchid-signature'];
  const timestamp = req.headers['x-vouchid-timestamp'];
  const rawBody = req.rawBody; // You need raw body - see note below

  if (!signature || !timestamp) {
    return res.status(401).json({ error: 'Missing signature headers' });
  }

  // 1. Check timestamp is within 5 minutes (replay protection)
  const timestampDate = new Date(timestamp);
  const now = new Date();
  const diffMinutes = Math.abs(now - timestampDate) / (1000 * 60);

  if (diffMinutes > 5) {
    return res.status(401).json({ error: 'Timestamp too old' });
  }

  // 2. Compute expected signature
  const signatureInput = `${timestamp}.${rawBody}`;
  const expectedSignature = 'sha256=' + crypto
    .createHmac('sha256', process.env.VOUCHID_WEBHOOK_SECRET)
    .update(signatureInput)
    .digest('hex');

  // 3. Compare signatures (timing-safe)
  if (!crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  )) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  next();
}

// IMPORTANT: Preserve raw body for signature verification
app.use('/webhooks/vouchid', express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf.toString();
  }
}));

app.post('/webhooks/vouchid', verifyWebhookSignature, (req, res) => {
  const { eventType, eventId, data } = req.body;

  // TODO: Check eventId for deduplication

  switch (eventType) {
    case 'case.created':
      console.log('New compliance case:', data.caseId);
      // Handle case creation
      break;
    case 'vid.fraud_status_changed':
      console.log('Fraud status changed:', data.scopedVid, data.action);
      // Handle fraud status change
      break;
    // ... handle other event types
  }

  // Always return 200 quickly to acknowledge receipt
  res.status(200).json({ received: true });
});

Best Practices

Return 200 Quickly

Acknowledge receipt immediately and process asynchronously. We timeout after 30 seconds and will retry on failure (up to 5 attempts).

Deduplicate Events

Store processed eventIds and skip duplicates. We guarantee at-least-once delivery.

Handle Retries

We retry failed deliveries with exponential backoff (1s, 2s, 4s, 8s, 16s). Make your handler idempotent.

Validate Timestamps

Reject webhooks with timestamps older than 5 minutes to prevent replay attacks.

Event Payload Examples

case.created

vid.fraud_status_changed

monitoring.alert.created

Need Help?

If you have questions about webhook integration or need assistance debugging, contact our support team at vouchid.co/support.