Concepts

Workflows

KYC workflows as node graphs of feature modules, how enabled modules are server-enforced, and the GET /v3/workflows/ endpoint.

A KYC workflow is a named, versioned set of verification modules with their configuration. It is built by an operator in Console → Config → KYC Workflows, referenced by workflow_id from the API, and run by the hosted flow when you create a session.

Every workflow starts from one of two mental models: a straight-line checklist of steps, or a graph of repeatable feature nodes wired together. Both compile down to the same runtime representation, so nothing about your integration changes based on which one an operator used to build the workflow — but understanding the graph model explains a few things you'll see in decision_v3 and the module catalog that a flat step list can't.

Workflows are node graphs

Under the hood a workflow is a directed graph of feature-instance nodes. Each node is one module (document verification, liveness, face match, AML screening, …) with its own config, wired to the next node by typed edges. Because nodes are instances, the same feature can appear more than once in a single workflow — each instance carries its own node_id, and results, billing and decisions are keyed by node_id.

For a simple linear workflow, node_id == feature, so the shape is unchanged from a flat list of modules — the graph model is backward-compatible. Nothing about a workflow authored as a flat step list changes behaviour; it's simply the graph's simplest possible shape.

ID_VERIFICATION ─▶ LIVENESS ─▶ FACE_MATCH ─▶ AML_SCREENING

Linear vs graph workflows

The console builder offers both. Most workflows never need more than the linear model — reach for the graph editor specifically when you need a module to repeat, or when a step's input needs to come from a specific earlier node rather than "whatever ran most recently."

Linear workflowGraph workflow
Setup speedFastest — add steps in order, doneSlower — place nodes, wire edges, configure ports
Branching / conditional stepsNo — one fixed sequenceYes — a DAG can route around nodes
Repeatable modules (two ID checks, two questionnaires)No — one instance per featureYes — each instance gets its own node_id
Face-match wiringAuto-wired from the first document + first selfieExplicit — you wire a specific document node's face output into a specific face-match node's face-a port
Result keyingnode_id == feature (results, billing, decisions all read the same as a flat list)node_id per instance — decision_v3 arrays hold one entry per node
Best forMost KYC workflows: one ID, one liveness, one face match, optional AMLMulti-document onboarding, split consumer/business identity checks, re-verification steps inside one workflow
Console structure fieldSIMPLEADVANCED

Tip

Start linear. Only move a workflow to the graph builder when you actually hit a case a flat list can't express — a repeated document capture, or a face-match that needs to pull from a specific one of two uploaded documents rather than "the most recent one."

Note

Switching a workflow from the graph builder back to the simple step list clears the authored graph — the linear list becomes authoritative again. The console builder confirms before doing this because it's a one-way structural change for that workflow version.

Node types

The catalog below is what an operator can drop onto the graph canvas (or add as a step in the linear builder). A subset — document, liveness, face match, NFC, AML, proof of address, contact OTP, questionnaire, video call and confirm details — cover the vast majority of real workflows:

Node typeFeature key(s)What it does
DocumentID_VERIFICATIONAuthenticate government IDs across 220+ countries and 14,000+ document types.
LivenessLIVENESSConfirm a real, present human — defeating photos, masks and deepfakes.
Face matchFACE_MATCH1:1 biometric comparison of the document portrait against the selfie.
NFCNFC_VERIFICATIONRead the chip in e-passports and biometric IDs for tamper-proof data.
AMLAML_SCREENINGScreen against global sanctions, PEP and criminal watchlists (auto-updated).
Proof of addressPROOF_OF_ADDRESSValidate a utility bill or bank statement and extract the address.
Contact OTPEMAIL_VERIFICATION, PHONE_VERIFICATIONConfirm ownership of an email address or phone number with a one-time code (SMS or voice).
QuestionnaireQUESTIONNAIRECollect structured answers with a form built in the console's questionnaire designer.
Video callVIDEO_CALLLive agent-attended video verification with server-side recording — the applicant talks to a trained agent who confirms identity on camera.
Confirm detailsCONFIRM_DETAILSShow the applicant the identity fields read from their ID and let them correct any OCR mistakes before continuing.

Repeatable nodes

