Skip to main content
This guide takes you from zero to a working verification in about ten minutes. You’ll create an organization, mint an API key, create a session via the API, send a user through the verify flow, and inspect the resulting webhook event.

Prerequisites

  • A Kayle ID account at kayle.id, or a self-hosted instance running the platform app
  • A way to expose a local HTTPS endpoint to the internet for webhook testing — ngrok or Cloudflare Tunnel both work
  • curl and jq for poking at the API
1

Create an organization

Sign in to the dashboard, follow the onboarding flow, and create an organization. The slug you choose becomes part of the dashboard URL (/organizations/<slug>) and is used by the demo flow if you wire it up later.
2

Mint an API key

Go to API keys, click Create API key, give it a name, and select the scopes you need. For this quickstart you need sessions:write (to create sessions) and webhooks:write (to register an endpoint). Copy the key — it’s shown once and starts with kk_.
The key is only displayed at creation. If you lose it, delete the key and create a new one.
3

Register a webhook endpoint

Either through the dashboard or with the API:
curl -X POST https://api.kayle.id/v1/webhooks/endpoints \
  -H "Authorization: Bearer kk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.ngrok.app/webhooks/kayle",
    "subscribed_event_types": [
      "verification.session.succeeded",
      "verification.session.failed",
      "verification.session.expired",
      "verification.session.cancelled"
    ]
  }'
The response includes the endpoint ID and signing_secret. Save the secret — it’s also shown only once. You’ll use it to verify incoming webhook signatures. See Verifying signatures.
4

Register an encryption key

Webhook payloads are always JWE-encrypted. The endpoint cannot receive deliveries until an active encryption key is attached.Generate an RSA key pair, export the public key as a JWK, then register it:
curl -X POST https://api.kayle.id/v1/webhooks/endpoints/whe_.../keys \
  -H "Authorization: Bearer kk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "key_id": "k1",
    "algorithm": "RSA-OAEP-256",
    "key_type": "RSA",
    "jwk": { "kty": "RSA", "n": "...", "e": "AQAB", "alg": "RSA-OAEP-256", "use": "enc", "kid": "k1" }
  }'
Store the matching private key in your secret manager. Your server uses it to decrypt incoming JWE bodies. See Encrypted payloads for key generation and decryption code.
5

Create a verification session

A session represents one Kayle check you want to run. You can request specific share fields — claims the user must consent to share before completing the flow.
curl -X POST https://api.kayle.id/v1/sessions \
  -H "Authorization: Bearer kk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "redirect_url": "https://yourapp.com/verification/done",
    "share_fields": {
      "date_of_birth": { "required": true,  "reason": "Check age eligibility" },
      "family_name":   { "required": true,  "reason": "Match account name" },
      "given_names":   { "required": true,  "reason": "Match account name" }
    }
  }'
The response includes a verification_url (where to send the user) and a one-shot cancel_token (in case you need to cancel from the browser):
{
  "data": {
    "id": "vs_mza7vecksrtyfw193ekcvl5vnws3bt1lz96buu3iw7zidckf8dga2zx2echb3t16",
    "status": "created",
    "verification_url": "https://verify.kayle.id/vs_...?cancel_token=...",
    "cancel_token": "...",
    "expires_at": "2026-05-05T12:00:00Z"
  },
  "error": null
}
6

Send the user through the flow

Redirect the user to verification_url. They’ll land on verify.kayle.id, see the consent screen for the share fields you requested, and complete the flow on their phone (NFC chip read of their document plus a selfie).Sessions expire after 60 minutes. The user gets up to three attempts within that window.
7

Receive the webhook

When the user finishes, your webhook endpoint receives a POST with Content-Type: application/jose. The body is a compact JWE. Verify X-Kayle-Signature against the JWE string, decrypt with your private key, and the cleartext is the event JSON. For a confirmed Kayle check:
{
  "type": "verification.session.succeeded",
  "metadata": {
    "contract_version": 1,
    "event_id": "evt_...",

    "verification_session_id": "vs_..."
  },
  "data": {
    "claims": {
      "date_of_birth": "1995-04-12",
      "family_name": "Curie",
      "given_names": "Marie"
    },
    "selected_field_keys": ["date_of_birth", "family_name", "given_names"]
  }
}
See Verifying signatures and Encrypted payloads for full code samples.
8

Pick up the redirect

After the Kayle check finishes, the user is sent to redirect_url if you supplied one. The redirect carries the session ID so you can match the browser back to the result you received over the webhook. Kayle’s result is not your final access, onboarding, or eligibility decision.
If you don’t supply a redirect URL, the user will be shown a button that closes the page.

Next

Verification sessions

Statuses, attempts, expiry, and cancellation.

Failure codes

What each failure_code means and what to do about it.

Webhook deliveries

Retry semantics, delivery inspection, manual replays.

API reference

Full endpoint reference, error codes, pagination.