> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kayle.id/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook overview

> How Kayle ID delivers check results to your servers.

When a Kayle check reaches a terminal state, Kayle emits a domain event and delivers it to every webhook endpoint subscribed to that event type. Webhooks are the primary way you learn about Kayle check results — polling the API works for one-off lookups, but a subscribed endpoint is what you should build against.

## Event types

Four event types exist:

| Event                            | Trigger                                                                                                                                                                                                                                                              |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `verification.session.succeeded` | An attempt was confirmed by Kayle. The payload includes the consented claims and the list of selected field keys.                                                                                                                                                    |
| `verification.session.failed`    | Kayle could not confirm an attempt. The payload includes a `failure_code`.                                                                                                                                                                                           |
| `verification.session.expired`   | A session passed its 60-minute window with no confirmed attempt.                                                                                                                                                                                                     |
| `verification.session.cancelled` | A session was cancelled by you (authenticated cancel), by the user (public cancel), or as a privacy-withdrawal replacement for a `succeeded` / `failed` payload that was scrubbed before delivery. The payload includes a `reason` and the per-check retry counters. |

Each endpoint subscribes to a subset by listing event types when you create or update it. See [Configuring endpoints](/webhooks/configuring-endpoints).

Every verification session produces **exactly one** terminal webhook from the set above — provided at least one of your endpoints subscribes to that event type. If a user withdraws consent (privacy request) after a `succeeded` or `failed` event was queued but before it delivered, the payload is scrubbed and a `verification.session.cancelled` event is emitted in its place. See [verification.session.cancelled](/api-reference/webhooks/verification-session-cancelled) for the full `reason` enumeration.

## Delivery semantics

* **At-least-once.** Kayle retries failed deliveries. Build idempotent handlers — key on `metadata.event_id` (one ID per event, never reused) or on `metadata.verification_session_id` combined with the event `type`.
* **Per-endpoint.** A single event triggers one delivery per subscribed endpoint. Disabling or deleting an endpoint does not affect other endpoints.
* **Best-effort ordering.** Within a session you'll usually receive events in the order they happened, but a delivery that retries can land after later events. Don't rely on order; rely on `metadata` and the resource state you can fetch from the API.
* **Sub-minute latency for the first attempt.** The first delivery is queued immediately when the event fires and processed by a scheduled job. See [Deliveries](/webhooks/deliveries) for the retry schedule.

## What you have to handle

Every endpoint must:

1. Register an [encryption key](/webhooks/encryption) before it can receive deliveries. Webhook payloads are always JWE-encrypted; an endpoint without an active key fails delivery before the body is sent.
2. Verify the `X-Kayle-Signature` header against the encrypted body before decrypting. See [Verifying signatures](/webhooks/verifying-signatures).
3. Respond with a `2xx` status code within a few seconds. Anything else is treated as a delivery failure and retried.
4. Be idempotent on `metadata.event_id` (or on `metadata.verification_session_id` + event `type`).

Endpoints that don't return `2xx` after the automatic retry schedule are marked `failed`. You can replay or retry them from the dashboard or via the API while the encrypted payload is still retained; see [Deliveries](/webhooks/deliveries).
