FLOW & CHECK LIST

How a verification session runs, end to end

This page walks through every step from the moment an end user enters the flow to the moment a signed webhook reaches your system — the checks that run at each step, and the exact numbers behind each decision.

Every threshold and limit below is a value actually enforced in the platform. Per-tenant configuration can only tighten them, never loosen them.

8
Flow steps — from session creation to webhook delivery
19
Distinct document checks — 15 scored + 4 hard rules
3
Liveness layers — active, passive ML, heuristic

Step by step

Session flow

The photo and NFC paths are identical through step 3, diverge at step 4, and converge again at step 5.

  1. 01Your system

    Session creation

    Your backend opens a verification session. The tenant API key is used only in this call and never reaches the browser. The returned session_id is the only user identifier exposed externally.

    POST /v1/sessions · X-Api-Key · state: created

    Checks that run at this step

    • Velocity limit: at most 5 sessions per applicant per 24 hours
    • Velocity limit: at most 20 sessions per IP per 24 hours
    • Tenant balance check on production sessions
    • Reusable KYC: an approved verification newer than 180 days with no AML alert skips re-verification
  2. 02End user

    Opening the flow

    The user opens the WebSDK (iframe) or a mobile SDK. Instead of an API key, the SDK works with a short-lived JWT scoped to that session, valid for 15 minutes by default. Tenant theme and the user's language (54 languages) are applied at this step.

    GET /v1/sessions/{id}/config · short-lived JWT (15 min) · CORS allowlist

    Checks that run at this step

    • Session validity window: 60 minutes (it moves to expired past that)
    • Per-tenant CORS allowlist — the SDK only loads from declared origins
    • Active modules (document / liveness) are read from the session config
  3. 03End user

    Document selection and capture

    The user picks a document type and captures it with the camera. Card edges are detected and cropped automatically; overly blurry or dark frames are flagged on-device and re-captured before ever reaching the server. On the NFC path the MRZ is scanned first — the key that unlocks the chip is derived from it.

    POST /v1/sessions/{id}/access-keys · POST /{id}/documents/images

    Checks that run at this step

    • Passports and MRZ ID cards from any country; documents without an MRZ are read and checked too
    • The back side is mandatory where the profile requires it (TD1 MRZ sits on the back)
    • On-device quality gates: sharpness, brightness, framing
  4. 04IDotta platformPhoto path

    Document analysis

    The session moves to document_processing and a background worker analyzes the images: field reading with our in-house OCR model, MRZ decoding via a separate path calibrated for OCR-B, ICAO 9303 check digit validation, and tamper detection via ELA and texture analysis. Each check produces a 0–100 score, reduced to a single document score by the profile weights.

    state: document_processing → liveness_pending | review | rejected

    Checks that run at this step

    • 15 scored checks, weighted per document type
    • 4 unweighted hard rules: document presence, file integrity, document expiry, specimen detection
    • Cross-field consistency between front and back (MRZ ↔ printed fields)
  5. 05IDotta platformNFC path

    Chip reading and passive authentication

    On eMRTD-compliant documents the chip is accessed over a BAC-established encrypted channel (Secure Messaging); data groups DG1 (MRZ), DG11 and DG12 are read. ICAO 9303 Passive Authentication proves two things: the hashes in the SOD show the data is unaltered, and the Document Signer certificate shows the data was genuinely written by the issuing state.

    POST /{id}/documents/nfc · state: nfc_processing → liveness_pending | review

    Checks that run at this step

    • SOD hash integrity — verified separately for each data group
    • Document Signer certificate signature, validated against the CSCA trust chain
    • Chip values are compared to the declared document number / date of birth / expiry — a mismatch goes to manual review (the chip is authoritative)
  6. 06IDotta platform

    AML / sanctions screening

    The name extracted from the document is screened against open sanctions and PEP lists synced daily. Names are normalized (accents and special characters folded), records with a fuzzy match score of 90% or above become candidates, and candidates whose birth year conflicts are eliminated.

    rapidfuzz token_sort_ratio ≥ 90% · match → state: review

    Checks that run at this step

    • OFAC SDN (U.S. Treasury), UN Consolidated, EU FSF and UK Sanctions List (FCDO) lists
    • False-positive elimination via birth-year conflict checks
    • A match never auto-rejects — the decision is left to a human
  7. 07End user

    Liveness detection and face matching

    The server generates a random sequence of 3–4 challenges (at least one blink and one head movement are guaranteed) alongside a single-use identifier. The user submits one frame per challenge plus a selfie; the server verifies each frame independently against its challenge type, then biometrically compares the selfie to the document photo.

    POST /{id}/liveness/start → /{id}/liveness/submit · challenge TTL 300 s

    Checks that run at this step

    • Every active challenge must pass — no tolerance
    • Frame-to-frame consistency: same person, and no static replay
    • Passive anti-spoofing (in-house model) + heuristic LBP/FFT/YCrCb signals
    • Document photo vs selfie comparison with our in-house face model
    • Each approved face is written to pgvector; the same face under a different identity raises a duplicate signal
  8. 08IDotta platform

    Decision and delivery

    The outcome of every layer is reduced to a single terminal state. Whatever the decision, each state transition is written to a timestamped event log. The result is delivered to your system via an HMAC-SHA256 signed webhook, and can always be read back from the result endpoint.

    webhook · X-Signature: sha256=… · GET /v1/sessions/{id}

    Checks that run at this step

    • Final statuses: approved, declined, expired, abandoned; in_review awaits an analyst and does not expire
    • A rejected step keeps the session in_progress and grants the user 3 retry attempts; once they run out it is declined
    • Failed webhook deliveries are retried; delivery records are retained
    • Personal data is deleted automatically 90 days after the terminal state

