> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.withverifa.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.withverifa.com/_mcp/server.

# Verifications & Checks

> Verification types, check lifecycle, and available policy executors.

A **verification check** is a single step in a workflow — one unit of identity
verification logic. Checks are executed by the workflow engine in the order
defined by your workflow graph. Each check produces an independent pass/fail
result.

## Check lifecycle

```
pending → passed
        → failed
        → error
        → skipped
        → requires_retry
```

| Status           | Description                                                                            |
| ---------------- | -------------------------------------------------------------------------------------- |
| `passed`         | The check completed successfully and passed.                                           |
| `failed`         | The check completed but the subject did not pass.                                      |
| `error`          | An unrecoverable error occurred (e.g., service unavailable).                           |
| `skipped`        | The check was skipped because prerequisites were missing (e.g., OCR data unavailable). |
| `pending`        | The check is currently executing.                                                      |
| `requires_retry` | A transient failure occurred. The engine will retry automatically (up to 4 retries).   |

Each check attempt is stored as an immutable record with an incrementing
`attempt` number. Retries create new records rather than overwriting the
original.

## Check categories

### Extraction

| Check          | Description                                                                                                                                                                                    |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `document_ocr` | Scans the ID document front and back images. Extracts name, date of birth, address, document number, expiration date, and other fields. Validates MRZ checksums and barcode data when present. |

### Biometric verification

| Check           | Description                                                                                                                                                                                                                                                                                                                                                           |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `face_match`    | Compares the selfie against the ID photo using ArcFace face embeddings. Returns a similarity score (0.0–1.0). Configurable security levels: `standard` (0.35 threshold), `strict` (0.50), `very_strict` (0.60). Includes liveness validation when available.                                                                                                          |
| `nfc_chip_auth` | Reads and validates the NFC chip in e-passports. Performs passive and active authentication, cross-references chip data against OCR, and compares the chip photo against the selfie.                                                                                                                                                                                  |
| `face_reauth`   | Returning User Face Verification — compares a fresh selfie 1:1 against a previously verified person's stored facial records (matched by `external_ref`). Runs on document-free workflows; liveness is always enforced. Sensitivity levels: `conservative`, `balanced`, `aggressive`. See [Returning-user re-verification](/workflows#returning-user-re-verification). |

### Document evaluation

