API
Hosted flow
The verification UI the person completes: how to send them there, embed it, brand it and get them back.
The hosted flow lives at https://kycverify.me/verify/{token}. It runs in any modern phone or laptop browser and needs nothing installed. It shows your app's name, logo and primary colour, and walks the person through the steps the session's workflow enables.
Sending people to it
Recommended
Full-page redirect
Send the person to url; they return to callback_url with ?session_id&status.
Asynchronous
Link by email or SMS
Deliver url through your own channel; set contact.email to pre-fill the email step.
Built in
Desktop to phone
On a laptop the flow offers a QR code so the person continues on their phone camera.
Possible
iframe
Works with allow="camera", but the final redirect stays inside the frame. Rely on the webhook.
Redirect and return
Redirect to url. When the session is final, the flow shows the result and, if the session has a callback_url (or the workflow a redirect_url), sends the person there after 5 seconds with two query parameters added:
https://app.example.com/kyc/done?session_id=ses_0k3v9x2m4a7qhd8f1rtb&status=approvedDesktop to phone
Opened on a laptop, the flow offers a QR code that opens the same session on a phone, where the camera is usually better. The person can also choose to continue on the current device.
In an iframe
<iframe
src="https://kycverify.me/verify/q3Xf…"
allow="camera; fullscreen"
style="width: 100%; height: 720px; border: 0"
></iframe>Camera access needs allow="camera". The final redirect navigates inside the frame, and the flow does not post messages to the parent, so drive your page from the webhook. The flow itself sets no frame-ancestors policy.
Branding
Each app has a primary_color (hex) and an optional logo_url, set in the console's app settings. The flow uses them for buttons, focus and the header. The app's name is shown in the consent text.
Step order
The order is fixed: consent → email → document → aadhaar → pan → liveness → review (submit). Disabled steps are skipped. Face match, AML, age and duplicate checks run in the background at submit.
Flow endpoints
The flow page calls a token-scoped API. You do not call it yourself, but it documents what is enforced:
| Method | Path | Body |
|---|---|---|
| GET | /flow/{token} | FlowState: steps, attempts, allowed documents, branding, redirect |
| POST | /flow/{token}/consent | { accepted: true } |
| POST | /flow/{token}/email/send | { email }: sends a 6-digit code |
| POST | /flow/{token}/email/verify | { code } |
| POST | /flow/{token}/document | multipart document_type, country, front, back? |
| POST | /flow/{token}/aadhaar/secure-qr | { qr_text } or multipart image |
| POST | /flow/{token}/aadhaar/offline-xml | multipart file (.zip), share_code |
| POST | /flow/{token}/pan | multipart pan, name?, image? |
| POST | /flow/{token}/liveness/start | issues challenge_id and challenges |
| POST | /flow/{token}/liveness | multipart challenge_id, frames meta, frame_0..n |
| POST | /flow/{token}/submit | moves the session to processing |
Rules the server enforces
- Every mutating call is refused with
409 conflictbefore consent, and once the session isprocessing, final or expired. - 60 requests per minute per token.
- Uploads up to 10 MB each, identified by content (JPEG, PNG, WebP, ZIP) and encrypted with AES-256-GCM before they touch disk. Images over 8,192 pixels a side or 24 megapixels are refused before they are decoded.
- Each step allows
max_attemptstries (3 by default). A capture rejected for quality returnsretry: truewith the issues, so the person can retake it. - Email codes: 6 digits, valid 10 minutes, 5 tries per code, 5 sends per session.
- Liveness challenges are single-use, expire after 5 minutes, and accept up to 16 frames.
Liveness challenges
frame 0 · center
baseline pose, size, embedding
frame 1 · turn_left
yaw left of the baseline
frame 2 · smile
mouth wider relative to eyes
frame 3 · move_closer
face larger than baseline
- Issued order, single-use challenge id, 5 minutes
- Same face in every frame (embedding vs centre)
- One significant face per frame
- Increasing timestamps, no duplicated frames
The vocabulary is turn_left, turn_right, smile and move_closer. The client captures one center frame, then frames for each challenge in the issued order, labelled with the challenge key and a millisecond timestamp, and uploads them un-mirrored.