API
API reference
Every public endpoint: parameters, example requests and responses, and the errors worth handling.
Base URL https://kycverify.me/api. JSON in and out (multipart for images), the x-api-key header on every call, and one error envelope. Timestamps are RFC 3339 UTC with second precision.
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/sessions | Create a session and its hosted link |
| GET | /v1/sessions | List sessions |
| GET | /v1/sessions/{id} | Session summary |
| GET | /v1/sessions/{id}/decision | Identity, checks and review |
| PATCH | /v1/sessions/{id}/status | Decide a session in review |
| DELETE | /v1/sessions/{id} | Purge files, embeddings and identity |
| GET | /v1/workflows | The app's workflows |
| POST | /v1/checks/aml | Screen a name |
| POST | /v1/checks/pan | Validate a PAN |
| POST | /v1/checks/mrz | Parse an MRZ |
| POST | /v1/checks/face-match | Compare two faces |
| POST | /v1/checks/aadhaar/secure-qr | Verify an Aadhaar Secure QR |
| GET | /health | Liveness: {"ok": true} (no key) |
Sessions
Create a session
POST/v1/sessions
Creates a verification session on the key's app and returns the hosted-flow link. The session runs a snapshot of the chosen workflow, so later workflow edits never change it.
Auth: x-api-key
Body
workflow_idstring · optional- A live workflow of this app. Omitted: the app's default workflow, else its oldest live one.
vendor_datastring ≤ 256 chars · optional- Your reference for the person. Echoed in every webhook and filterable in
GET /v1/sessions. callback_urlabsolute http(s) URL ≤ 2048 · optional- Where the hosted flow sends the person when it finishes, with
session_idandstatusappended. Overrides the workflow'sredirect_url. metadataobject ≤ 8 KB · optional- Any JSON object. Returned with the decision.
contact.emailstring · optional- Pre-fills the email step. Must be a valid address.
expires_in_hoursinteger 1-720 · optional- Link lifetime. Default
168(7 days).
curl -X POST https://kycverify.me/api/v1/sessions \
-H "x-api-key: $KYC_API_KEY" \
-H "content-type: application/json" \
-d '{ "vendor_data": "user-42", "expires_in_hours": 48 }'{
"session_id": "ses_0k3v9x2m4a7qhd8f1rtb",
"status": "not_started",
"url": "https://kycverify.me/verify/q3Xf…",
"session_token": "q3Xf…",
"workflow_id": "wf_0k3t1c8n5e2wpzr6g4ya",
"vendor_data": "user-42",
"expires_at": "2026-10-10T09:12:44Z"
}400 bad_request: a field fails validation, e.g.expires_in_hours must be between 1 and 720.404 not_found:workflow_idis unknown, archived or belongs to another app.422 unprocessable: the app has no workflow yet.
List sessions
GET/v1/sessions
The key's sessions, newest first.
Auth: x-api-key
Query parameters
statusstring · optional- One of
not_started,in_progress,processing,approved,declined,in_review,expired,abandoned. vendor_datastring · optional- Exact match on your reference.
pageinteger ≥ 1 · optional- Default
1. page_sizeinteger 1-100 · optional- Default
25.
curl "https://kycverify.me/api/v1/sessions?status=in_review&page_size=50" \
-H "x-api-key: $KYC_API_KEY"{ "data": [ { "session_id": "ses_0k3v9x2m4a7qhd8f1rtb", "status": "in_review", "decision_reason": "aml_potential_match", "workflow_id": "wf_0k3t1c8n5e2wpzr6g4ya", "vendor_data": "user-42", "created_via": "api", "created_at": "2026-10-03T09:02:11Z", "updated_at": "2026-10-03T09:12:44Z", "started_at": "2026-10-03T09:03:20Z", "submitted_at": "2026-10-03T09:11:58Z", "completed_at": "2026-10-03T09:12:44Z", "expires_at": "2026-10-10T09:02:11Z", "purged_at": null } ], "page": 1, "page_size": 50, "total": 1 }400 bad_request:Unknown status ….
Get a session
GET/v1/sessions/{id}
The session summary: status, reason, timestamps. Use the decision endpoint for identity and checks.
Auth: x-api-key
curl https://kycverify.me/api/v1/sessions/ses_0k3v9x2m4a7qhd8f1rtb \
-H "x-api-key: $KYC_API_KEY"{
"session_id": "ses_0k3v9x2m4a7qhd8f1rtb",
"status": "in_review",
"decision_reason": "aml_potential_match",
"workflow_id": "wf_0k3t1c8n5e2wpzr6g4ya",
"vendor_data": "user-42",
"created_via": "api",
"created_at": "2026-10-03T09:02:11Z",
"updated_at": "2026-10-03T09:12:44Z",
"started_at": "2026-10-03T09:03:20Z",
"submitted_at": "2026-10-03T09:11:58Z",
"completed_at": "2026-10-03T09:12:44Z",
"expires_at": "2026-10-10T09:02:11Z",
"purged_at": null
}404 not_found: no such session in this app.
Get the decision
GET/v1/sessions/{id}/decision
The full decision: identity with a source per field, every check with its score, data and warnings, and the review if a person decided. Available at any status; checks fill in as the session progresses.
Auth: x-api-key
curl https://kycverify.me/api/v1/sessions/ses_0k3v9x2m4a7qhd8f1rtb/decision \
-H "x-api-key: $KYC_API_KEY"{
"session_id": "ses_0k3v9x2m4a7qhd8f1rtb",
"status": "approved",
"decision_reason": null,
"workflow_id": "wf_0k3t1c8n5e2wpzr6g4ya",
"vendor_data": "user-42",
"metadata": {},
"identity": {
"full_name": "ANNA MARIA ERIKSSON",
"date_of_birth": "1974-08-12",
"nationality": "UTO",
"document_type": "passport",
"document_number_masked": "L8989****",
"expiry_date": "2032-04-15",
"sources": { "full_name": "mrz", "date_of_birth": "mrz" }
},
"checks": [
{ "kind": "document", "status": "passed", "score": 0.97 },
{ "kind": "liveness", "status": "passed", "score": 0.91 },
{ "kind": "face_match", "status": "passed", "score": 0.612 },
{ "kind": "aml", "status": "passed", "score": 0 }
],
"review": null,
"created_at": "2026-10-03T09:02:11Z",
"submitted_at": "2026-10-03T09:11:58Z",
"completed_at": "2026-10-03T09:12:44Z",
"expires_at": "2026-10-10T09:02:11Z"
}Decide a session in review
PATCH/v1/sessions/{id}/status
Approve or decline a session that is in_review, from your own tooling. The API key is recorded as the actor and a webhook is sent.
Auth: x-api-key
Body
status"approved" | "declined" · required- The decision. A decline sets
decision_reasontomanual_review_declined. notestring ≤ 2000 chars · optional- Kept with the review.
curl -X PATCH https://kycverify.me/api/v1/sessions/ses_0k3v9x2m4a7qhd8f1rtb/status \
-H "x-api-key: $KYC_API_KEY" -H "content-type: application/json" \
-d '{ "status": "approved", "note": "Portrait checked by hand" }'{ "session_id": "ses_0k3v9x2m4a7qhd8f1rtb", "status": "approved", "review": { "reviewed_by": null, "reviewed_at": "2026-10-03T10:40:02Z", "note": "Portrait checked by hand" }, … }409 conflict: the session is notin_review(the message names its status), or it changed while you decided.
Purge a session
DELETE/v1/sessions/{id}
Deletes the session's encrypted files, face embeddings, document fingerprints and extracted identity, strips the Decision from stored webhook deliveries, and audits the purge. An open session is ended as expired (reason purged); a session still processing is refused with 409 conflict. The row stays as a tombstone with purged_at set, so references and history stay consistent.
Auth: x-api-key
curl -X DELETE https://kycverify.me/api/v1/sessions/ses_0k3v9x2m4a7qhd8f1rtb \
-H "x-api-key: $KYC_API_KEY"(empty body)409 conflict: the session isprocessing; purge it once the engine has reached a decision.
Workflows
List workflows
GET/v1/workflows
The app's live workflows with their configuration, default first.
Auth: x-api-key
curl https://kycverify.me/api/v1/workflows -H "x-api-key: $KYC_API_KEY"{
"data": [
{ "id": "wf_0k3t1c8n5e2wpzr6g4ya", "name": "Default", "is_default": true, "version": 3,
"created_at": "…", "updated_at": "…", "config": { "steps": { … }, "decision": { "auto_decline": true }, "redirect_url": null } }
],
"page": 1, "page_size": 1, "total": 1
}Standalone checks
Run one check from your backend without a hosted flow. These calls create no session and store nothing.
Screen a name
POST/v1/checks/aml
Screens one name against every loaded sanctions list without a session. Matches are returned from a name score of 0.80, up to 10, best first. (In a session, the AML step instead applies the workflow's review_threshold, 0.82 by default.)
Auth: x-api-key
Body
full_namestring ≤ 300 chars · required- Any order of names; matching is order-insensitive.
date_of_birthYYYY-MM-DD | YYYY · optional- Adjusts scores: a match adds 0.05, a mismatch subtracts 0.10.
nationalityISO alpha-3 · optional- Adds
nationality_matchto each result where the entry lists nationalities.
curl -X POST https://kycverify.me/api/v1/checks/aml \
-H "x-api-key: $KYC_API_KEY" -H "content-type: application/json" \
-d '{ "full_name": "Anna Maria Eriksson", "date_of_birth": "1974-08-12" }'{
"matches": [],
"screened_at": "2026-10-03T09:12:40Z",
"lists": [
{ "list": "ofac_sdn", "version": "3f9a1c0b7e2d", "fetched_at": "…", "entity_count": …, "status": "ok" },
{ "list": "un_consolidated", "version": "…", "fetched_at": "…", "entity_count": …, "status": "ok" }
],
"warnings": []
}- If no list is loaded,
matchesis empty andwarningsholdslists_not_loadedwith severityhigh: nothing was screened.
Validate a PAN
POST/v1/checks/pan
Structural validation: format, holder type and, with a name, the name initial. There is no Income Tax Department lookup, and the response says so.
Auth: x-api-key
Body
panstring · required- Spaces are removed and letters upper-cased.
namestring ≤ 200 chars · optional- The holder's name, to compare the fifth character with the surname (individuals) or entity name.
curl -X POST https://kycverify.me/api/v1/checks/pan \
-H "x-api-key: $KYC_API_KEY" -H "content-type: application/json" \
-d '{ "pan": "ABCPE1234F", "name": "Ravi Kumar Edathil" }'{
"normalized": "ABCPE1234F",
"valid_format": true,
"holder_type": "individual",
"name_initial_match": true,
"issues": [],
"government_lookup": false,
"note": "…structural validation only…"
}Parse an MRZ
POST/v1/checks/mrz
Parses two or three MRZ lines (or up to 20 noisy OCR lines, from which it locates the MRZ) and recomputes every check digit. Supports TD1, TD2, TD3, MRV-A, MRV-B and the French national ID.
Auth: x-api-key
Body
linesstring[] (≤ 20, ≤ 200 chars each) · required- The MRZ lines in order, or OCR output containing them.
curl -X POST https://kycverify.me/api/v1/checks/mrz \
-H "x-api-key: $KYC_API_KEY" -H "content-type: application/json" \
-d '{ "lines": [
"P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<",
"L898902C36UTO7408122F1204159ZE184226B<<<<<10"
] }'{
"format": "td3", "document_code": "P", "issuing_state": "UTO",
"surname": "ERIKSSON", "given_names": "ANNA MARIA",
"document_number": "L898902C3", "nationality": "UTO",
"birth_date_raw": "740812", "birth_date": "1974-08-12", "sex": "F",
"expiry_date_raw": "120415", "expiry_date": "2012-04-15",
"optional_data": "ZE184226B", "optional_data_2": null,
"check_digits": { "document_number": true, "birth_date": true, "expiry_date": true, "optional_data": true, "composite": true },
"valid": true,
"lines": [ "P<UTOERIKSSON<<…", "L898902C36UTO…" ],
"corrections": []
}422 unprocessable:Could not parse an MRZ: no MRZ found(or an unsupported layout).
Compare two faces
POST/v1/checks/face-match
1:1 comparison of the most prominent face in each image, using SFace embeddings and cosine similarity. match is true at or above SFace's published threshold, 0.363.
Auth: x-api-key
Multipart fields
image_aJPEG, PNG or WebP ≤ 10 MB · required- For example the document portrait.
image_bJPEG, PNG or WebP ≤ 10 MB · required- For example a selfie.
curl -X POST https://kycverify.me/api/v1/checks/face-match \
-H "x-api-key: $KYC_API_KEY" \
-F image_a=@portrait.jpg -F image_b=@selfie.jpg{ "similarity": 0.612, "match": true, "threshold": 0.363, "faces": { "a": 1, "b": 1 } }422 unprocessable: no face found in one image, or the face models are unavailable.
Verify an Aadhaar Secure QR
POST/v1/checks/aadhaar/secure-qr
Decodes the text of an Aadhaar Secure QR and verifies UIDAI's RSA signature against the installed certificates. Returns the last four digits and demographics; never photo bytes, never the reference id.
Auth: x-api-key
Body
qr_textstring ≤ 16 KB · required- The QR payload: one long decimal number.
curl -X POST https://kycverify.me/api/v1/checks/aadhaar/secure-qr \
-H "x-api-key: $KYC_API_KEY" -H "content-type: application/json" \
-d '{ "qr_text": "6979414848205548481619299442879901900893978332594614407…" }'{
"source": "secure_qr", "version": "V2",
"aadhaar_last4": "1234", "generated_at": "2025-01-01T12:00:00Z",
"name": "…", "dob_raw": "01-01-1990", "date_of_birth": "1990-01-01", "gender": "F",
"address": { "district": "…", "state": "…", "pincode": "…", … },
"address_text": "…",
"mobile_hash": "…", "email_hash": null,
"photo_format": "jp2", "photo_present": true,
"signature": "valid", "signature_key": "uidai_offline_publickey_26022021.cer"
}422 unprocessable:Not a valid Aadhaar Secure QR: ….signatureisinvalidorno_certificateswhen it cannot be verified; the response still decodes.