Skip to main content

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.

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.attempt.succeeded",
      "verification.attempt.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 identity verification you want to perform. 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": "Verify 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 passport 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 successful verification:
{
  "type": "verification.attempt.succeeded",
  "metadata": {
    "contract_version": 1,
    "event_id": "evt_...",
    "verification_attempt_id": "va_...",
    "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 verification 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.
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.