END-USER FLOW

The six screens your user sees

The flow is white-label — shown here in the IDotta theme. Tenants supply their own colours and logo, and the steps shrink to the modules you selected.

  • 9:411 / 6

    Verify your identity

    Takes about 2 minutes. You'll need your ID and a well-lit room.

    Start

    Secured by

    Intro & consent

  • 9:412 / 6

    Scan the front of your ID

    Place the document in the frame; the card is detected and cropped automatically.

    align document
    Capturing…

    Secured by

    Document capture

  • 9:413 / 6

    Hold your phone on the passport

    The chip data is read and verified against the state signature.

    Reading chip…

    Secured by

    NFC read

  • 9:414 / 6

    Look at the camera

    Follow the on-screen prompts — you may be asked to blink or turn your head.

    face
    Checking liveness…

    Secured by

    Liveness & selfie

  • 9:415 / 6

    Almost done

    The checks are running on the server. Don't close this screen.

    • Document✓
    • Chip signature✓
    • Liveness✓
    • Face match·
    Please wait

    Secured by

    Processing

  • 9:416 / 6

    You're verified

    The result was delivered to your system over a signed webhook.

    Continue

    Secured by

    Result

State machine

Verification statuses

Every response and webhook you receive carries status (the key your code checks), status_label (the display name) and step (the internal step, informational only). Internal steps only make defined transitions, each one written to the audit log, so replay attempts that resubmit an earlier step are structurally blocked.

  1. not_started

    Not Started

    Session opened; the user has not opened the link yet.

  2. in_progress

    In Progress

    The user is going through the steps. A rejected step stays here while retries remain.

  3. resubmitted

    Resubmitted

    An analyst asked for some steps to be redone; the user has not finished yet.

  4. in_review

    In Review

    No automatic decision; awaiting an analyst. Does not expire.

  5. approved

    Approved

    All checks passed — identity verified.

  6. kyc_expired

    Kyc Expired

    The approval is older than 365 days; request re-verification.

  7. declined

    Declined

    Final decline: an analyst declined, retries ran out, or time ran out with retries left.

  8. expired

    Expired

    60 minutes passed before the user started.

  9. abandoned

    Abandoned

    The user started but did not finish within 60 minutes.