| Check                    | Description                                                                                                                                                                                                                                                                                                                                                    |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `document_expiration`    | Rejects documents that have expired based on the extracted expiration date.                                                                                                                                                                                                                                                                                    |
| `id_document_filter`     | Filters by country and document type matrix. Reject or allow specific combinations (e.g., only accept US passports and driver's licenses).                                                                                                                                                                                                                     |
| `proof_of_address_check` | Validates proof-of-address documents against a policy you configure: accepted document types, per-type recency windows, and how closely the name and address must match. Country-aware — it normalises addresses, postcodes, and PO-box patterns per issuing country. Three-state: `passed`, `review`, or `failed`. See [Proof of address](#proof-of-address). |

### Compliance screening

| Check                 | Description                                                                                                                                                    |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `watchlist_screening` | Screens against built-in OFAC, EU, UN, and UK sanctions lists. Automatically injected into all workflows — you don't need to add it manually.                  |
| `aml_screening`       | Enhanced AML screening against 6,000+ global watchlists, PEP databases, and adverse media sources. Supports continuous monitoring with automatic re-screening. |
| `email_risk_enhanced` | Email risk analysis including breach history, deliverability, domain age, and fraud scoring.                                                                   |
| `phone_risk_enhanced` | Phone number risk analysis including SIM swap detection, porting history, VoIP detection, and carrier verification.                                            |
| `address_validation`  | Address standardization and deliverability verification. Validates against postal databases and returns geocoded coordinates.                                  |

### Fraud detection

| Check                 | Description                                                                                                                                                            |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `duplicate_detection` | Searches for the same person across all previous sessions using hashed device fingerprints, email, phone, document number, and face embeddings.                        |
| `risk_assessment`     | Aggregates 150+ fraud signals (behavioral, device, network, document forensics) into a composite risk score (0–100). Automatically injected into all workflows.        |
| `check_against_list`  | Matches subject data against your custom allow/block/flag lists. Supports name, email, phone, IP address, device fingerprint, government ID, and geolocation matching. |
| `identity_crossref`   | Cross-references user-submitted data (from the capture form) against data extracted from the document. Detects mismatches in name, date of birth, and address.         |

### Decisioning

| Check           | Description                                                                                                                                                                                      |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `auto_approve`  | Evaluates all prior check results. If everything passed, routes to `approved`. Otherwise routes to `needs_review` or `rejected` based on configuration.                                          |
| `external_gate` | Pauses the workflow and waits for your server to make a decision via API callback. Useful for custom business logic or human-in-the-loop integrations. Configurable timeout (default: 24 hours). |

## Proof of address

`proof_of_address_check` is the one check with a policy of its own. Rather than
a single threshold, it resolves a full **proof-of-address policy** and reports
every finding it made along the way.

### Three-state outcome

Unlike other checks, a proof-of-address result is not just pass or fail:

| `details.outcome` | Check `status` | What it means                                                                                                                                                      |
| ----------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `passed`          | `passed`       | The document is accepted, recent enough, and the name and address match.                                                                                           |
| `review`          | `failed`       | Plausible, but something warrants a human look. `details.action_override` is set to `"review"` and the session routes to your review queue instead of a rejection. |
| `failed`          | `failed`       | A hard failure — wrong document type, out of window, or an address that does not match.                                                                            |

`review` deliberately surfaces as `passed: false`, so integrations that only
read `passed` stay safe: they treat a borderline document as not-yet-approved
rather than silently approving it. Branch on `details.outcome` when you want
the three-way distinction.

### Reasons

Every finding is collected into `details.reasons` — a list of
`{code, severity, message}` objects. Severity is `fail`, `review`, or `info`.
The check takes the worst severity present: any `fail` reason fails it,
otherwise any `review` reason routes it to review, otherwise it passes.

| Code                                   | Typical severity | Raised when                                                                                                                  |
| -------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `unsupported_type`                     | fail             | The document subtype is not in the policy's accepted types.                                                                  |
| `unsupported_country`                  | fail             | The issuing country is excluded by a country override.                                                                       |
| `too_old`                              | fail             | The document is older than its recency window.                                                                               |
| `future_dated`                         | fail             | The document is dated in the future.                                                                                         |
| `date_mismatch`                        | fail             | The submitted date and the date read off the document disagree by more than `date_match_tolerance_days`.                     |
| `name_mismatch` / `name_partial`       | fail / review    | The name score fell below `name_match_pass` (partial) or `name_match_review` (mismatch).                                     |
| `address_mismatch` / `address_partial` | fail / review    | The same banding for the address score. A unit-number-only difference caps at partial rather than failing.                   |
| `address_not_compared`                 | info             | Unguided flow, or no reference address was held — the address was extracted but never scored.                                |
| `name_not_compared`                    | info             | No reference name was held — the name was extracted but never scored.                                                        |
| `po_box`                               | fail             | The address is a PO Box and the policy does not allow one. Set `allow_po_box` to accept them.                                |
| `business_address`                     | review           | The address looks like a business and the policy does not allow one.                                                         |
| `screenshot`                           | review           | Forensics identified a screen capture rather than a document. Downgraded to `info` when the policy sets `allow_screenshots`. |
| `tampering_suspected`                  | review           | Document forensics flagged editing artefacts.                                                                                |
| `unreadable`                           | fail / review    | OCR could not read the document, or read too little of it to judge.                                                          |
| `classifier_disagreement`              | configurable     | The classifier read a different subtype than the one submitted.                                                              |
| `country_mismatch`                     | configurable     | The document was issued in a different country than the ID document.                                                         |
| `review_only_type`                     | review           | The subtype has no official issuer (lease agreement, institution letter, other).                                             |
| `not_independent_source`               | review           | The document shares its issuer with the ID document.                                                                         |
| `missing_document`                     | fail             | No proof-of-address document was uploaded.                                                                                   |

Codes are stable — pivot your UI and metrics off `code`, never off `message`.

### Presets and policy precedence

A policy is resolved from four layers, each overriding the last:

```
built-in defaults  ←  regional preset  ←  organization settings  ←  workflow node config
```

Presets (`us`, `uk`, `eu`, `in`, `mx`, `au`, `ca`, `za`, or `custom`) are
regional starting points — the UK preset accepts council tax and rejects PO
boxes, the India preset accepts mobile phone bills and address-bearing IDs.
Anything a preset omits keeps the built-in default.

Organization settings live in the dashboard under **Settings → Proof of
address**. A workflow node either says "use organization settings" — the
default — or overrides individual fields for that one workflow. Node config
wins wherever it sets a value; everything else falls through to the org.

The policy that actually ran is snapshotted into `details.policy`, so a
decision stays explainable after you change the settings.

### Document types and recency windows

Each accepted subtype carries its own default window — 90 days for a utility
bill or bank statement, 180 for a mobile phone bill or insurance statement,
365 for council tax, tax documents, benefits letters, and mortgage statements.
Override them per type in `max_age_days_by_type`, and per country in
`country_overrides` — for example, utility bills issued in India are commonly
held to 60 days regardless of your preset.

Three subtypes are **review-only**: `lease_agreement`, `institution_letter`,
and `other` have no official issuer, so a pass on one of them still routes to a
human. `id_with_address` is not an independent source — it shares its issuer
with the ID document — and is only accepted when the policy sets
`id_with_address_satisfies`.

### Issuing country

Pass `poa_issuing_country` (ISO 3166-1 alpha-2) with the upload when you know
it; otherwise the check falls back to the country of the ID document on the
session. The issuing country drives address normalisation (postcode formats,
region names versus codes, unit tokens), PO-box detection, and any per-country
window override. When it disagrees with the ID document's country,
`country_mismatch_action` decides whether that is ignored, sent to review, or
failed.

### Name and address matching

Name and address are scored separately, 0.0–1.0, and each is banded against two
thresholds. At or above `*_match_pass` the component passes; at or above
`*_match_review` it is partial and routes to review; below that it fails. The
default bands are 0.85 / 0.60 for names and 0.80 / 0.55 for addresses.

Name matching ignores titles and middle names, folds accents, understands
common nicknames, and accepts any one of several joint holders printed on the
document. Address matching compares components — line 1, city, region,
postcode, unit — and treats a region code and its spelled-out name as
equivalent.

By default the reference is the applicant's declared address; set
`address_source` to `id_document` to compare against the address extracted from
the ID instead. The other source is still compared and reported under
`details.secondary_cross_reference` — a divergence there is a fraud signal
rather than a check failure. In an unguided flow
(`require_declared_address: false`) there is no reference at all: the address is
extracted, no score is produced, and `address_not_compared` is reported.

### Example result

```json
{
  "status": "failed",
  "details": {
    "outcome": "review",
    "passed": false,
    "action_override": "review",
    "reasons": [
      {
        "code": "address_partial",
        "severity": "review",
        "message": "Address matches except for the unit number."
      }
    ],
    "reason_code": "poa_review",
    "document_type": "utility_bill",
    "document_type_canonical": "utility_bill",
    "document_type_detected": "utility_bill",
    "document_date": "2026-07-02",
    "period_start": "2026-06-01",
    "period_end": "2026-06-30",
    "age_days": 56,
    "max_age_days": 90,
    "issuing_country": "GB",
    "address_type": "residential",
    "cross_reference": {
      "overall_score": 0.82,
      "name_score": 0.97,
      "address_score": 0.72,
      "partial": true,
      "partial_reasons": ["unit_mismatch"],
      "component_scores": {
        "first_name": 1.0,
        "last_name": 0.95,
        "line1": 0.78,
        "city": 1.0,
        "postcode": 1.0,
        "unit": 0.0
      }
    },
    "policy": {
      "preset": "uk",
      "address_match_pass": 0.8,
      "address_match_review": 0.55
    }
  }
}
```

`details.issuer`, `details.holder_names`, and `details.extracted_address` are
personal data. They are returned only on PII-bearing surfaces, are subject to
your organization's sensitive-data access window, and are never exposed through
the [MCP server](/integration-guides/mcp-server).

## Auto-injected checks

Some checks are automatically added to every workflow run regardless of your
workflow configuration:

* **`watchlist_screening`** — Required for compliance. Always runs.
* **`risk_assessment`** — Required for fraud scoring. Always runs.
* **`identity_crossref`** — Runs for certain plan tiers to detect form/document
  mismatches.

You cannot remove auto-injected checks, but you can configure how their results
affect the workflow outcome.

## Check results in the session result

After a session completes, each check's result is available in the
`check_results` object:

```json
{
  "check_results": {
    "document_ocr": {
      "status": "passed",
      "details": {
        "fields_extracted": 12,
        "mrz_valid": true,
        "barcode_valid": true
      }
    },
    "face_match": {
      "status": "passed",
      "score": 0.94,
      "liveness_passed": true
    },
    "age_verification": {
      "status": "passed",
      "extracted_age": 28,
      "minimum_age": 18
    },
    "watchlist_screening": {
      "status": "passed",
      "hits": 0
    },
    "risk_assessment": {
      "status": "passed",
      "risk_score": 12,
      "risk_level": "low",
      "triggered_signals": 2
    }
  }
}
```

## Configuring checks in workflows

Each check can be configured with parameters specific to its type. For example:

```json
{
  "type": "policy",
  "policy": "face_match",
  "config": {
    "security_level": "strict"
  },
  "on_pass": "next_step",
  "on_fail": "review_terminal"
}
```

See [Workflows](/core-concepts/workflows) for how to build verification graphs with
checks, conditionals, and routing logic.

## Standalone checks via API

You can also run checks independently of a session through the checks API:

```bash
curl -X POST https://api.withverifa.com/api/v1/checks \
  -H "X-API-Key: vk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "check_type": "sanction",
    "external_ref": "user_abc123",
    "name_first": "Jane",
    "name_last": "Doe",
    "birthdate": "1997-06-15"
  }'
```

This is useful for:

* Running AML screening on existing customers
* Adding phone or email risk checks after onboarding
* Re-screening identities on a schedule

See the API reference for the full list of check endpoints.

## Related

* [Workflows](/core-concepts/workflows) — Orchestrate checks into verification pipelines
* [Screening & Reports](/core-concepts/screening-reports) — AML screening and
  continuous monitoring
* [Fraud Signals](/best-practices/fraud-signals) — The 150+ signals behind risk
  assessment