Webhooks
Signed, retried event deliveries — the authoritative way to learn a session's decision.
Add an endpoint
In the dashboard open Developers → Webhooks, add your HTTPS URL and choose events (*, session.* or
single types). The signing secret (whsec_…) is shown when you create the endpoint and can be revealed later.
Or use the API: POST /v1/webhook_endpoints with { "url", "events" } — the secret is returned once.
Endpoints belong to a mode: test sessions deliver only to test-mode endpoints. In production, URLs must be
https and publicly reachable (no private or loopback addresses).
Events
| Type | When |
|---|---|
session.created | A session was created. |
session.started | The applicant opened the flow. |
session.processing | All steps were submitted; checks and rules are running. |
session.approved | Approved, by the rules or by a reviewer. |
session.declined | Declined, by the rules or by a reviewer. |
session.review | Waiting for a person in your team to decide. |
session.resubmission_requested | A reviewer asked the applicant to redo some steps. |
session.expired | The session expired before it was finished. |
session.cancelled | The session was cancelled. |
check.completed | An async direct check finished. |
screening.hit | Sanctions or PEP screening found a potential match. |
Payload
{
"id": "evt_…",
"type": "session.approved",
"created": 1790000000,
"mode": "test",
"data": {
"session": {
"id": "ses_…",
"reference_id": "user_123",
"status": "approved",
"type": "kyc",
"decision": "approved",
"decision_source": "auto",
"parent_session_id": null
}
}
}Payloads carry ids and statuses only, never personal data. Fetch the session for details.
Verify the signature
Every delivery has an OKYC-Signature header:
OKYC-Signature: t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdv1 is hex(HMAC_SHA256(secret, t + "." + raw_body)). With @okyc/node, pass the raw body (not parsed
JSON) to webhooks.constructEvent; it checks the signature in constant time and rejects timestamps older than
5 minutes:
// Webhooks: verify against the RAW body, dedupe on event.id, then fetch details from the API.
const seen = new Set();
app.post('/okyc/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
let event;
try {
event = await okyc.webhooks.constructEvent(
req.body,
req.header('okyc-signature'),
process.env.OKYC_WEBHOOK_SECRET,
);
} catch {
return res.sendStatus(400);
}
if (seen.has(event.id)) return res.sendStatus(200);
seen.add(event.id);
if (event.type === 'session.approved' || event.type === 'session.declined') {
const session = await okyc.sessions.retrieve(event.data.session.id);
console.log(`user ${session.reference_id} → ${session.status}`);
// mark the user verified / rejected in your database
}
res.sendStatus(200);
});In other languages:
- Split the header on
,; readtand everyv1. - Reject if
|now − t| > 300seconds. - Compute
HMAC_SHA256(secret, "<t>.<raw body>")as lowercase hex. - Accept if it equals any
v1, compared in constant time.
To test your handler, generateTestHeader(secret, body) from @okyc/node builds a valid header, and the
dashboard can send a test event to an endpoint.
Delivery
- Respond with any
2xxwithin 10 seconds. Anything else is retried with exponential backoff for up to 24 hours. - Delivery is at least once and not ordered: dedupe on the event
id, and treat the session you fetch as the truth. - Every attempt is in the dashboard's delivery log, and you can replay events from there.
GET /v1/eventslists recent events for reconciliation.
Sessions
Create a verification session for each applicant, hand its URL to your frontend, and read the result.
Idempotency
Retry POST requests safely with an Idempotency-Key header.
© 2026 Aiistic Technologies Private Limited. OKYC is a product of Aiistic Technologies Private Limited. Privacy notice