Scoring

How the decision is made

Every check produces a score between 0 and 100. The document score is the weighted average of the checks that ran, and falls into one of three decision bands.

Document score

Σ (check score × weight) ÷ Σ (weight)

Thresholds can be raised per tenant but never lowered — the rule engine clamps every value to the global floor.

  • ≥ 70Automatic passDocument passed; the flow advances to liveness (or straight to approval in document-only mode).
  • 45 – 69Manual reviewA document was detected but could not be fully verified; an operator decides.
  • < 45Automatic rejectThe checks do not support the validity of the document.

Hard rules that override the score

In these cases the decision is as follows regardless of the weighted score.

  • Face but no text → reject

    Catches users who upload a selfie instead of a document. The rule is disabled once the MRZ is verified, so a genuine card is never rejected by mistake.

  • No document on a side → reject

    Checked side by side, before scoring: a selfie or blank photo on either side, or the same photo uploaded twice, is rejected and the reason is shown to the user. The MRZ on the back cannot vouch for the front.

  • Expired document → reject

    A hard reject when the expiry date is verified by its MRZ check digit and lies in the past.

  • Specimen document → reject

    A conclusive specimen marking rejects; a weak or ambiguous marking goes to manual review instead of automatic approval.

  • National ID cross-check failed → review

    Splicing the two sides of different cards is only visible in the cross-comparison; automatic approval is blocked and a human decides.

  • AML match → review

    A sanctions list match never produces an automatic rejection under any circumstances.

  • Duplicate face → review

    If the same face was previously approved under a different identity, the session is routed to a human.

  • Chip/declaration mismatch → review

    Where the NFC chip value differs from the declared one, the chip is treated as authoritative and the session goes to review.

  • Required back side missing → reject

    ID cards and residence permits carry the MRZ on the back; without the back image the document cannot be verified, whatever the front scores.

  • MRZ not verified → review

    On a document with an MRZ, automatic approval requires a verified MRZ (at most one failed check digit); an unreadable or invalid MRZ sends the session to a human.

  • Any check failed → review

    If any scored check fails (for example a screen capture, image tampering or an MRZ/printed data mismatch), no automatic approval is given, however well the others score.

  • AML screening not performed → review

    If no name can be extracted, the lists are not loaded or screening errors, the result is not treated as 'no match'; the session goes to a human.

  • Face comparison not performed → review

    If the selfie cannot be compared with the portrait on the document or the chip, or the duplicate face search cannot run, no automatic approval is given.

Liveness

Three layers of liveness assurance

The layers complement each other: the active layer proves the user is really there, the passive layer proves what faces the camera is not a screen or a print, and the heuristic layer looks for artifacts the other two miss.

  1. 01

    Active challenges

    The server generates a random sequence (blink, head turn). Each frame is verified independently on the server against its challenge type — the client claiming 'passed' counts for nothing.

    In-house face detectorEye stateHead pose

  2. 02

    Passive anti-spoofing

    Our in-house anti-spoofing model, trained to catch print, screen replay and moiré artifacts, produces a single genuineness probability.

    In-house modelPrint / screen replayServer-side

  3. 03

    Heuristic signals

    Texture, frequency and colour-space heuristics (LBP / FFT / YCrCb), frame-to-frame consistency and optical-flow motion analysis are weighed as secondary signals.

    LBPFFTOptical flow

Liveness confidence score

(passive score × 0.35 + face match × 0.65) × 100

Two thirds of the weight sits on the face match: a genuine face that does not belong to the person on the document is not rescued by a high passive score. And the score alone is not enough — every gate beside it must be cleared as well.

Gates that must be cleared

Active challenges
allA single missing or failed frame fails the attempt; the user can retry.
Passive score floor
≥ 0.40Lower bound on the ensemble genuineness probability.
Anti-spoof gate
≥ 0.55Ensemble threshold for a 'genuine face' verdict.
Confidence score
≥ 60Lower bound on the combined liveness confidence (a tenant may raise it).
Challenge TTL
300 sValidity window of the single-use challenge id — it closes the replay window.

