> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qa.esectra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Verification flows

> Decide what a person is asked for, and which documents are accepted from which country.

A verification flow is a reusable, versioned description of one verification journey. An organisation admin drafts it in Control (or over the API), chooses one of three types, selects the countries and the document types accepted for each, and publishes it. Publishing writes an immutable revision. A session opened under a flow freezes that revision: a later change to the flow, or to the catalog it was built from, never changes what a session that has already started asks for or accepts.

## The three types

| Type | Checks | The person is asked for |
| - | - | - |
| `BIOMETRIC` | `LIVENESS`, `AUTHENTICITY` | A selfie only |
| `DOCUMENT` | `DOCUMENT_EXTRACTION`, `DOCUMENT_CONSISTENCY` | A document only |
| `FULL_IDENTITY` | `FACE_MATCH`, `LIVENESS`, `AUTHENTICITY`, `DOCUMENT_EXTRACTION`, `DOCUMENT_CONSISTENCY` | Both |

The checks are fixed by the type. There is no way to publish a flow that runs no checks.

A `BIOMETRIC` flow proves that a live person was present and that the capture was not manipulated. It does **not** prove who they are: nothing in it compares the face to a document or to an earlier enrolment, and its outcome must not be read as an identity match. A `DOCUMENT` flow reads and checks a document and never opens the camera on the person's face. A `FULL_IDENTITY` flow is the strict default every session ran before flows existed, including the first-time face comparison against the document portrait.

## What may be selected

The documents a flow may offer come from the deployment's **verification capability catalog** - the `(country, document type)` pairs the platform operator has enabled. Read it with any tenant credential:

```bash theme={null}
curl "$ESECTRA_BASE_URL/v1/verification-catalog" \
  -H "Authorization: Bearer $ESECTRA_API_KEY"
```

Each entry carries a stable `code` (`NG_PASSPORT`, `NG_NIN_SLIP`, `GH_DRIVING_LICENCE`, ...), the `country` and `country_name`, the `document_type`, a `display_name`, and an `extraction_support` of `AUTOMATED` (a reader exists for it) or `MANUAL_REVIEW` (the document is captured, inspected and used for the face comparison, and its reading goes to a reviewer). Disabled entries are left out unless you add `?include_disabled=true`; a disabled entry cannot be selected in a new draft, but every published revision and open session that already carries it keeps it.

## Create, publish, archive

Flows are created and changed by an organisation admin (`VERIFICATION_FLOWS_MANAGE`, held by the `ADMIN` role). They are read by any tenant credential, including an API key, because an integration has to choose one.

```bash theme={null}
# Draft
curl -X POST "$ESECTRA_BASE_URL/v1/verification-flows" \
  -b "esectra_session=$CONTROL_SESSION" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Onboarding",
    "flow_type": "FULL_IDENTITY",
    "documents": [
      { "country": "NG", "capabilities": ["NG_NIN_SLIP", "NG_PASSPORT"] },
      { "country": "GH", "capabilities": ["GH_PASSPORT"] }
    ],
    "return_url": "https://your-platform.example/verification/done"
  }'

# Publish revision 1
curl -X POST "$ESECTRA_BASE_URL/v1/verification-flows/$FLOW_ID/publish" \
  -b "esectra_session=$CONTROL_SESSION"
```

Every rule is enforced by the server, whatever the client sent: a `BIOMETRIC` draft may select no documents; a `DOCUMENT` or `FULL_IDENTITY` draft must select at least one country with at least one document type; a code must exist in the catalog, be enabled, and belong to the country it is listed under; the return URL, when present, must be `https` with no credentials or fragment. A draft that fails answers `422 INVALID_FLOW` with the reason.

`PUT /v1/verification-flows/{flow_id}` changes the draft without touching the published revision; publish again to make a new revision. `POST .../archive` retires the flow: no new session can open under it, while its revisions and the sessions that used them are kept. `GET .../revisions/{n}` returns any revision as it was published.

## Open a session under a flow

Add `flow_id` when opening a session. Everything else about the call is unchanged, and omitting `flow_id` keeps exactly the behaviour integrations have today: the tenant policy's checks, every country, every document type.

```bash theme={null}
curl -X POST "$ESECTRA_BASE_URL/v1/verifications/sessions" \
  -H "Authorization: Bearer $ESECTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject_id":"customer-123","flow_id":"vf_...","state":"order-42"}'
```

The response carries `flow` (`flow_id`, `revision`, `flow_type`) beside `capture_url`. The session is frozen to the revision that was current when it opened. The hosted page asks only for the steps the flow needs and offers only the flow's countries and, for each, its document types; the capture route refuses a document whose declared country and type are not in the frozen configuration (`422 DOCUMENT_NOT_ALLOWED`), a document with no declaration (`422 DECLARATION_REQUIRED`), and a capture the flow does not ask for (`422 CAPTURE_NOT_REQUIRED`).

A draft that was never published answers `409 FLOW_NOT_PUBLISHED`; an archived flow `409 FLOW_ARCHIVED`; an unknown or another tenant's flow `404 FLOW_NOT_FOUND`.

`state` is optional, at most 256 characters, and comes back unchanged on the return redirect - see [Return flow](/verify/return-flow).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.