Using CertivantLive ID & developerWebhooks

Webhooks

Receive real-time Certivant events in your own systems when verifications change status — setup, HMAC signature verification, retry schedule, and full event-type reference.

Who can do this:Org ownerOrg admin— Members need the api.webhooks permission to view; setup requires admin.

The Webhook Events page showing outbound webhook delivery history — event type, target URL, delivery status, attempts, response code, and time.

What it does

A webhook is a URL we POST to whenever something happens in your Certivant org. Instead of polling our API for changes ("did the verification finish yet?"), you give us a URL and we tell you the moment it does. Webhook events are how you keep your own systems in sync — customer-onboarding flows, AML risk databases, CRM tags, internal compliance dashboards.

Where it lives

KYC → Webhooks in the left sidebar. (KYB events ship to the same webhook endpoints — there's no separate KYB webhook config.)

Setup

Pick a URL on your side

Your endpoint must:

  • Accept HTTPS POSTs with Content-Type: application/json.
  • Respond 2xx quickly (under 5 seconds) to acknowledge receipt.
  • Verify the signature header (next step) before trusting the payload.

Add the URL to Certivant

KYC → Webhooks → Add endpoint. A field opens in place: paste the URL and click Save (the same URL can be edited later in Settings → KYC/B). The Webhook endpoints card shows the organization-wide default endpoint, any per-form endpoints, and the signing secret.

The Webhook endpoints card — the organization default endpoint with its Add endpoint link, the per-form endpoints, and the signing secret with the Rotate secret button.

Copy the signing secret

After saving you'll see a one-time-shown signing secret. Copy it now — it's not displayed again. If you lose it you rotate to a new one.

Verify signatures on your side

Every request has an X-Certivant-Signature-V2 header of the form t=<unix time>,v1=<hex>. The v1 value is the HMAC-SHA256 of the unix time, a dot and the raw request body, signed with the secret you just copied. Verify it, and reject events whose time is more than five minutes away from your own clock, before parsing. The recipe, with code in several languages, is in the developer guide.

The old body-only X-KYC-Signature-Legacy header is no longer sent (it was removed on 24 October 2026). If your receiver still checks it, every event now fails verification — switch to X-Certivant-Signature-V2.

Pseudocode:

const sig = req.headers["x-certivant-signature"];
const expected = hmac("sha256", secret).update(rawBody).digest("hex");
if (!timingSafeEqual(sig, expected)) return 401;

Don't skip this. Without signature verification, anyone who guesses your endpoint URL can post fake events at you.

Test from the dashboard

On the Developer page, open the Webhook events section. Pick an event in the Test event dropdown — kyc.webhook.test (the default), kyc.request.created, kyc.request.approved, kyc.request.rejected or kyc.request.escalated — and click Send a test event. Certivant sends a synthetic event to your endpoint, signed exactly like a real one (X-Certivant-Signature-V2, using your webhook secret) and carrying test: true so you can tell it apart. Confirm your endpoint answers with a 2xx.

The Webhook events section of the Developer page — the list of event types with a one-line description, the Test event dropdown and the Send a test event button.

Event types

EventWhen
kyc.request.createdAnyone created a verification request (dashboard, API, or public form).
kyc.request.documents_readyThe applicant uploaded all required documents; processing started.
kyc.request.approvedFinal status is APPROVED (auto or manual).
kyc.request.rejectedFinal status is REJECTED.
kyc.request.escalatedSent to manual review.
kyc.request.cancelledA reviewer cancelled the request before completion.
kyb.*Same shape as kyc.* but for business verifications.
monitoring.alert.createdA new monitoring alert fired on a previously-approved applicant.
monitoring.alert.resolvedA reviewer resolved an alert.
form.submittedA public-form submission. Useful when you're using the form for non-KYC data collection.
webhook.testThe synthetic test event from the Test button. Always ships, regardless of subscriptions.

The full list and JSON payloads are documented under KYC → Developer → Webhook events.

Payload shape

Every event has the same envelope:

{
  "id": "evt_2k3f9a...",
  "type": "kyc.request.approved",
  "createdAt": "2026-05-27T14:30:00Z",
  "organizationId": "org_abc123",
  "data": { /* event-specific payload */ }
}

The data field varies per event type. For example, kyc.request.approved ships:

{
  "request": { "id": "kyr_...", "status": "APPROVED", "type": "INDIVIDUAL", ... },
  "applicant": { "id": "ka_...", "fullName": "Jane Doe", "email": "...", ... },
  "decision": { "by": "auto" | "manual", "reviewerId": "user_..." | null, "note": "..." | null },
  "policy": {
    "id": "pol_..." | null,
    "name": "default-kyc-policy",
    "version": 3 | null,
    "isDefault": true,
    "decision": "approve" | "reject" | "escalate" | "live_verification",
    "rulesFired": ["tier1-disqualified", "approve-by-score"],
    "reason": "High-risk country",
    "liveVerificationRequired": true
  }
}

The policy block (on kyc.request.approved / rejected / escalated) tells you which risk policy decided the request and which rules fired. isDefault: true means Certivant's default policy ran (id/version are your organization's default-policy row, or null when it hasn't been created yet); a custom policy reports its stored id, name, and version. rulesFired lists the rule ids that matched, in evaluation order — branch your downstream automation on these. reason is the winning rule's reason when it set one. decision: "live_verification" (with liveVerificationRequired: true, on kyc.request.escalated) means the policy wants a live ID verification before a final decision — the request waits in manual review until the call decides. See Design a risk policy.

If you operate in partner mode (one org serving many end customers), every event envelope also carries subTenantName and subTenantExternalId — the tags set on the request when it was created via the API — so your handler can route the result to the right end customer. See the developer webhook reference for the exact field placement.

Retries

If your endpoint returns non-2xx (or times out), we retry with exponential backoff:

attempt 1: immediately
attempt 2: 30 seconds later
attempt 3: 5 minutes later
attempt 4: 30 minutes later
attempt 5: 2 hours later
attempt 6: 6 hours later
attempt 7: 24 hours later

After 7 failed attempts we mark the event as permanently_failed and stop retrying. Failed events appear in the webhooks dashboard with their last error; you can manually re-fire any of them with Retry.

Your endpoint must be idempotent. We don't deduplicate retries on our side — your endpoint will see the same event multiple times if its early attempts timed out but eventually succeeded. Key off the event id and skip if you've already processed it.

Multiple endpoints

You can register multiple endpoints (e.g., one for your CRM, one for your AML data lake). Each can subscribe to a different set of event types. Per-endpoint signing secrets — rotating one doesn't break the others.

Per-form webhooks

The org-wide webhook fires for every relevant event. Public forms can ALSO have a per-form webhook URL under their settings — useful when one form feeds a separate system. Per-form webhooks run in addition to the org-wide ones.

The KYC → Webhooks page now lists both in one place: your organization webhook URL at the top, and a row for each form's webhook URL below it — so you can see every place events are delivered without hunting through individual form settings.

The webhooks dashboard

KYC → Webhooks lists every endpoint with:

  • Endpoint URL + friendly name.
  • Status — active / paused / failing.
  • Last delivery — when the most recent event was POSTed.
  • Success rate — % of attempts that returned 2xx in the last 7 days.
  • Recent deliveries — table of recent events with status code, latency, and response body.

Pause an endpoint to stop deliveries without losing the config.

Troubleshooting

My endpoint is getting hammered

Endpoints are called once per event, but a busy org might fire dozens per minute. Make your endpoint async — accept the POST, ack 2xx immediately, process the payload off the request lifecycle. If you need to slow us down, set the per-endpoint Max concurrent deliveries option.

Signature verification keeps failing

Three common causes:

  1. Parsing the body before verifying the signature. Use the raw bytes. Express + body-parser users: set bodyParser.raw({ type: 'application/json' }) for the webhook route specifically.
  2. Wrong secret. Rotate and re-paste.
  3. Stripping or transforming the body in a middleware (gzip, BOM, encoding). The signature is over the exact bytes we sent.

Events look duplicated

Retries on slow responses. Key off event.id for idempotency.

Order of events matters but I'm seeing them out of order

We don't guarantee strict ordering across event types. For per-applicant ordering use the createdAt field on the event; the order within a single applicant's lifecycle is monotonic.