Agent skill
Footprint Webhooks Skill
Receive and verify Footprint webhooks (identity verification / KYC, delivered via Svix). Use when setting up Footprint webhook handlers, debugging svix-signature verification, or handling events like footprint.onboarding.completed, footprint.onboarding.status_changed, and footprint.watchlist_check.completed.
Install this skill
npx skills add hookdeck/webhook-skills --skill footprint-webhooks
Footprint (identity verification and KYC) delivers webhooks through Svix. Every delivery is an HTTP POST with a JSON body and three signature headers: svix-id, svix-timestamp, and svix-signature. The event name is the event_kind field in the body. Footprint's own pages point to Svix's guides for receiving and verifying; its event catalog is hosted on svix.com.
When to Use This Skill
- How do I receive Footprint webhooks?
- How do I verify Footprint webhook signatures (
svix-signature)? - How do I handle
footprint.onboarding.completedwhen a user's status waspending? - How do I react to
footprint.watchlist_(continuous AML monitoring)?check.completed - Why is my Footprint webhook signature verification failing, or rejecting Hookdeck retries?
Verification (core)
Signed content is {svix-id}.{svix-timestamp}.{raw body}. Svix signs it with HMAC-SHA256 using the base64-decoded secret (the part after whsec_), base64-encodes the result, and sends one or more space-delimited v1,<signature> entries. Footprint recommends Svix's verification guide and the official svix library, which compares signatures in constant time.
import { Webhook } from 'svix'; // svix 2.x is ESM-only
const wh = new Webhook(process.env.FOOTPRINT_WEBHOOK_SECRET); // "whsec_..."
// rawBody: the raw Buffer/string, never re-serialized JSON
wh.verify(rawBody, {
'svix-id': req.headers['svix-id'],
'svix-timestamp': req.headers['svix-timestamp'],
'svix-signature': req.headers['svix-signature'],
}); // throws on a bad signature or a timestamp outside the window
const event = JSON.parse(rawBody.toString('utf8')); // parse only after verify passes
from svix.webhooks import Webhook, WebhookVerificationError
wh = Webhook(os.environ["FOOTPRINT_WEBHOOK_SECRET"])
wh.verify(raw_body, {"svix-id": msg_id, "svix-timestamp": ts, "svix-signature": sig})
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Secret format is unconfirmed on Footprint's side. whsec_ is Svix's secret format. Footprint's webhook pages never show the secret, so treat the whsec_ prefix as an assumption from Svix and check the value you get from your Footprint dashboard.
Hookdeck retries and replays hit the 5-minute window
The svix and standardwebhooks libraries always enforce a fixed 5-minute timestamp window, and it can't be configured. Hookdeck retries and replays resend the original svix-timestamp, so the library rejects any retry or replay that happens more than 5 minutes after the original delivery. If you verify behind Hookdeck (or replay old events), use the manual verifier in references/verification.md, where the window can be dropped or widened. Deduplicate on svix-id instead of relying on the timestamp.
Common Event Types
The event name is event_kind in the JSON body.
| Event | Triggered When |
|---|---|
footprint.onboarding.completed | A user's onboarding reaches a terminal status (pass, fail, none). Footprint recommends subscribing to this one |
footprint.onboarding.incomplete | An onboarding started via POST /onboardings still has unmet requirements |
footprint.onboarding.status_ | A user's status changed after onboarding. Deprecated in favor of onboarding.completed and user.manual_review |
footprint.watchlist_ | A watchlist check ran for a user (needs continuous monitoring on the playbook) |
footprint.user.manual_ | A dashboard user changed the user's status during manual review |
footprint.user.info_ | A dashboard user asked the user for more information during review |
footprint.user.bank_ | A user's bank link was updated |
footprint.vault.updated | A user or business vault was updated |
footprint.entity.entity_ | A custom status was set, updated, or cleared on a user or business |
See references/overview.md for payload fields.
Environment Variables
# Signing secret of the webhook endpoint (Svix format, starts with whsec_)
FOOTPRINT_WEBHOOK_SECRET=whsec_xxxxx
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 footprint --path /webhooks/footprint
Register the tunnel URL as an endpoint in the Footprint dashboard, then complete a test onboarding in sandbox mode to trigger events.
Reference Materials
- references/overview.md - Footprint webhook concepts, events, payloads, IP allowlist
- references/setup.md - Register an endpoint and get the signing secret
- references/verification.md - Svix verification details, manual verifier, timestamp window