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"]
  }
]
idstring

Workflow UUID. Pass this as workflow_id to POST /v3/session/.

namestring
Human-readable workflow name, as set in the console builder.
statusstring

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

featuresarray

Enabled 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 keyModuleDocs
ID_VERIFICATIONDocument capture + OCR + authenticityDocument
NFC_VERIFICATIONChip read of an e-passport / e-IDNFC
PASSIVE · PASSIVE_VIDEO · FLASHING · 3D_ACTION · GESTURELiveness method (one per workflow)Liveness
LIVENESSLiveness step (method resolved from settings)Liveness
FACE_MATCHSelfie ↔ document face comparisonFace match
AGE_ESTIMATIONAge from selfie / documentOverview
AML_SCREENINGSanctions / PEP / watchlist screeningAML
PROOF_OF_ADDRESSAddress document captureProof of address
DATABASE_VALIDATIONGovernment / authoritative DB cross-checkDatabase validation
EMAIL_VERIFICATION · PHONE_VERIFICATIONOTP contact verificationOverview
IP_ANALYSIS · QUESTIONNAIRE · VIDEO_CALLRisk signal / form / vKYC stepsOverview

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

StatusCodeWhen
401invalid API keyMissing or wrong x-api-key.
429rate-limitedOver 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.

FAQ