Verification
KYC modules
The module catalog for ThirdFactor Verify — what each module does, where it runs, and how to pick the right set for your risk tier.
Obsidian ships ThirdFactor Verify, a built-in, standards-based KYC engine: document verification, passive / colour-flash / 3D-action / gesture liveness, 1:1 face match, AML screening, proof of address, document AI, NFC chip reads, and agent video calls. No external verification SDK and no third-party verification credentials are involved — heavy inference runs on your own deployment's inference service.
Modules are the building blocks of a workflow (built in Console → Config → KYC Workflows). A session runs one workflow's enabled modules once. Each module has its own configuration, its own result on the session, and its own credit rate (set per tenant/module — there is no fixed public price).
Info
Workflows are DAGs (directed graphs) of repeatable feature-instance nodes, not a fixed linear list. You can run ID_VERIFICATION twice in one workflow (front ID + proof-of-address document, or primary + secondary ID) and wire each FACE_MATCH node to the specific document node it should compare against. See Workflows for the graph model.
Where each module runs
Not every module is applicant-facing. Three execution surfaces exist:
- Hosted flow — the applicant captures evidence inside the hosted WebView / iframe (document photos, selfie, OTP). Every client SDK supports these identically.
- Server-side — no applicant capture; the engine runs the check in the background from data already on the session.
- Native-only — needs a device radio the WebView cannot reach (NFC). Present in the iOS / Android / Flutter SDKs; on web it falls back to a server-side review.
Info
The workflow decides which modules run and how — including which liveness method. The hosted flow asks the server what to do at each step; the client cannot downgrade or skip a check. A tampered browser gets a review or a decline, never a cheap approval.
Capability comparison
Every module maps to a ModuleResult with a status of approved, declined, in_review, or not_started. The table below adds what each module typically declines or reviews on, so you can see at a glance which checks carry real fraud-detection weight versus which ones just capture data.
| Module (feature key) | What it checks | Runs | Typical outcomes | Common failure reasons |
|---|---|---|---|---|
ID_VERIFICATION | Document authenticity, OCR/MRZ/barcode field extraction, tamper analysis | Hosted flow | approved / declined / review | Forgery suspected, MRZ↔visual mismatch, unreadable capture, expired (if disallowed), document type not permitted |
NFC_VERIFICATION | Cryptographic chip integrity (BAC/PACE, DG1/DG2/SOD, passive authentication) | Native-only (web → review) | approved / review / declined | No CSCA masterlist configured (caps at review), chip data tampered (decline), host has no NFC radio |
LIVENESS | Real, present human vs. photo/replay/mask/deepfake | Hosted flow | approved / declined / review | Spoof/replay detected (active methods), capture-quality retries exhausted, static-capture (injection) suspected |
FACE_MATCH | 1:1 biometric comparison, document portrait vs. selfie | Hosted flow | approved / declined / review | Impostor (cosine below model threshold), no face detected after retries, similarity below tenant's review floor |
AGE_ESTIMATION | Biometric age estimate from the selfie | Server-side | approved / review / declined | Estimated age under the configured minimum (decline or review depending on decline_underage) |
AML_SCREENING | Sanctions / PEP / adverse-media / POI watchlist screening | Server-side | approved / review / declined | Confirmed hit on a decline-action list, hit on a review-action list, insufficient identity data to screen |
PROOF_OF_ADDRESS | Address-document classification, freshness, address extraction | Hosted flow | approved / review | Stale document (older than max_age_days), unclassifiable document, no address found |
DOCUMENT_AI | Generic OCR/classification of an arbitrary custom document | Hosted flow | approved / review | Unreadable capture, configured fields not found |
IP_ANALYSIS | VPN / proxy / Tor / datacenter and abuse signals | Server-side | approved / review / declined | Tor exit node (decline), VPN detected (decline), elevated abuse score (review) |
DATABASE_VALIDATION | Cross-check against an external authoritative database | Needs an external provider → review | review only | No provider wired up — always routes to review |
EMAIL_VERIFICATION | One-time-code ownership check | Hosted flow | approved / in-progress / review | Watchlisted email address held for review |
PHONE_VERIFICATION | SMS/voice OTP ownership check | Hosted flow | approved / in-progress / review | Watchlisted phone number held for review |
QUESTIONNAIRE | Structured answers via a console-built form | Hosted flow | approved | — (informational capture, doesn't gate) |
CONFIRM_DETAILS | Applicant corrects OCR'd fields before continuing | Hosted flow | approved | — |
INFORMATION | Plain instruction/consent screen | Hosted flow | approved | — |
PRODUCT_SELECTION | Applicant picks one of your products | Hosted flow | approved | — |
VIDEO_CALL | Agent-attended live video verification, server-recorded | Hosted flow (agent-attended) | approved / declined / review | Agent-driven; depends on the call outcome the operator records |
Warning
Two modules have no automated implementation and must never fake-approve. NFC_VERIFICATION on a non-native host, and DATABASE_VALIDATION (needs an external data provider), route to manual review instead of silently passing. They are hidden from the workflow builder but can still appear in legacy or imported workflows.
Which modules do I need?
There is no universal answer — it depends on your regulatory obligations and fraud risk — but most integrations converge on one of three tiers. Use this as a starting point, then tune per jurisdiction with your compliance team.
Identity capture with a live-human check. Suitable for lower-risk onboarding (content platforms, marketplaces, age-gated services) where you need "a real person who presented this document," not deep financial-crime assurance.
ID_VERIFICATION— document capture and field extractionLIVENESS(PASSIVEorPASSIVE_VIDEO) — highest-conversion, low-frictionFACE_MATCH— confirms the applicant is the document holder
Note
This tier alone does not satisfy most AML/CFT regulatory regimes — it proves identity, not the absence of sanctions/PEP exposure. Add AML_SCREENING before treating it as compliant KYC for a regulated product.
Tip
You don't have to choose a fixed tier per product — a graph workflow can branch: run AML_SCREENING for everyone, then route only sessions with a medium risk level or a large transaction amount to an NFC_VERIFICATION + VIDEO_CALL sub-path. See Workflows for conditional branching.
Reading module results
Per-module outcomes come back on GET /v3/session/<id>/ and, in summary form, in decision_v3 on GET /v3/session/<id>/decision/. Each result carries a status (approved / declined / in_review / not_started), an optional score, warnings, and a data blob. The evidence checklist (/checklist/) tells you what was captured; the timeline (/logs/) tells you what happened and when.
{
"session_id": "6f0e2b7a-…",
"status": "in_review",
"decision": "review",
"decision_v3": {
"id_verifications": [{ "node_id": "ID_VERIFICATION", "status": "approved", "score": 92.0, "warnings": [], "data": { "detected_type": "passport" } }],
"liveness_checks": [{ "node_id": "LIVENESS", "status": "approved", "score": 87.5, "warnings": [], "data": { "method": "PASSIVE" } }],
"face_matches": [{ "node_id": "FACE_MATCH", "status": "in_review", "score": 61.2, "warnings": ["similarity_below_minimum"], "data": {} }],
"aml_screenings": [{ "node_id": "AML_SCREENING", "status": "approved", "score": 100, "warnings": [], "data": { "risk_level": "clear" } }],
"nfc_verifications": [],
"poa_verifications": [],
"reviews": []
},
"reason": "similarity_below_minimum"
}Every decision_v3 array key maps 1:1 to a feature key: id_verifications, liveness_checks, face_matches, aml_screenings, nfc_verifications, poa_verifications, phone_verifications, email_verifications, ip_analyses, database_validations, questionnaire_responses. Each entry carries node_id (so you can tell repeated instances of the same module apart in a graph workflow), status, score, warnings, and data.
Warning
The client-visible result is advisory. Grant access or move money only after your backend confirms the authoritative decision from the signed webhook (identity.kyc.session.approved / .declined / .review / .expired) or GET /v3/session/<id>/decision/. See Decisions for the full contract.
Standalone tools
Several module engines are also exposed as stateless one-shot utilities under /v3/tools/* — AML check, face search, signature verify/search/enroll, document extract. They create no session and carry no audit trail. See Standalone tools.
Comparing execution modes
| Hosted-flow module | Server-side module | Standalone tool | |
|---|---|---|---|
| Applicant capture | ✅ Yes | ❌ No | ❌ No |
| Creates a session | ✅ Yes | ✅ Yes | ❌ No |
| Audit trail / evidence checklist | ✅ Yes | ✅ Yes | ❌ No |
Contributes to decision_v3 | ✅ Yes | ✅ Yes | ❌ No (returns its own response) |
| Good for a compliance decision | ✅ Yes | ✅ Yes | ⚠️ Only as enrichment/triage input |
| Typical use | Onboarding a new applicant | Background checks during onboarding | Ad-hoc lookups, back-office tooling |
FAQ
Related
Document verification
OCR, MRZ, barcode extraction and tamper checks.
AML screening
Sanctions, PEP, adverse-media and POI screening.
Standalone tools
One-shot lookups with no session attached.
Workflows
The DAG model — nodes, edges, and versioning.
Decisions
The stable decision contract to branch your integration on.
Hosted flow
How a session's URL runs the workflow end to end.