Reference
Workflows
List the tenant's KYC workflows and their enabled features, then route sessions to a specific workflow_id.
KYC workflows are designed in Console → Config → KYC Workflows. Each workflow is an ordered graph of enabled modules — document capture, liveness, face match, AML screening, and more — that a session runs end to end. A tenant can keep several workflows side by side (a light "age-only" check, a full onboarding, a re-verification variant) and pick which one a given user runs at session-create time.
Use GET /v3/workflows/ to discover the workflow UUIDs you pass as
workflow_id to POST /v3/session/.
Info
This endpoint is on the /v3 surface and authenticates with x-api-key. Base
URL is https://<your-domain>/v3 — there is no /api/v1 prefix on the
ThirdFactor Verify surface. Errors carry { "detail": "…" }.
List workflows
GET /v3/workflows/
x-api-key: <tenant_api_key>Returns a bare JSON array of workflow objects for the API key's tenant. There are no query parameters — the list is small (workflows are authored by hand in the console) and returned whole.
curl -s https://acme.thirdfactor.ai/v3/workflows/ \
-H "x-api-key: $OBSIDIAN_API_KEY"const resp = await fetch("https://acme.thirdfactor.ai/v3/workflows/", {
headers: { "x-api-key": process.env.OBSIDIAN_API_KEY },
});
const workflows = await resp.json();
// Route by name, then reuse the id on session create:
const onboarding = workflows.find((w) => w.name === "Standard onboarding");import requests
resp = requests.get(
"https://acme.thirdfactor.ai/v3/workflows/",
headers={"x-api-key": OBSIDIAN_API_KEY},
)
workflows = resp.json()
onboarding = next(w for w in workflows if w["name"] == "Standard onboarding")Response Example
[
{
"id": "6f0e2b7a-1c2d-4e5f-8a9b-0c1d2e3f4a5b",
"name": "Standard onboarding",
"status": "live",
"features": ["ID_VERIFICATION", "LIVENESS", "FACE_MATCH", "AML_SCREENING"]
},
{
"id": "b81c9f4d-2a3e-4c6f-9d0a-1b2c3d4e5f60",
"name": "Age check only",
"status": "live",
"features": ["ID_VERIFICATION", "AGE_ESTIMATION"]
},
{
"id": "d0e1f2a3-b4c5-4d6e-8f90-1a2b3c4d5e6f",
"name": "Onboarding v2 (draft)",
"status": "draft",
"features": ["ID_VERIFICATION", "NFC_VERIFICATION", "LIVENESS", "FACE_MATCH"]
}
]idstringWorkflow UUID. Pass this as workflow_id to
POST /v3/session/.
namestringstatusstringPublish state — one of draft, live, or archived. Only route production
users to a live workflow; draft workflows are still being edited and
archived ones are retired.
featuresarrayEnabled feature keys, in graph order — the modules the session will run. These
are the canonical uppercase catalog keys (e.g. ID_VERIFICATION,
LIVENESS, FACE_MATCH, AML_SCREENING).
How features map to modules
Each entry in features is a catalog key that maps one-to-one to a KYC module.
The most common keys:
| Feature key | Module | Docs |
|---|---|---|
ID_VERIFICATION | Document capture + OCR + authenticity | Document |
NFC_VERIFICATION | Chip read of an e-passport / e-ID | NFC |
PASSIVE · PASSIVE_VIDEO · FLASHING · 3D_ACTION · GESTURE | Liveness method (one per workflow) | Liveness |
LIVENESS | Liveness step (method resolved from settings) | Liveness |
FACE_MATCH | Selfie ↔ document face comparison | Face match |
AGE_ESTIMATION | Age from selfie / document | Overview |
AML_SCREENING | Sanctions / PEP / watchlist screening | AML |
PROOF_OF_ADDRESS | Address document capture | Proof of address |
DATABASE_VALIDATION | Government / authoritative DB cross-check | Database validation |
EMAIL_VERIFICATION · PHONE_VERIFICATION | OTP contact verification | Overview |
IP_ANALYSIS · QUESTIONNAIRE · VIDEO_CALL | Risk signal / form / vKYC steps | Overview |
Note
features reflects the workflow's enabled nodes only (a node disabled in
the builder is omitted). A feature can legitimately repeat in the graph — e.g.
two ID_VERIFICATION nodes to capture a primary and a secondary document — so
treat features as a multiset in graph order, not a unique set.
Using a workflow_id on session create
Pass the id you discovered here as workflow_id:
curl -X POST https://acme.thirdfactor.ai/v3/session/ \
-H "x-api-key: $OBSIDIAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workflow_id": "6f0e2b7a-1c2d-4e5f-8a9b-0c1d2e3f4a5b",
"vendor_data": "your-user-reference-123"
}'await fetch("https://acme.thirdfactor.ai/v3/session/", {
method: "POST",
headers: {
"x-api-key": process.env.OBSIDIAN_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
workflow_id: onboarding.id,
vendor_data: "your-user-reference-123",
}),
});requests.post(
"https://acme.thirdfactor.ai/v3/session/",
headers={"x-api-key": OBSIDIAN_API_KEY},
json={"workflow_id": onboarding["id"], "vendor_data": "your-user-reference-123"},
)Tip
Omit workflow_id to run the tenant's default workflow. You only need this
endpoint when you route different users to different workflows (e.g. a
lightweight age check for one product and full onboarding for another). If
every user runs the same check, hard-coding nothing and letting the default
apply is simpler and one fewer call.
Errors
| Status | Code | When |
|---|---|---|
401 | invalid API key | Missing or wrong x-api-key. |
429 | rate-limited | Over the kyc_v3_read bucket. |
A workflow_id that doesn't exist for the tenant is not caught here (this
endpoint only lists) — it surfaces at session create as
404 workflow_id not found. See Sessions → Errors.
Common pitfalls
Warning
Hard-coding a workflow_id that later gets archived. Workflow UUIDs are
stable, but a workflow can move to archived when a team publishes a v2. If you
cache an id, re-list periodically and confirm status === "live" before routing
production traffic through it — a session against an archived workflow is not
what your compliance team expects to run.
Warning
Assuming features is lowercase or de-duplicated. The keys are the
uppercase catalog keys (FACE_MATCH, not face_match) and a feature may appear
more than once. Match against the exact keys in the table above.