Create hosted identity-verification sessions that your customers complete in the browser. A session walks the customer through a configurable workflow — document capture, liveness check, face match, custom forms, signatures — and produces a normalized verification result you can consume programmatically.
Requires the KYC Sessions product on your API key. Session endpoints are free of charge.
Creating a Session
Code
Request Fields
| Field | Required | Description |
|---|---|---|
customer | No | Customer identity: externalId (your reference ID), name, email. When email and name are provided, an invitation email with the session link and QR code is sent automatically |
expiresInMs | No | Session expiry duration in milliseconds. Defaults to 1 hour (3600000) |
workflowId | No | Workflow to use for this session. Defaults to your organization's default workflow |
flowConfig | No | Explicit flow configuration defining the steps the customer goes through. If omitted, derived from the workflow |
claims | No | Integrator-asserted values for the workflow's session-sourced claims, keyed by canonical field (e.g. { "dateOfBirth": "1990-01-15" }). These are cross-checked against extracted document data. Unknown fields and missing required claims are rejected |
Response
Code
Send the customer to sessionUrl — it embeds a short-lived access token. The workflow is snapshotted at creation time, so later workflow edits never affect sessions already in flight.
Session Lifecycle
| Status | Meaning |
|---|---|
not-started | Session created, customer has not begun |
in-progress | Customer is working through the flow |
complete | Customer finished all steps; checks are running or awaiting review |
approved | Verification approved (automatically or by an admin) |
rejected | Verification rejected |
Track progress in real time via the kycSessions.* webhook events — session.created, session.updated, session.completed, session.checksCompleted, session.reviewed, step.completed, step.failed, and more.
Retrieving Sessions
Single Session
Code
Returns the full session including per-step results and the normalized verification object. If document verification is still in progress, the latest status is refreshed before returning.
Listing Sessions
Code
Paginated (limit 1–100, default 50; offset), with filters for completion status, validity, creation date range, checkpoint, and free-text search across session ID, external ID, customer name, and email. Sortable by createdAt, expiresAt, or completedAt.
Deleting a Session
Code
Permanently deletes the session and all associated data.
Reading Results
A session exposes results at two levels:
result— the raw read layer: every workflow step with its full submission history (input,output, captured media with signed viewing URLs) plus a per-steprollupof the best result.verification— the normalized, versioned contract (schemaVersion: "1.0") designed for integrations. Prefer this: its shape is stable across workflow changes and internal refactors.
The verification Object
| Field | Description |
|---|---|
outcome | Normalized lifecycle outcome: PENDING, AWAITING_USER, IN_REVIEW, APPROVED, DECLINED, RESUBMISSION_REQUESTED, EXPIRED, WARNING, ABANDONED, ERROR |
decision | The verdict detail: outcome, deduplicated blocking reasons (normalized reason codes), retryable, and reviewer comments |
person | Extracted identity: name (original script + Latin), dateOfBirth, gender, nationality, nationalIdentifiers, addresses |
documents | Every verified document with normalized type (PASSPORT, NATIONAL_ID, DRIVERS_LICENSE, …), numbers, dates, issuing country, per-field extractedFields with confidence, and image references |
biometrics | faceMatch (document portrait vs. liveness best frame — note the raw score is a distance, lower is better) and liveness (decision + video reference; with the color-flash challenge also method: "COLOR_FLASH" and a colorFlash block — verdict, matchFraction, responseAmplitude, per-slot expected vs. reflected colours) |
checks | Every executed rule, deduplicated, each with category, stable rule name, decision (PASS/WARNING/FAIL/REVIEW/NOT_EXECUTED), score, and reason codes |
provenance | Claimed-vs-extracted reconciliation per field: { claimed, extracted, match, confidence } |
narrative | AI-written reviewer-facing summary of the evidence. Advisory only — it describes the decision, never contributes to it |
checkRun | Which check-run produced the relational verdicts (version, status). Absent while the first run is pending |
How the Verdict Is Produced
Checks run in two layers, and the distinction decides when you can trust the result:
- Per-step checks run while the customer is still in the flow, as each step settles: document quality and template validation, tampering and visual-integrity analysis, extraction plausibility rules (e.g. the NIC number encoding its date of birth). A failing step is surfaced to the customer immediately and — submission limits permitting — retriable.
- Session-level relational checks run once, after the whole workflow
ends, because they compare evidence across steps:
- Claims comparison — what the customer asserted (via workflow claims) against what the document says, with deterministic name/date/identifier matching.
- Face match — the portrait cropped from the document against the best-aligned frame captured during the liveness challenge.
- Narrative — the reviewer-facing summary in
verification.narrative.
Because layer 2 runs after completion, session.completed does not mean
the verdict is final. A session that looked approvable can still be
declined by a failed cross-check. Wait for
session.checksCompleted before acting.
Check runs are versioned: if the session's evidence changes (a late
document-pipeline result reconciling, a resubmission), a new run with a higher
version supersedes the previous one, and verification.checkRun.version
tells you which run produced the verdicts you are reading. A run where some
evaluator could not produce a verdict (a model outage, a storage failure)
reports partial: true in the webhook and re-triggers automatically — the
affected checks show NOT_EXECUTED with a TECHNICAL_ERROR reason rather
than a fabricated pass or fail.
Reason Codes
Every failure is expressed as a normalized reason code — switch on category:
DOCUMENT_QUALITY, DOCUMENT_EXPIRED, DOCUMENT_UNSUPPORTED, DOCUMENT_INCOMPLETE, DOCUMENT_TAMPERED, DOCUMENT_NOT_AUTHENTIC, DATA_MISMATCH, DATA_INVALID, DATA_NOT_FOUND, FACE_MISMATCH, LIVENESS_FAILURE, AML_SANCTIONS, AML_PEP, AML_ADVERSE_MEDIA, TECHNICAL_ERROR, OTHER
Each reason also carries the stable rawLabel (the underlying rule id, e.g. NIC_DOB_MISMATCH), a retryable flag, and a customer-safe description.
Media Access
Document images and liveness videos are referenced by short-lived /media/{token} URLs. The token is the credential — fetch promptly, and use the stable fileId / key to correlate media across reads.
Media types you will encounter on attempts and results:
mediaType | Content |
|---|---|
document-front / document-back | Captured document pages, scoped to their owning step via stepId |
liveness-video | Full recording of the liveness challenge |
liveness-best-frame | The best-aligned still from the challenge — the frame the face match compares against the document portrait |
form-data | Files uploaded through form steps |
signature | Captured e-signature image |
Attempts & Device Intelligence
Every session tracks attempts — one per device/browser run of the flow — each carrying its captured media, telemetry events, and a device identity resolved from browser fingerprint signals at capture time:
| Field | Description |
|---|---|
browserId / deviceIdentity.visitorId | Stable device identifier, resilient to incognito mode and minor signal drift |
deviceIdentity.visitorFound | Whether this device was seen before on the platform |
deviceIdentity.confidence | Identification confidence, 0–1 |
deviceIdentity.isSuspicious | Global platform flag for the device |
deviceIdentity.priorAttemptsWithOrg | Distinct prior onboarding sessions from this device with your organization — a repeat-onboarding signal |
deviceIdentity.firstSeenWithOrgAt / lastSeenWithOrgAt | Unix ms bounds of the device's history with your organization |
deviceIdentity.crossChecks | Server-side request cross-checks (user-agent vs. signals consistency), with a mismatchCount |
linkedId | The business id the fingerprint event was linked to — the owning session id |
Use priorAttemptsWithOrg and isSuspicious to feed your own risk rules —
for instance, routing a device that has onboarded five customers this week to
manual review. Fields are absent on attempts captured before fingerprint
collection ran.
Recommended Integration Flow
POST /kyc-sessionswith your customer's details and knownclaims- Send the customer to
sessionUrl(or let the invitation email handle it) - Listen for
kycSessions.session.completed, thenkycSessions.session.checksCompleted— the verification verdicts are final only after checks complete - Read
GET /kyc-sessions/{id}and act onverification.outcomeandverification.decision.reasons - If your compliance flow includes manual review, also listen for
kycSessions.session.reviewed

