Webhooks
Real-time events, cryptographically signed
When something changes in Valyd, we POST a signed event to your endpoint. Same envelope shape as Stripe. Same signing scheme. Same retry semantics.
Overview
- You register an endpoint URL in the dashboard or via API.
- Valyd assigns it a signing secret (
whsec_…). - When an event happens, Valyd POSTs a JSON payload with a
Valyd-Signatureheader. - Your handler verifies the signature, processes idempotently, returns 2xx.
If your endpoint returns non-2xx or times out, we retry with exponential backoff for 72 hours.
The event envelope
{ "id": "evt_4f8a3b2c1d9e7f6a", "object": "event", "type": "identity.verified", "livemode": true, "created": 1746547200, "data": { "object": { "id": "vp_4f8a..." } } }
Event types
Identity events
identity.created | A person completed onboarding. |
identity.verified | A live verification was performed and passed. |
identity.verification_failed | A live verification failed. |
License events
license.added | A new credential was attached. |
license.status_changed | A license moved between active / suspended / expired. |
license.expiring_soon | Fired 90, 30, and 7 days before expiry. |
Agent events
agent.created | An agent instance was minted. |
agent.session_issued | A short-lived token was released to an agent. |
approval.requested | An agent created a human-in-the-loop approval. |
approval.resolved | The approver responded. |
Signature verification
Verify the signature on every webhook. Without it, anyone who knows your URL can POST fake events.
import { Webhook } from "@valyd/node"; app.post("/webhooks/valyd", (req, res) => { const event = Webhook.verify( req.body, req.headers["valyd-signature"], process.env.VALYD_WEBHOOK_SECRET ); res.json({ received: true }); });
Use the raw body
If your framework parses JSON before the verifier sees it, the signature will fail. Capture the raw body before any middleware.
Testing locally
$ valyd webhooks listen --forward-to localhost:3000/webhooks/valyd → Listening on whsec_test_a8f2b3c4... → Forwarding to http://localhost:3000/webhooks/valyd $ valyd webhooks fire identity.verified → Event evt_test_4f8a fired (200 OK)
Retries and replay
| Attempt | Wait |
|---|---|
| 2nd | ~30 seconds |
| 3rd | ~2 minutes |
| 4th | ~10 minutes |
| 5th–nth | Exponential, capped at 12 hours |
| Final | 72 hours after first attempt — then dropped |
Quick reference
POST/v1/webhooks/endpoints
GET/v1/webhooks/endpoints
POST/v1/webhooks/replay
DEL/v1/webhooks/endpoints/:id
