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 weight, 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
5
Document profiles — each with its own weight set
3
Liveness layers — active, passive ML, heuristic

Step by step

Session flow

  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). 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.

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

    Checks that run at this step

    • Supported types: Turkish ID card, passport, driving licence, residence permit, Ghana Card
    • 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 platform

    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 (with per-profile weights in the table below)
    • 4 unweighted hard rules: document presence, file integrity, document expiry, specimen detection
    • Cross-field consistency between front and back (MRZ ↔ printed fields)
  5. 05IDotta platform

    AML / sanctions screening

    The name extracted from the document is screened against open sanctions lists synced daily. Names are normalized (accents and Turkish 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
  6. 06End 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
  7. 07IDotta 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 / 5

    Verify your identity

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

    Start

    Secured by

    Intro & consent

  • 9:412 / 5

    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 / 5

    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:414 / 5

    Almost done

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

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

    Secured by

    Processing

  • 9:415 / 5

    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.

Document engine

Check list and weights

Each document type carries its own weight set — the MRZ dominates on a passport, while text readability leads on a driving licence that has no MRZ. The percentages below are the contribution shares when every check runs.

Check list and weights
CheckTR ID CardPassportDriving Lic.Residence P.Ghana Card
MRZ ValidationICAO 9303 machine-readable zone: check digits and field consistency.19.5%27.1%—25.3%23.5%
Text ReadabilityHow many document-specific keyword groups OCR can find.8.8%10.4%19.8%12.6%9.8%
Sharpness / FocusBlur measured via Laplacian variance.8.8%10.4%12.3%10.5%9.8%
Tampering (ELA)Error Level Analysis: traces of re-saved or edited regions.7.1%8.3%9.9%8.4%7.8%
Face DetectionPresence and placement of the biometric portrait on the document.7.1%8.3%9.9%8.4%7.8%
Screen/Print DetectionMoiré patterns via FFT periodic peak analysis — a photo of a screen.7.1%8.3%9.9%8.4%7.8%
Document ContourID-1 rectangular edge detection and the 1.586:1 aspect ratio.5.3%6.3%7.4%6.3%5.9%
Brightness & GlareOverly dark, blown-out or flash-glared frames.4.4%5.2%6.2%5.3%4.9%
Local Texture ConsistencyRegional texture deviation — region-level splicing detection.4.4%5.2%6.2%5.3%4.9%
ResolutionA readability floor of at least 600×400 pixels.2.7%3.1%3.7%3.2%2.9%
TR National ID AlgorithmMathematical checksum validation of the Turkish national ID number.8.0%—14.8%——
Ghana Card PINGHA-XXXXXXXXX-X format plus cross-check against the MRZ.————8.8%
Colour SignatureThe turquoise/pink print colour signature of the Turkish ID card.7.1%————
Micro PrintPresence of the repeating fine print pattern on the back side.4.4%————
Data ConsistencyCross-comparison of MRZ fields against the printed fields on the front.5.3%7.3%—6.3%5.9%

A check that could not run (the MRZ with no back side, for example) never enters the score; the remaining checks are rescaled among themselves. Missing data therefore does not behave like a low score.

Unweighted hard rules

These four checks do not enter the score — they set the outcome directly. Even at a score of 100, a failure here blocks automatic approval.

  • DOCUMENT_PRESENCE

    Document Presence

    Each side must show a document: a keyword group, a verified MRZ or at least 60 readable characters — checked before any scoring.

  • INTEGRITY

    File Integrity

    Corrupt, empty or under-5 KB files — no other check is run.

  • EXPIRY

    Document Validity

    A check-digit-verified expiry date in the past is a hard reject.

  • SPECIMEN

    Specimen Detection

    Specimen markings and known sample document numbers.

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.

  • 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.68

    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 (optional)

    An operator-supplied politically exposed persons source — skipped when not configured.

How matching works

  1. 01The name is normalized: accents and Turkish 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 WebSDK.
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.