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

# Document types and fields

> The document types the reader returns, the fields it reads, which fields each check needs, and the step keys.

The reader is a general document reader. It looks at the file, names the document type, and returns whichever of a fixed set of field keys it can read. There is no separate schema per document type in the code. What differs per type is which fields the checks expect, and which fields are withheld. This page lists both.

## Document types the reader returns

`documents[].doc_type` is one of these codes, or another lowercase string, or null.

| `doc_type` | What it is | Checks specific to it |
| - | - | - |
| `national_id` | National or provincial ID card (Moroccan CIN, provincial photo ID) | legible, expiry, adult, MRZ if printed, CIN format (Morocco) |
| `passport` | Passport | legible, expiry, adult, MRZ |
| `drivers_license` | Driver's licence | legible, expiry, adult, licence number format by province or state |
| `pr_card` | Canadian permanent resident card | legible, expiry, adult, MRZ if printed |
| `residence_permit` | Any other residence permit (carte de séjour, US green card) | legible, expiry, adult, MRZ if printed |
| `utility_bill` | Bill from an electricity, gas, water, oil, internet, cable or phone provider | legible, recency |
| `proof_of_address` | Any other document showing an address (lease, insurance, government letter) | legible, recency. The `proof_of_address` step does not accept it. |
| `bank_statement` | Account statement with transactions and balances | legible, recency |
| `void_cheque` | A single cheque. Never proof of address. | legible. A VOID marking is ignored on a `banking` or `rib` step, and for any document read as `void_cheque`. |
| `bank_letter` | Letter from the bank confirming the account | none |
| `invoice` | Invoice from a supplier that is not a utility | none |
| `payslip` | Payslip | none. Fields only. |
| `articles_of_incorporation` | Articles, letters patent | legible |
| `business_registration` | Business number letter or registry extract | legible |
| `bylaws` | Bylaws, operating or partnership agreement | none |
| `beneficial_ownership` | Declaration naming owners of 25% or more | none |
| `directors_register` | Register of directors and officers | none |
| `board_resolution` | Board resolution, for example opening the account | none |
| `financial_statements` | Balance sheet and income statement | none |
| `trust_deed` | Deed or declaration of trust | none |
| `beneficiary_list` | List of trust beneficiaries | none |
| `other` | Anything the reader is not sure of | none |