Face matching and duplicate detection

The document photo and the selfie are compared by our in-house face model. Every approved face is written to a vector database, which catches the same face enrolling again under a different identity.

  • 0.67

    Match threshold

    Lower bound on cosine similarity between document photo and selfie.

  • 0.60

    Quality gate

    Face detection score — no embedding is produced from blurry or partial faces.

  • 0.75

    Duplicate threshold

    Normalized cosine; above it is a 'same person' suspicion and goes to manual review.

AML

Sanctions list screening

Screening uses publicly available lists only; identity data is never sent to a third-party commercial data vendor. Lists are downloaded by a daily cron and written to a local database.

  • OFAC SDN

    U.S. Department of the Treasury — Specially Designated Nationals list (CSV).

  • UN Consolidated

    United Nations consolidated sanctions list (XML).

  • EU FSF

    European Union Financial Sanctions Files (CSV).

  • UK Sanctions List

    UK Foreign, Commonwealth & Development Office (FCDO) — the single UK sanctions list (CSV).

  • PEP

    Politically exposed persons from public sources: Wikidata (heads of state and government, ministers, members of parliament, ambassadors) and the CIA World Leaders list. Family members and close associates are not covered yet.

How matching works

  1. 01The name is normalized: accents and special characters are folded, leaving only A–Z and single spaces.
  2. 02Fuzzy matching runs via rapidfuzz token_sort_ratio, so a different word order does not break the match.
  3. 03Records scoring 90% or above are taken as candidates.
  4. 04A candidate whose birth year conflicts with the one read from the document is eliminated (false-positive filter).
  5. 05If any match survives, the session goes to manual review and the match details are written to the record.

A match never produces an automatic rejection. Because sanctions lists can contain people who merely share a name, the final decision always rests with a human.

Abuse protection

Limits and windows

All of these are counted per environment — sandbox testing never consumes a production limit.

5 / 24 h
Sessions / applicant
Daily sessions openable for the same applicant_external_id.
20 / 24 h
Sessions / IP
Daily sessions an end user can start from the same IP address.
3
Retries
Retry attempts granted after a rejection; beyond that the API returns 409.
60 min
Session lifetime
An incomplete session becomes expired after this window.
15 min
SDK token
Lifetime of the short-lived JWT used by the web and mobile SDKs.
180 days
KYC reuse
An approved verification newer than this is not asked for again (absent an AML alert).
90 days
PII retention
How long after the terminal state personal data is automatically deleted.

Delivery

Getting the result into your system

A distinct event is produced for every status change. Webhooks are signed with HMAC-SHA256; undeliverable ones are retried, and every delivery attempt is recorded.

  • document.verifiedThe document (or chip) was verified; the flow moved on to liveness.
  • document.review_requiredThe document could not be auto-verified, or there is an AML match — an operator decides.
  • document.rejectedDocument checks failed; the status in the body says whether it is retryable (in_progress) or final (declined).
  • liveness.failedOne of the liveness layers was not cleared.
  • session.manual_reviewLiveness passed but there is a duplicate-face suspicion.
  • session.approvedAll checks passed — identity verified.
  • session.rejectedFinal decline (status: declined).
  • session.resubmittedAn analyst asked for steps to be redone; send the user a new link.
  • session.expiredTime ran out: the user never started (expired) or did not finish (abandoned).
  • session.kyc_expiredAn approved verification reached 365 days.

# webhook
X-Signature: sha256=9d41…
{ "event_type": "session.approved",
"session_id": "s_7f3a…",
"status": "approved",
"status_label": "Approved",
"environment": "production" }

Beyond the webhook, the result can always be read via GET /v1/sessions/{id}, so a missed webhook never stalls the flow.

Important note

This page describes the platform's current behavior. Thresholds and limits may be configured more strictly in your tenant agreement; loosening them is not possible.

Ready to integrate?

Get in touch for a sandbox environment, API keys and integration documentation.