Only some features can appear more than once in the same workflow: ID_VERIFICATION, DOCUMENT_AI, PROOF_OF_ADDRESS and QUESTIONNAIRE are repeatable — useful for "front of ID + a second supporting document," or two different questionnaires gating different steps. LIVENESS, AML_SCREENING, IP_ANALYSIS and AGE_ESTIMATION each run once per session regardless of how many nodes reference them — the engine mirrors the single run's verdict onto any sibling nodes so the session never gets stranded waiting on a second run that was never going to happen.

Typed face edges

Two node types produce a face: ID_VERIFICATION, NFC_VERIFICATION, LIVENESS and AGE_ESTIMATION. FACE_MATCH consumes two faces, on two distinct typed input ports — face-a (the document-side face) and face-b (the selfie-side face). In the graph builder you wire a specific document node's face output into face-a and a specific liveness node's face output into face-b; in a linear workflow this wiring is synthesized automatically from the first document node and the first liveness node. A face-match node wired to a document node that was never uploaded withholds rather than matching against an unrelated sibling's document — it never silently cross-matches the wrong pair of nodes.

How a graph walk resolves node_id-keyed results

Every module result, billing debit and decision entry is keyed by the node's own node_id, not by its feature name. This is what lets ID_VERIFICATION appear twice in one workflow without one instance's result clobbering the other's:

Workflow graph (authored once, in the console builder)
────────────────────────────────────────────────────────────────
  [doc_front]──face──┐
  ID_VERIFICATION     │
                       ├──▶ face-a ──┐
  [doc_back]           │              │
  ID_VERIFICATION      │      [fm_1]  │
                       │      FACE_MATCH ──▶ [aml_1] AML_SCREENING
  [selfie]──face───────┘      face-b   │
  LIVENESS                             │

                                   decision_v3

Hosted flow walk (one session)
────────────────────────────────────────────────────────────────
  applicant screen 1 → capture doc_front   → ModuleResult(session, node_id="doc_front")
  applicant screen 2 → capture doc_back    → ModuleResult(session, node_id="doc_back")
  applicant screen 3 → selfie (liveness)   → ModuleResult(session, node_id="selfie")
  engine resolves fm_1's wired inputs:
      face-a ← doc_front's face output
      face-b ← selfie's face output
  engine runs fm_1                          → ModuleResult(session, node_id="fm_1")
  engine runs aml_1                         → ModuleResult(session, node_id="aml_1")

decision_v3 (grouped by category, each entry tagged with its node_id)
────────────────────────────────────────────────────────────────
  id_verifications: [ {node_id: doc_front, …}, {node_id: doc_back, …} ]
  liveness_checks:  [ {node_id: selfie, …} ]
  face_matches:     [ {node_id: fm_1, …} ]
  aml_screenings:   [ {node_id: aml_1, …} ]

The hosted flow walks the graph in topological order, rendering one screen per node — a repeated feature (two document captures) gets its own screen each time, and an INFORMATION node renders wherever it's placed in the order. Anything downstream that reads results — decision_v3, the evidence checklist, per-node credit billing, incomplete-module detection — keys off node_id, so two instances of the same feature never overwrite each other's outcome. See Decisions for the shape each array entry takes.

Note

A legacy or simple linear workflow still produces exactly this shape — with node_id == feature for every node — so reading decision_v3 never requires knowing whether the workflow that ran was authored as a flat list or a graph.

Templates

The console builder offers pre-built starting points so operators aren't wiring a workflow from a blank canvas:

TemplateFeatures
KYC VerificationID_VERIFICATION, LIVENESS, FACE_MATCH, IP_ANALYSIS
KYC + AMLID_VERIFICATION, LIVENESS, FACE_MATCH, AML_SCREENING, IP_ANALYSIS
Age VerificationID_VERIFICATION, LIVENESS, AGE_ESTIMATION
Proof of AddressID_VERIFICATION, PROOF_OF_ADDRESS
Full OnboardingID_VERIFICATION, LIVENESS, FACE_MATCH, PROOF_OF_ADDRESS, AML_SCREENING, IP_ANALYSIS

Every template is a linear starting point — an operator can still convert it to a graph later if they need repeatable nodes or custom face-match wiring.

