Agent skill
Veriff Webhooks Skill
Receive and verify Veriff webhooks. Use when setting up Veriff identity verification webhook handlers, debugging X-HMAC-SIGNATURE verification, or handling decision webhooks (approved, declined, resubmission_requested, expired, abandoned) and event webhooks (started, submitted).
Install this skill
npx skills add hookdeck/webhook-skills --skill veriff-webhooks
When to Use This Skill
- How do I receive Veriff webhooks?
- How do I verify the Veriff
X-HMAC-SIGNATUREheader? - Why is my Veriff webhook signature verification failing?
- How do I handle Veriff decision webhooks (
approved,declined,resubmission_requested)? - How do I track the
startedandsubmittedVeriff event webhooks?
Verification (core)
Veriff signs the raw request body with HMAC-SHA256, keyed with your integration's shared secret key (used as a plain string, no decoding), and sends the lowercase hex digest in X-HMAC-SIGNATURE. There is no timestamp and no signature prefix. Veriff has no webhook-verification SDK, so verify manually.
Veriff also sends X-AUTH-CLIENT (your integration API key) and possibly vrf-integration-id. They are identifiers, not proof of authenticity: only X-HMAC-SIGNATURE authenticates the sender.
Node:
const crypto = require('crypto');
function verifyVeriff(rawBody, signatureHeader, sharedSecret) {
if (!signatureHeader) return false;
const expected = crypto.createHmac('sha256', sharedSecret).update(rawBody).digest('hex');
try {
return crypto.timingSafeEqual(
Buffer.from(signatureHeader.trim().toLowerCase()),
Buffer.from(expected)
);
} catch {
return false; // Different lengths = invalid
}
}
Python:
import hmac, hashlib
def verify_veriff(raw_body: bytes, signature_header: str, shared_secret: str) -> bool:
if not signature_header:
return False
expected = hmac.new(shared_secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature_header.strip().lower(), expected)
For complete handlers with route wiring, event dispatch, and tests, see:
Webhook Types and Events
Veriff has several webhook types, each with its own URL in the Customer Portal. This skill handles the two most common. A decision webhook carries a verification object; an event webhook carries a top-level action.
| Webhook | Field | Values |
|---|---|---|
| Decision | verification.status | approved, declined, resubmission_requested, review, expired, abandoned |
| Event | action | started, submitted, waiting_complete, waiting_continued, flow_finished, flow_cancelled, document_ |
review is only sent if agreed with Veriff. Event actions other than started and submitted need extra configuration and apply to the web flow only. Other types (watchlist screening, INE, CURP, user-defined statuses, full auto) are not covered here. See references/overview.md.
For the full payload reference, see Veriff Webhooks Guide and the Decision webhook / Event webhook pages.
Important Headers
| Header | Description |
|---|---|
X-HMAC-SIGNATURE | Hex HMAC-SHA256 of the raw body (the only header that authenticates the request) |
X-AUTH-CLIENT | Your integration API key (identifies the sender, not proof of authenticity) |
vrf-integration-id | Integration ID (shown in Veriff's example headers; don't rely on it) |
Environment Variables
VERIFF_SHARED_SECRET=your_shared_secret_key # Customer Portal > Integrations > your integration (not the API key)
Delivery Behavior
- Respond with
200within 5 seconds. Any other response triggers retries for up to a week. - Delivery is at-least-once and unordered: make handlers idempotent (key on
verification.id+verification.attemptId+status). - Only HTTPS URLs with no custom port are accepted.
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 veriff --path /webhooks/veriff
Reference Materials
- references/overview.md - Webhook types, statuses, event actions, payload shapes
- references/setup.md - Configure webhook URLs in the Customer Portal
- references/verification.md - Signature details, test vector, gotchas