Every document also gets the checks that do not depend on its type: the `doctype:` gate when a step or hint gates (below), `expiry:` when an `id_expiry` was read, an MRZ check when MRZ lines were read, `provenance:` file checks and the `authenticity:specimen:` check. The [verification guide](/guides/verification#checks) explains each.

The reader is told to answer `other` when unsure, because a confident wrong label gets a client's document refused. Free-form legal documents (the last group) are accepted alongside `other` by the steps that expect them. Free text from the reader is mapped to these codes by stem, so "Driver License", "PR Card" and "Void Cheque" arrive as `drivers_license`, `pr_card` and `void_cheque`.

Two address documents are accepted as proof of address: `utility_bill` and `bank_statement`. A cheque is never one.

## Field keys

Every key the reader may return, grouped. A key not in this list is dropped.

| Group | Keys |
| - | - |
| Identity | `first_name`, `middle_name`, `last_name`, `date_of_birth`, `sex`, `citizenship`, `id_type`, `id_number`, `id_expiry`, `id_country`, `id_province`, `sin`, `ssn` |
| MRZ | `mrz_line1`, `mrz_line2`, `mrz_line3`. Passport: 2 lines of 44. ID card: 3 lines of 30. Verbatim, with `<` fillers. |
| Residential address | `street1`, `city`, `province`, `postal_code`, `country`, `phone`, `email` |
| Employment | `occupation`, `employer_name`, `type_of_business`, `employer_address`, `employer_city`, `employer_province`, `employer_postal`, `business_phone` |
| Financials | `annual_income`, `net_liquid_assets`, `net_fixed_assets`, `total_net_worth` |
| Banking | `bank_name`, `bank_number` (Canadian institution, 3 digits), `bank_transit` (Canadian branch, 5 digits), `bank_routing` (US ABA, 9 digits), `bank_account`, `iban` |
| Entity | `legal_name`, `business_number`, `registration_number`, `ice`, `if_number`, `tax_id`, `incorporation_date`, `incorporation_jurisdiction`, `entity_address`, `entity_city`, `entity_province`, `entity_postal`, `entity_phone` |
| Control persons | `rp_first_name`, `rp_last_name`, `rp_occupation`, `director_names`, `beneficial_owners` (comma-separated full names) |
| About the document | `document_date` (the date printed on it), `document_holder_name` (whose document it is), `specimen_markings` (comma-separated signs of a sample, kept on the document entry only) |

`id_type` is one of `Passport`, `Driver License`, `National ID`, `Residence Permit` (a PR card and a green card are `Residence Permit`).

The reader is told which side of a document is the client's. On an invoice, a bill or a statement the client is the recipient (the "bill to" party), never the issuer. `document_holder_name`, `legal_name`, address and contact keys describe the recipient.

## Fields by document type

What the reader is asked to look for on each type, and what the checks need. The reader returns only what is printed and legible. Nothing below is guaranteed to come back.

| `doc_type` | Fields to expect | Fields the check needs (`legible:`) |
| - | - | - |
| `passport` | `first_name`, `last_name`, `date_of_birth`, `sex`, `citizenship`, `id_type`, `id_number`, `id_expiry`, `id_country`, `mrz_line1`, `mrz_line2`, `document_holder_name` | `first_name`, `last_name`, `date_of_birth`, `id_number` |
| `national_id` | Names, `date_of_birth`, `sex`, `id_number`, `id_expiry`, `id_country`, address fields on the back, `mrz_line1` to `mrz_line3` | `first_name`, `last_name`, `id_number` |
| `drivers_license` | Names, `date_of_birth`, `id_number`, `id_expiry`, `id_province`, `id_country`, address fields, `sex` | `first_name`, `last_name`, `id_number` |
| `pr_card`, `residence_permit` | Names, `date_of_birth`, `id_number`, `id_expiry`, `citizenship`, MRZ lines | `first_name`, `last_name`, `id_number` |
| `utility_bill`, `proof_of_address` | `street1`, `city`, `province`, `postal_code`, `country`, `document_holder_name`, `document_date` | `street1`, `city`, `postal_code`, `document_holder_name` |
| `bank_statement` | `bank_name`, `document_holder_name`, `document_date`, `bank_number`, `bank_transit`, `bank_routing`, `bank_account`, `iban`, address fields | `bank_name`, `document_holder_name` |
| `void_cheque` | `bank_number`, `bank_transit`, `bank_account` (Canada) or `bank_routing`, `bank_account` (US) | `bank_account` |
| `bank_letter` | `bank_name`, `bank_number` and `bank_transit` (Canada) or `bank_routing` (US), `bank_account`, `document_holder_name` | none |
| `invoice` | Recipient: `document_holder_name`, address fields. Issuer fields are withheld, bank fields withheld. | none |
| `payslip` | `document_holder_name`, `first_name`, `last_name`, `employer_name`, `occupation`, `document_date`, employer address fields. `annual_income` only if the payslip states it. | none |
| `articles_of_incorporation` | `legal_name`, `incorporation_date`, `incorporation_jurisdiction`, `registration_number`, `director_names` | `legal_name` |
| `business_registration` | `business_number`, `legal_name`, `registration_number`, `ice`, `if_number`, `tax_id`, entity address fields | `business_number`, `legal_name` |
| Other entity papers | `legal_name`, `director_names`, `beneficial_owners` where the document lists them | none |

If every expected field of a type is missing, `legible:` is critical. If only some are missing it is a warning that names them (`could not read: id_number`).

<Note>
  Money amounts are read only from a document that states them. A payslip shows pay for a period. The reader is told never to estimate, so `annual_income` is absent unless it is printed. Do not rely on a payslip alone for annual income. See the [walkthrough](/guides/walkthrough).
</Note>

## Which fields reach `fields`

| Rule | Effect |
| - | - |
| Bank keys from a non-bank document | Removed. Only `void_cheque`, `bank_statement`, `bank_letter` and `rib` keep `bank_*`. For an unnamed type, a `doc_type` hint that names a bank document (cheque, bank statement, bank letter, relevé, RIB) keeps them. |
| Issuer identity on an `invoice` | Removed from the merge unless the recipient name and the legal name agree. |
| `specimen_markings` | Document entry only. |
| Same key in several files | The first non-empty value wins in the merged `fields`. Each document keeps its own value in `documents[].fields`. |

## Gating: `step_key` and `doc_type`

`step_key` makes the call check that the document is one the step expects. The `doctype:` check is critical: if the reader says "utility bill" and the step takes a photo ID, the answer says `this is a utility bill; this step takes a passport, a national ID card, a driver's licence...` and the entry fails.

If you send no `step_key`, the `doc_type` text is also used as a gate when it contains one of these phrases (lowercase, spaces matter): `photo id`, `passport`, `pr card`, `proof of address`, `bank`, `articles of incorporation`, `business registration`. For example `doc_type=passport` accepts a passport, national ID, driver's licence or residence permit. `doc_type=bank_statement` contains `bank` and accepts a bank statement, utility bill or void cheque. `doc_type=payslip` matches nothing and is not gated. Use `step_key` when you want a precise gate.

A known step with no type gate (`banking`, `rib` and a few others) accepts any document.

### Step keys

| `step_key` | Field class | Accepted `doc_type` values |
| - | - | - |
| `photo_id` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `proof_of_address` | address | `bank_statement`, `utility_bill` |
| `banking` | entity | any (no type gate) |
| `cin` | identity | `drivers_license`, `national_id`, `passport`, `residence_permit` |
| `rib` | entity | any (no type gate) |
| `bulletin_paie` | entity | `other`, `payslip` |
| `articles_of_incorporation` | entity | `articles_of_incorporation` |
| `business_registration` | entity | `business_registration` |
| `bylaws` | entity | `bylaws`, `other` |
| `beneficial_ownership` | entity | `beneficial_ownership`, `other` |
| `directors_register` | entity | `directors_register`, `other` |
| `director_photo_id` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `business_address` | address | `bank_statement`, `business_registration`, `proof_of_address`, `utility_bill` |
| `board_resolution` | entity | `board_resolution`, `other` |
| `financial_statements` | entity | `financial_statements`, `other` |
| `partnership_agreement` | entity | `other`, `partnership_agreement` |
| `trust_deed` | entity | `other`, `trust_deed` |
| `trustee_photo_id` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `settlor_id` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `beneficiary_list` | entity | `beneficiary_list`, `other` |
| `trust_address` | address | `bank_statement`, `proof_of_address`, `utility_bill` |
| `trust_tax_id` | entity | any (no type gate) |
| `trust_financial_statements` | entity | `financial_statements`, `other` |
| `estate_authority` | entity | `estate_authority`, `other` |
| `liquidator_id` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `ein_letter` | entity | `business_registration`, `other` |
| `good_standing` | entity | `business_registration`, `other` |
| `registre_commerce` | entity | `business_registration`, `other` |
| `statuts` | entity | `articles_of_incorporation`, `bylaws`, `other` |
| `manager_cin` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `attestation_ice` | entity | `business_registration`, `other` |
| `attestation_if` | entity | `business_registration`, `other` |
| `cni_ci` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `iban_ci` | entity | any (no type gate) |
| `cni_sn` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `iban_sn` | entity | any (no type gate) |
| `cin_tn` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `iban_tn` | entity | any (no type gate) |
| `nid_eg` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `iban_eg` | entity | any (no type gate) |
| `nid_sa` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `iban_sa` | entity | any (no type gate) |
| `rccm_extract` | entity | `business_registration`, `other` |
| `dfe_ci` | entity | `business_registration`, `other` |
| `manager_id` | identity | `drivers_license`, `national_id`, `passport`, `pr_card`, `residence_permit` |
| `ninea_sn` | entity | `business_registration`, `other` |
| `rne_extract_tn` | entity | `business_registration`, `other` |
| `mf_tn` | entity | `business_registration`, `other` |
| `beneficial_ownership_tn` | entity | `beneficial_ownership`, `other` |
| `cr_eg` | entity | `business_registration`, `other` |
| `tax_card_eg` | entity | `business_registration`, `other` |
| `cr_sa` | entity | `business_registration`, `other` |
| `vat_sa` | entity | `business_registration`, `other` |
| `proof_of_address_abroad` | address | `bank_statement`, `proof_of_address`, `utility_bill` |

A step marked `address` is a proof-of-address step. A `utility_bill`, `proof_of_address` or `bank_statement` read there has a blocking recency check (critical, 90 days by default). A `utility_bill` or `proof_of_address` is also critical on any step. A `bank_statement` on any other step is a warning.

## Format checks by country

Warnings only. A malformed number is more often a misread than a forgery, so none of these blocks a file.

| Document | Check | Applies to |
| - | - | - |
| Moroccan CIN | `format:cin:` 1 or 2 letters then 5 to 7 digits | `national_id` with country `MA` |
| Driver's licence | `format:licence:` per province or state; a jurisdiction with no rule is reported as not checked | `drivers_license` |
| Ontario licence | `consistency:licence:` the number agrees with last name and date of birth | `drivers_license`, ON |
| National ID | `format:national_id` style checks for Côte d'Ivoire, Senegal, Tunisia, Egypt, Saudi Arabia (Egypt and Saudi numbers also encode birth date, sex or citizen status) | `national_id`, `residence_permit` |
| IBAN | `format:iban:` | any document that returns an `iban` |
| Company numbers | `format:ice`, `format:if_number`, `format:rc` (Morocco); `format:business_number` (Canada); `format:ein` (US); `format:registration_number`, `format:tax_id` (CI, SN, TN, EG, SA) | `/verify` on an entity profile |


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