Listing workflows

curl -H "x-api-key: $TF_API_KEY" "$BASE/v3/workflows/"

The response is a bare array — one object per workflow the API key's tenant owns:

[
  {
    "id": "9c2f1c4e-9a2b-4f2f-8f0f-0f1f3f4f5f6f",
    "name": "FreeKYC",
    "status": "live",
    "features": ["ID_VERIFICATION", "LIVENESS", "FACE_MATCH"]
  },
  {
    "id": "b1d0f8a2-…",
    "name": "Enhanced",
    "status": "live",
    "features": ["ID_VERIFICATION", "LIVENESS", "FACE_MATCH", "AML_SCREENING", "IP_ANALYSIS"]
  }
]
const res = await fetch(`${BASE}/v3/workflows/`, {
  headers: { "x-api-key": process.env.TF_API_KEY },
});
const workflows = await res.json();
const live = workflows.filter((w) => w.status === "live");
resp = requests.get(f"{BASE}/v3/workflows/", headers={"x-api-key": os.environ["TF_API_KEY"]})
workflows = resp.json()
live = [w for w in workflows if w["status"] == "live"]
idstring
Workflow UUID — pass as workflow_id on POST /v3/session/.
namestring
Human-readable workflow name.
statusstring
live, draft or archived.
featuresarray
The enabled module feature keys, in workflow order.

Note

GET /v3/workflows/ returns the flattened features list regardless of whether the workflow was authored linearly or as a graph — it's a summary view for choosing a workflow_id, not the graph itself. Node-level detail (node_id, wiring, per-instance config) is a console-builder concern, not something the partner API exposes directly.

Which workflow runs?

When you omit workflow_id on session create, resolution falls back, in order, to the tenant's configured default workflow, then the workflow flagged as default, then the most recent live one. A brand-new tenant with no workflow gets a default (ID + liveness + face match) provisioned automatically, so a session always resolves to a real workflow.

Tip

Pin workflow_id explicitly in production. Relying on "the tenant default" means an operator changing a console setting silently changes what your integration runs.

Enabled modules are server-enforced

The set of modules that run is decided server-side, from the workflow's enabled features — it is not something the client can add to, skip, or downgrade:

  • The hosted flow runs exactly the workflow's enabled modules. A required evidence-driven module (document, liveness, face match, contact, questionnaire) that never produces a terminal result leaves the session incomplete — it will not silently approve. A skipped selfie cannot pass as a verified identity.
  • The configured liveness method (passive / colour-flash / 3D-action) is enforced by the server and cannot be swapped for a weaker one from the client.
  • Modules that have no real web implementation — currently NFC verification and database validation — are hidden from the builder. If a legacy or imported workflow still enables one, the engine routes it to manual review rather than fabricating a pass.
  • The standalone tools and priced modules are gated by tenant entitlement: a feature your tenant is not entitled to returns 403 feature_not_enabled.

Note

The features array on GET /v3/workflows/ is the authoritative list of what a workflow will run. Read it to know which modules — and therefore which credits — a session will exercise before you create it.

Module catalog

Common feature keys you will see in features and decision_v3:

Feature keyModule
ID_VERIFICATIONDocument verification
LIVENESSLiveness
FACE_MATCHFace match
AML_SCREENINGAML / PEP / sanctions
NFC_VERIFICATIONNFC chip read
PROOF_OF_ADDRESSProof of address
DATABASE_VALIDATIONDatabase validation
IP_ANALYSISDevice & IP analysis

See KYC modules for the behaviour and configuration of each, and Credits for what each one costs.

Common pitfalls

Warning

Assuming features order is execution order for a graph workflow. For a linear workflow, features is the execution order. For a workflow with branches, the flattened array still lists every enabled feature, but the actual per-applicant path depends on the graph's edges — don't hard-code an assumption that step 2 always follows step 1.

Warning

Treating a node_id as if it were a stable feature name. Once a workflow repeats a feature, the node_id values are workflow-specific labels, not a fixed enum. Read them from decision_v3 at runtime rather than hard-coding "ID_VERIFICATION" as the only possible key for a document result.

FAQ