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

# Verify a profile

> POST /v1/kyc/verify: the request, the three layers of checks, every check id, and how to read passed and flags.

`POST /v1/kyc/verify` returns the verdict for a client profile and the documents behind it. Scope: `kyc:verify`. It does not read files and it costs no document read. `POST /v1/kyc/assess` takes the same body, runs this same verdict, and adds a [risk assessment](/guides/risk-assessment).

## Request

JSON body. Every field is optional, but an empty body verifies nothing.

| Field | Type | Default | Meaning |
| - | - | - | - |
| `reference` | string | none | Your id for the client, 1 to 64 characters of `A-Z a-z 0-9 _ . : -`. With one, the verdict is filed on the case and the answer has `case_id`. A bad value is a 422. |
| `environment` | string | `sandbox` | `sandbox` or `production`. |
| `subject` | string | none | Client name or legal name, up to 255 characters. |
| `values` | object | `{}` | The client profile: field keys to values. See [values](#values). |
| `documents` | array | `[]` | The `documents[]` entries `/extract` returned, unchanged. |
| `kind` | string | none | `individual`, `corporation`, `partnership`, `charitable_org`, `trust`, `estate`. Aliases: `entity`, `business`, `corporate` and `kyb` (corporation), `societe` (partnership), `charity` (charitable\_org), `fiducie` (trust), `succession` (estate). Anything else counts as `individual`. |
| `entity` | boolean | `false` | Coarse flag used when `kind` is null. True means a corporation. |
| `require_documents` | boolean | `true` | Ask for the identity document. See [policy and switches](#policy-and-switches). |
| `screen` | boolean | `true` | Screen every party on the file. See [screening](#screening). |
| `canadian_screening` | boolean or null | null | Extra Canadian screening for a Canadian client. Null lets the workspace policy decide. |
| `extra_checks` | array | `[]` | Checks you ran yourself, added to the verdict. See [your own checks](#your-own-checks). |
| `purpose` | string | `onboarding` | `onboarding` or `periodic_review`. |

### `values`

`values` is a free object. The engine reads the keys below and ignores the others. Send strings. Dates are `YYYY-MM-DD`.

The completeness check counts these keys as present when they are non-empty.

| Kind | Required data points |
| - | - |
| `individual` (26 keys, plus two either-or groups) | `first_name`, `last_name`, `date_of_birth`, `citizenship`, `id_type`, `id_number`, `id_expiry`, `street1`, `city`, `province`, `postal_code`, `country`, `phone`, `email`, `occupation`, `employer_name`, `annual_income`, `net_liquid_assets`, `total_net_worth`, `source_of_funds`, `objective`, `horizon`, `investment_knowledge`, `investment_experience`, `account_type`, `third_party`. Either-or groups: `sin` or `ssn`; any of `pep_foreign`, `pep_domestic`, `pep_hio`, `pep`. |
| `corporation`, `partnership`, `charitable_org`, `estate` (21 keys) | `legal_name`, `business_number`, `incorporation_date`, `entity_address`, `entity_city`, `entity_province`, `entity_postal`, `industry`, `source_of_wealth`, `expected_activity`, `rp_first_name`, `rp_last_name`, `rp_id_type`, `rp_id_number`, `director_names`, `beneficial_owners`, `ownership_control_structure`, `bo_accuracy_measure`, `account_type`, `objective`, `horizon` |
| `trust` (17 keys) | `legal_name`, `entity_address`, `entity_city`, `entity_province`, `entity_postal`, `source_of_wealth`, `expected_activity`, `rp_first_name`, `rp_last_name`, `rp_id_type`, `rp_id_number`, `beneficiaries`, `ownership_control_structure`, `bo_accuracy_measure`, `account_type`, `objective`, `horizon` |

The required list comes from an engine that was first built for North American files. It asks for `province`, `postal_code` and a `sin` or `ssn`, so a Moroccan individual can reach 27 of 28 at most (96 percent) and `sin/ssn` stays in `missing`. 80 percent or more is not flagged. The sample requests here use country `MA`, `id_type` `National ID` and a CIN number such as `BK123456`; `province` and `postal_code` take any text for a Moroccan address, and the Canadian postal format is checked only when `country` is `CA`.

Other keys the checks use:

| Keys | Used by |
| - | - |
| `sin`, `ssn`, `email`, `postal_code`, `province`, `date_of_birth` | `format:` and `age:` checks |
| `id_expiry` | `expiry:recorded:id_expiry` and `expiry_soon:recorded:id_expiry` |
| `bank_number`, `bank_transit`, `bank_routing`, `bank_account` | `bank:` and `format:bank_` checks |
| `other_names` | Extra names to screen (former and maiden names) |
| `country`, `entity_country`, `incorporation_jurisdiction` | Country rules, registry lookup, eID eligibility |
| `ice`, `if_number`, `registration_number`, `tax_id`, `iban` | Entity format checks |
| `pep`, `pep_foreign`, `pep_domestic`, `pep_hio`, `third_party` | Determination checks and [risk](/guides/risk-assessment) |
| `verified_in_person` | A policy that requires eID for clients not met in person |
| `objective`, `horizon`, `investment_knowledge`, `investment_experience`, `uses_leverage`, `industry`, `high_risk_jurisdiction`, `citizenships` | [Risk assessment](/guides/risk-assessment) |

## Response

```json theme={null}
{
  "passed": true,
  "checks": [
    { "id": "format:cin:national_id", "label": "national_id — CIN number is well-formed", "severity": "warning", "passed": true, "detail": "" },
    { "id": "screening", "label": "Sanctions screening — no matches; PEP not list-screened", "severity": "info", "passed": true, "detail": "screened against 23000 sanctions entries. The bundle carries no PEP list, so politically-exposed status rests on the client's declaration, not on a list check." },
    { "id": "completeness", "label": "KYC/KYB data completeness (61%)", "severity": "warning", "passed": false, "detail": "missing 11 required data point(s): street1, city, province, postal_code, phone, email, source_of_funds, account_type" }
  ],
  "critical_failures": [],
  "flags": [
    { "id": "completeness", "label": "KYC/KYB data completeness (61%)", "severity": "warning", "passed": false, "detail": "missing 11 required data point(s): street1, city, province, postal_code, phone, email, source_of_funds, account_type" }
  ],
  "completeness": {
    "required": 28,
    "present": 17,
    "missing": ["street1", "city", "province", "postal_code", "phone", "email", "source_of_funds", "account_type", "third_party", "sin/ssn", "pep_foreign/pep_domestic/pep_hio/pep"],
    "percent": 61
  },
  "policy": { "id": null, "version": 0, "source": "legacy", "regime": "none", "regulator": null, "purpose": "onboarding", "overrides_refused": [] },
  "registry": null,
  "case_id": "00000000-0000-4000-8000-000000000001"
}
```

The `checks` list above is shortened to three entries; a full answer has more. The example is the real output of the engine for fake data (a Moroccan national ID and a payslip for `Test Client`).

| Key | Meaning |
| - | - |
| `passed` | `true` when no check with severity `critical` failed. Warnings never change it. |
| `checks` | Every check that ran, passing or not. |
| `critical_failures` | The checks with `passed: false` and severity `critical`. These block the file. |
| `flags` | Everything that needs a person: failed critical checks and failed warnings. `info` checks are never flags. |
| `completeness` | `required` data points, how many are `present`, the `missing` keys, and the `percent`. |
| `policy` | The workspace policy the verdict ran under. See [policy](#policy-and-switches). |
| `registry` | What Corporations Canada lists for a federal (CBCA) corporation, normalised. Null in every other case. |
| `case_id` | Only with a `reference`. |

Each check has five keys.

| Key | Meaning |
| - | - |
| `id` | Stable id. Match on it. Some ids end with a document label (`expiry:national_id`) or a normalised party name. |
| `label` | A sentence for a person. |
| `severity` | `critical`, `warning` or `info`. |
| `passed` | `true` or `false`. |
| `detail` | The reason when it failed. Empty when it passed. |

Do not match on `label`: it can change. Match on `id`, and treat the part after the first colon as a variable.

## How to read the verdict

| Severity | `passed: false` means | Suggested action |
| - | - | - |
| `critical` | Blocks. `passed` is `false` for the whole file. | Stop. Fix the cause or send to a reviewer. |
| `warning` | A compliance flag. The file can proceed. | Send to a person. It counts one point in the [risk assessment](/guides/risk-assessment). |
| `info` | Kept for the audit trail. `passed` is usually `true`. | Nothing. |

The file passes when no critical check failed. A file with 30 warnings still passes, so look at `flags` as well as `passed`.

## The three layers

```mermaid theme={null}
flowchart TD
    D[documents[] entries] --> L1[Layer 1: per document]
    L1 --> L2[Layer 2: across documents and values]
    V[values] --> L2
    L2 --> L3[Layer 3: screening, determinations, completeness]
    P[Workspace policy] --> L3
    X[extra_checks] --> L3
    L3 --> R[passed, checks, flags, completeness]
```

1. Per document: the same checks `/extract` already returned for each file (type, readable, expiry, adult, MRZ, recency, provenance, specimen).
2. Cross-document and profile: the name, date of birth and address agree across documents and with `values`; the formats of numbers; the bank details; the required identity document is among the uploads.
3. File level: sanctions and PEP screening, beneficial ownership for entities, registry checks, the policy's own determinations, then completeness.

Verification is deterministic: the same input gives the same verdict, except for screening (which depends on the list loaded) and the date, which comes from the server clock.

## Checks

Ids are listed with the part after the first colon replaced by `*`. "Document" means the label of the step or the `doc_type` hint (for example `passport`, or `Government photo ID` when a `step_key` is sent).

### Per document

| Id | Severity | Passes when | If it fails |
| - | - | - | - |
| `doctype:*` | critical | The reader's `doc_type` is one the step accepts. Only runs when the step or hint gates. | Detail names what the document is and what the step takes. Ask for the right document. |
| `legible:*` | critical if every expected field is missing, else warning | The key fields of the type were read ([list](/guides/document-types#fields-by-document-type)). | Detail lists `could not read: ...`. Ask for a clearer scan. |
| `expiry:*` | critical | `id_expiry` is today or later. | `expired YYYY-MM-DD`. Ask for a valid ID. |
| `expiry_soon:*` | warning | The ID does not expire within 90 days (policy `id_expiry_days`). | Failed only when it does. Not a block. |
| `adult:*` | critical | The holder of an ID document is 18 or older. | `holder is N`. |
| `mrz:*` | critical | The MRZ check digits are valid (ICAO 9303, passport 2 lines of 44, ID card 3 lines of 30). | Possible tampering or a bad read. |
| `mrz:unreadable:*` | warning | Only added when a passport MRZ was read but could not be parsed. | The digits were not verified. |
| `mrz:dob:*`, `mrz:docnum:*` | warning | The MRZ birth date and document number match the printed fields. | The MRZ says one thing, the card another. |
| `recency:*` | critical on a proof-of-address step or for a `utility_bill` or `proof_of_address` document, else warning | The older of the printed date and the file date is within 90 days (policy `document_recency_days`). | `effective date X is N days old`. Only for `utility_bill`, `proof_of_address`, `bank_statement`. |
| `recency:future:*` | critical | The printed date is not in the future. | |
| `authenticity:*` | warning | The file date is not more than 14 days older than the printed date (policy `backdate_days`). | Possible edit or backdating. |
| `provenance:editor:*` | warning | The file does not name an image or PDF editor (Photoshop, GIMP, Canva, Sejda, Smallpdf and similar) as its software. | Not proof of fraud. People redact files. Send to a reviewer. |
| `provenance:revisions:*`, `provenance:modified:*`, `provenance:future:*` | warning | Issued once, not modified after creation, file dates plausible. | Same. |
| `provenance:reprint:*` | info | A browser or phone print is noted and passes. | |
| `authenticity:specimen:*` | critical | The document is not a specimen, sample or template. Triggered by words such as SPECIMEN or SAMPLE, a placeholder holder (`John Doe`, `Customer`, `Specimen Test Card`), a template address (`123 Any St`), or a specimen number (`P123456AA`, a run of one digit, `123456789`). | Use a real document. |
| `format:cin:*`, `format:licence:*`, `consistency:licence:*`, `format:iban:*`, `format:national_id` | warning | Number formats by country. See [format checks](/guides/document-types#format-checks-by-country). | Usually a misread. |

### Across documents and profile

| Id | Severity | Passes when | If it fails |
| - | - | - | - |
| `required:photo_id` | critical | A readable government photo ID is among the documents (passport, national ID, driver's licence, PR card, residence permit). Only when `require_documents` is true. | `no readable government photo ID among the uploads`. |
| `required:incorporation`, `required:partnership_agreement`, `required:trust_deed`, `required:estate_authority` | critical | The document that establishes an entity of that kind is present. | Detail names what is missing. |
| `consistency:name`, `consistency:name_unreadable` | critical | Every proof of address, bank statement or entity paper belongs to the applicant or entity. Unreadable holder names also block. | A third party's bill. |
| `consistency:poa_name_id`, `consistency:profile_name_id`, `consistency:id_name` | critical | The proof of address is in the ID's name, the name in `values` is the person on the ID, two IDs are the same person. | |
| `consistency:dob` | critical | The date of birth agrees across documents. | |
| `consistency:address`, `consistency:profile_address_docs`, `consistency:postal_province` | warning | The address agrees across documents and with `values`; the postal code matches the province. | |
| `expiry:recorded:id_expiry` | critical | The `id_expiry` in `values` is not past. | |
| `expiry_soon:recorded:id_expiry` | warning | It does not expire within 90 days. | |
| `format:sin` | critical | `sin` passes the Luhn check. | |
| `format:sin_series`, `format:sin_temporary` | warning | A SIN does not start with 0 or 8, and a 9 is flagged as temporary-resident series. | |
| `format:ssn`, `format:email`, `format:postal` | warning | Structurally valid SSN, email, Canadian postal code. | |
| `age:majority` | warning | An 18-year-old is flagged in a province where the age of majority is 19. | |
| `bank:split` | warning | Bank details are in the right fields (a 9-digit routing number is not in `bank_number`). | |
| `format:bank_institution`, `format:bank_transit`, `format:bank_routing` | warning | 3-digit institution, 5-digit transit, ABA checksum for 9-digit routing. | |
| `consistency:bank_account` | warning | One account number across statements. | |

### File level

| Id | Severity | Passes when | If it fails |
| - | - | - | - |
| `screening` | info or critical | See [screening](#screening). | |
| `screening:sanctions:*` | critical | The party has no sanctions match. | `matches sanctions entry 'X' (source, N%) ... blocked pending manual review`. |
| `screening:pep:*` | warning | The party has no PEP match. | Enhanced due diligence. |
| `screening:review:*` | warning | No adverse or regulatory record (disciplinary order, cease-trade order, adverse media). | Review before approving. |
| `screening:canchek`, `screening:canchek_skipped`, `screening:canchek_unavailable` | info / info / warning | Canadian screening ran / was wanted but the workspace has no account / the service did not answer. | Screened against the bundle only. Screen again before approving. |
| `completeness` | warning | At least 80% of the required data points are present. | Detail lists the first 8 missing keys. |
| `bo:none_recorded`, `bo:names_only`, `bo:addresses`, `bo:senior_officer`, `bo:unconfirmed_risk` | warning | Beneficial ownership is recorded as people with addresses, a most senior managing officer is named, and ownership is confirmed. Entities only. | |
| `discrepancy:*` | warning or info | Federal corporations: beneficial owners agree with Corporations Canada, and a discrepancy was reported. | |
| `registry:found`, `registry:active`, `registry:directors` | warning, critical, info or warning | A federal corporation is found, active, and its directors agree with the registry. Only for CBCA corporations when the policy enables the lookup. | |
| `registry:*`, `registry:manual:*` | warning | Other jurisdictions: names the registry a reviewer must consult. | |
| `format:ice`, `format:if_number`, `format:rc`, `required:ma_identifiers`, `format:business_number`, `format:ein`, `format:registration_number`, `format:tax_id`, `required:*_identifiers` | warning | Company identifiers are well-formed and recorded. | |
| `determination:pep_hio`, `determination:third_party` | warning | The policy asks the file to record those answers and they are present. | |
| `policy:eid_non_face_to_face` | critical | See [eID](/guides/eid#meeting-a-policy-eid-requirement). | |
| `document:*` | warning or critical | The policy enforces document slots and the slot is present. | |
| `policy:periodic_review`, `policy:periodic_review_unanchored`, `policy:override_refused:*`, `policy:met_in_person_declared` | info / warning / info / info | See [policy and switches](#policy-and-switches). | |
| `partner:*` | as you sent | Your own checks, see below. | |

## Screening

Every party on the file is screened by default (`screen: true`).

| File | Parties screened |
| - | - |
| Individual | The holder (first, middle, last name) and each name in `other_names`. |
| Entity | `legal_name`, the signing officer (`rp_*`), every name in `director_names` and `beneficial_owners`, and `other_names`. Duplicates are removed. |

A sanctions hit is critical and blocks. A PEP hit is a warning. A clean result produces one `screening` check that says what the party was screened against:

| `screening` check | Severity | Meaning |
| - | - | - |
| `Sanctions screening — no matches; PEP not list-screened` | info, passed | Screened against the sanctions bundle (OFAC, the UN consolidated list and OSFI). That bundle has no PEP list, so PEP status rests on the client's declaration. |
| `Sanctions/PEP screening — no matches` | info, passed | Screened against a source that includes PEP tables (the Canadian screening). |
| `Sanctions/PEP screening — sample list only` | warning | The environment has only a 30-name sample list. It is not a compliant screen. You should not see this in production. |
| `Sanctions/PEP screening — NOT PERFORMED` | critical | No list was loaded. |
| `Sanctions/PEP screening — coverage unknown` | critical | The list used could not be determined. Treat the party as unscreened. |

With `canadian_screening` true (or the policy asking for it) and a Canadian client (`country` is `CA`, `CAN` or `Canada`), the parties the bundle found nothing on are also screened by the eID provider's Canadian AML and PEP tables, when the workspace has an account. The `screening:canchek` check says how many parties were screened.

## Policy and switches

Each call runs under your workspace's KYC policy for the client kind (`kyc` for a person, `kyb` for any entity) and the environment. The answer's `policy` says which one.

| `policy.source` | Meaning |
| - | - |
| `tenant` | A policy saved in the console (Settings, KYC policy), at `version`. |
| `preset` | No saved policy. The preset of the regulatory regime of your workspace (FINTRAC for Canada, BSA/CIP for the US, Law 43-05 for Morocco). |
| `legacy` | No saved policy, no regime: the light default. Nothing locked. |
| `fallback` | A saved policy that no longer validates. The call ran under the preset or default instead. |

A request switch can add checks or be stricter. It cannot turn off an item the policy locks. A refused switch is not an error: the call runs with the stricter rule and the refusal is recorded.

```json theme={null}
"policy": {
  "source": "preset",
  "regime": "fintrac",
  "purpose": "onboarding",
  "overrides_refused": [
    { "field": "screen", "requested": false, "enforced": true, "locked_item": "sanctions_screening" }
  ]
}
```

(Illustrative: the shape of `overrides_refused` is real, the values are an example.) A refusal also adds an info check `policy:override_refused:screen`.

| Switch | Honoured when |
| - | - |
| `require_documents: false` | The policy does not lock `id_verification`, or `purpose` is `periodic_review`. |
| `screen: false` | The policy does not lock `sanctions_screening`. |
| `canadian_screening: false` | The policy does not lock the Canadian screening. |

What a policy can change:

| Setting | Default | Effect |
| - | - | - |
| `thresholds.document_recency_days` | 90 | The proof-of-address window (1 to 365). |
| `thresholds.backdate_days` | 14 | Gap between the file date and the printed date that is flagged. |
| `thresholds.id_expiry_days` | 90 | `expiry_soon` window (0 to 365). |
| `risk_bands.low_max_points`, `medium_max_points` | 1 and 3 | Stricter only. See [risk assessment](/guides/risk-assessment#compliance-risk). |
| `beneficial_ownership_threshold_pct` | 25 | Percent of ownership that must be named. Up to 25. |
| `enabled_checks` | all families | Families that appear in the verdict. Your own `extra_checks` and eID results are never filtered. |
| `document_slot_enforcement` | `off` | `warning` or `critical` adds `document:*` checks for the required slots. |
| `eid_required_non_face_to_face` | false | A person not met in person must have a passed eID check. |

Policy values are edited in the console, not through the API.

### Periodic review

`purpose: "periodic_review"` is for a client already onboarded. It does not re-verify identity (PCMLTFR s.155(1)): the identity documents, the eID requirement and the slot checks are not applied. Screening, the determinations and every other lock still run.

* With a passed onboarding on file at Sahl for the same `reference` and `environment`, the review adds an info check `policy:periodic_review`.
* With none, or without a `reference`, the review is honoured but adds a warning `policy:periodic_review_unanchored`: identity was skipped on your word alone.

## Your own checks

`extra_checks` lets you fold a check you ran yourself, such as a duplicate client or a blocklist in your database, into the verdict so it can block it.

```json theme={null}
"extra_checks": [
  { "id": "internal:duplicate_client", "label": "No duplicate client in our database", "severity": "critical", "passed": false, "detail": "matches client 8812" }
]
```

Each needs `id`, `label`, `severity` (`critical`, `warning` or `info`) and `passed`; `detail` is optional. An id that starts with `eid:` or `policy:` is Sahl's alone: it comes back as `partner:eid:...` or `partner:policy:...`, shown and still able to block, but it never meets the policy's eID requirement.

## Corporations Canada registry

For a `corporation` the advisor says is federally incorporated (CBCA), and when the policy enables it (default on), Sahl looks the company up. `registry` then holds the normalised record, so you can pre-fill from it, and the `registry:` checks compare it with your data. In every other case `registry` is null.

## Filing

With a `reference` the verdict is filed on the case for (workspace, environment, reference). Documents whose `document_id` came from `/extract` are linked to it. A status a person set (`approved`, `refused`) is never undone by a new verdict. The `kyc.case_verified` [webhook](/guides/webhooks) fires after the commit.

## Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://app.sahlfinancial.com/api/v1/kyc/verify \
    -H "Authorization: Bearer $SAHL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "reference": "client-0001",
      "environment": "sandbox",
      "subject": "Test Client",
      "kind": "individual",
      "values": {
        "first_name": "Test", "last_name": "Client", "date_of_birth": "1988-04-12",
        "citizenship": "MA", "country": "MA",
        "id_type": "National ID", "id_number": "BK123456", "id_expiry": "2030-05-01"
      },
      "documents": [{
        "filename": "cin-test.jpg", "doc_type": "national_id", "step_hint": "national_id",
        "step_key": null, "mapped": 9, "notes": [],
        "fields": {
          "first_name": "Test", "last_name": "Client", "date_of_birth": "1988-04-12",
          "id_type": "National ID", "id_number": "BK123456", "id_expiry": "2030-05-01",
          "citizenship": "MA", "id_country": "MA", "document_holder_name": "Test Client"
        }
      }]
    }'
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch("https://app.sahlfinancial.com/api/v1/kyc/verify", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SAHL_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      reference: "client-0001",
      environment: "sandbox",
      subject: "Test Client",
      kind: "individual",
      values: { first_name: "Test", last_name: "Client", date_of_birth: "1988-04-12" },
      documents: extracted.documents, // from /extract, unchanged
    }),
  });
  const verdict = await res.json();
  if (!verdict.passed) {
    for (const c of verdict.critical_failures) console.log(c.id, c.detail);
  }
  ```

  ```python Python theme={null}
  import os, requests

  res = requests.post(
      "https://app.sahlfinancial.com/api/v1/kyc/verify",
      headers={"Authorization": f"Bearer {os.environ['SAHL_API_KEY']}"},
      json={
          "reference": "client-0001",
          "environment": "sandbox",
          "subject": "Test Client",
          "kind": "individual",
          "values": {"first_name": "Test", "last_name": "Client", "date_of_birth": "1988-04-12"},
          "documents": extracted["documents"],  # from /extract, unchanged
      },
      timeout=60,
  )
  res.raise_for_status()
  verdict = res.json()
  for c in verdict["critical_failures"]:
      print(c["id"], c["detail"])
  ```
</CodeGroup>

### A blocked file

An expired national ID gives `passed: false` and two critical failures, one from the document and one from the profile:

```json theme={null}
{
  "passed": false,
  "critical_failures": [
    { "id": "expiry:national_id", "label": "national_id — not expired", "severity": "critical", "passed": false, "detail": "expired 2024-01-01" },
    { "id": "expiry:recorded:id_expiry", "label": "The identity document on file is not expired", "severity": "critical", "passed": false, "detail": "expired 2024-01-01" }
  ]
}
```


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