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

# Creating users — KYC/KYB required fields

> Which fields POST /v1/users needs to create and verify an end client, by user category, with the conditional rules and country restrictions that apply.

A "user" is one of your end clients — the person or company that owns a virtual account. Creating one with **`POST /v1/users`** kicks off identity verification (KYC for individuals, KYB for businesses). The field set you must send depends on two values in the payload: **`type`** (`individual` or `business`) and **`address_country`** (ISO **alpha-3**, e.g. `USA`, `MEX`). Together they put every user into one of four categories.

<Note>
  **Two things matter and they are not the same.** A bare payload (just the [required-to-create](#minimum-to-create) fields) returns `201` — but the user will not be product-eligible: verification does not trigger and the user stays `CREATED` until the field set is complete. A **complete** user triggers verification automatically — but approval is a **manual** step, not instant; per Kira it can take **up to 24 hours**, and the sandbox can route the user to **manual review** (`status: REVIEW`, `verification_status: in_review`) at any point in that window, which a Kira operator resolves. Don't build against a "verifies in seconds" assumption — see [Known limitations](/guides/known-limitations#identity--user-lifecycle). The authoritative, per-user list of what is still outstanding is the **`missing_fields` map** on `GET /v1/users/{id}` (`product_code → [field tokens]`) and on the `user.created` webhook. Treat that map as the source of truth; the tables below tell you what to send up front so you never hit it. See [Known limitations](/guides/known-limitations) and [For AI agents](/guides/for-ai-agents).
</Note>

<Note>
  **Checking KYB status via the API.** A client can check where a business stands without contacting Kira: `GET /v1/users/{id}` returns `identifying_information[]` listing the documents actually submitted — each entry's `documents[]` carries the real `file_name` and `uploaded_at` timestamp Kira received — plus the `missing_fields` map (what's still outstanding per product) and `status` / `verification_status` (the review state). This does **not** mean everything you submitted round-trips: scalar questionnaire fields (`account_purpose`, `source_of_funds`, `expected_monthly_volume`, `expected_transaction_count`, `employment_status`) are accepted on write but not returned by `GET` — persist those on your own side. See [Known limitations](/guides/known-limitations#identity--user-lifecycle).
</Note>

## The four user categories

`type` and `address_country` select the required field set:

| `type`       | `address_country` | Category                       |
| ------------ | ----------------- | ------------------------------ |
| `individual` | `USA`             | USA individual (KYC)           |
| `individual` | anything else     | International individual (KYC) |
| `business`   | `USA`             | USA business (KYB)             |
| `business`   | anything else     | International business (KYB)   |

International categories drop the US-only fields (`ssn`, `ein`) and add a few of their own (`immigration_status`, `international_entity_type`, `additional_info`). The per-category fields are below.

<Warning>
  **Country codes are ISO-3 on users** (`USA`, `MEX`) — not ISO-2. Recipients use ISO-2; do not mix them up. `address_country` drives category selection, so an ISO-2 value here misroutes the whole payload.
</Warning>

## Minimum to create

`POST /v1/users` only **enforces** a handful of fields. Everything else is enforced later, by verification and by per-product eligibility.

| Category   | Required by the endpoint                   |
| ---------- | ------------------------------------------ |
| Individual | `type`, `first_name`, `last_name`, `email` |
| Business   | `type`, `business_legal_name`, `email`     |

<Warning>
  **Always send `source_of_funds`.** It's a required KYC/KYB questionnaire field, and it behaves differently by user type. For an **individual**, live-tested: omitting it does not by itself block verification from triggering, and does not cause an auto-reject — a fully complete individual missing only `source_of_funds` still triggered verification and landed in manual `REVIEW`. For a **business**, live-tested: it's a **blocking** KYB field — a fully complete business missing only `source_of_funds` did **not** trigger verification and stayed `CREATED` with `source_of_funds` in `missing_fields`. Send it on every user regardless of type; see [Known limitations](/guides/known-limitations#identity--user-lifecycle).
</Warning>

## Required fields by category

To get a user **verified and product-eligible**, send the full set for its category. Fill scalar gaps later with **`PUT /v1/users/{id}`** and re-submit uploads for document gaps; the product flips `eligible: true` once `missing_fields` is empty.

### Shared identity fields

Sent by every user, individual or business:

| Field                                                                      | Accepted values                                                                               |
| -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `email` · `phone`                                                          | Email string · phone string                                                                   |
| `address_street` · `address_city` · `address_zip_code` · `address_country` | Free text; `address_country` = ISO alpha-3                                                    |
| `account_purpose`                                                          | One of the documented enum values (the set differs for individuals vs businesses — see below) |
| `source_of_funds`                                                          | Enum — differs for individuals vs businesses (see below)                                      |
| `expected_monthly_volume` · `expected_transaction_count`                   | Bucketed enums — differ for individuals vs businesses (see below)                             |
| `pep_status`                                                               | Boolean                                                                                       |

<Warning>
  **`expected_monthly_volume` and `expected_transaction_count` are required for a virtual account** — a sponsor-bank rule. They are **not** auto-filled. If either is absent when you create the user, it shows up in the `missing_fields` map on `GET /v1/users/{id}`, and **`POST /v1/virtual-accounts` returns `422` with `code: "missing_required_fields"`** and a top-level `missing_fields` array naming exactly which are missing — the account is not created in a silently-ineligible state. Send both on `POST /v1/users` (the enum sets differ for individuals vs businesses — see the tables below) or backfill with `PUT /v1/users/{id}` before creating the VA.
</Warning>

<AccordionGroup>
  <Accordion title="USA individual (type: individual, address_country: USA)">
    **Identity**

    | Field                                                    | Notes                                                                                                                                                           |
    | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `first_name` · `last_name`                               | Free text                                                                                                                                                       |
    | `birth_date`                                             | ISO 8601, e.g. `1990-01-15`                                                                                                                                     |
    | `nationality`                                            | ISO alpha-3                                                                                                                                                     |
    | `country_of_birth`                                       | ISO alpha-3 (e.g. `USA`, `MEX`). Optional to create and verify a user, but **required by some banking partners** to open a virtual account — see the note below |
    | `address_state`                                          | Free text (US state)                                                                                                                                            |
    | `document_type` · `document_number` · `document_country` | e.g. `passport`, `drivers_license`, `national_id`; `document_country` = ISO alpha-3                                                                             |
    | `ssn`                                                    | Sent inside `identifying_information`: `[{ "type": "ssn", "number": "…" }]` — required unless `immigration_status` is a non-US citizen status                   |
    | `identifying_information: front`                         | Document photo                                                                                                                                                  |
    | `identifying_information: back`                          | Document photo — required **unless** `document_type` is `passport`                                                                                              |

    <Note>
      **Some banking partners additionally require `country_of_birth` and an identity-document expiry date (`identifying_information[].expiration`).** These two behave differently from the required fields described above, so send them whenever you have them:

      * They **are** reported in `missing_fields`, under the entry for the product that needs them — so you can see the gap before you create anything. They do **not** appear under products that don't require them, and they do **not** block verification: a user missing only these still reaches `VERIFIED`.
      * They do **not** produce a `422`. If you create the account anyway, `POST /v1/virtual-accounts` returns `201`, the account stays in `activating`, and its status reason names the unmet condition — read it from `GET /v1/virtual-accounts/{id}`. Backfill with `PUT /v1/users/{id}` and create the virtual account again.
      * A user already onboarded at the partner is unaffected — an existing banking relationship is reused rather than re-checked.
    </Note>

    **Questionnaire**

    | Field                        | Accepted values                                                                                                                                                                                                                                                                                                                                                                  |
    | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `employment_status`          | `employed` · `self_employed` · `unemployed` · `retired` · `student`                                                                                                                                                                                                                                                                                                              |
    | `occupation`                 | Free text — required **when** `employment_status` = `employed`                                                                                                                                                                                                                                                                                                                   |
    | `current_employer`           | Free text — required when `employment_status` = `employed`                                                                                                                                                                                                                                                                                                                       |
    | `income_source`              | Free text — required **unless** `employment_status` = `employed`                                                                                                                                                                                                                                                                                                                 |
    | `account_purpose`            | `receive_payments` · `manage_professional_income` · `make_payments` · `manage_personal_funds` · `investment_trading` · `charitable_donations` · `investment_purposes` · `operating_a_company` · `payments_to_friends_or_family_abroad` · `personal_or_living_expenses` · `purchase_goods_and_services` · `protect_wealth` · `receive_salary` · `receive_payment_for_freelancing` |
    | `source_of_funds`            | `salary` · `self_employment_income` · `investment_proceeds` · `savings_inheritance_gift` · `gifts` · `inheritance` · `investments_loans` · `pension_retirement` · `sale_of_assets_real_estate` · `savings` · `someone_elses_funds`                                                                                                                                               |
    | `expected_monthly_volume`    | `less_than_10000` · `10000_to_49999` · `50000_to_199999` · `200000_to_999999` · `1000000_or_more`                                                                                                                                                                                                                                                                                |
    | `expected_transaction_count` | `1_to_10` · `11_to_50` · `51_to_200` · `more_than_200`                                                                                                                                                                                                                                                                                                                           |
  </Accordion>

  <Accordion title="International individual (type: individual, address_country ≠ USA)">
    All **USA individual** fields apply, with these differences:

    | Field                                     | Change                                                                                                                                                              |
    | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `ssn`                                     | Not required                                                                                                                                                        |
    | `immigration_status`                      | Required. Exactly one of: `U.S. Citizen` · `Permanent U.S. Resident` · `Lawful Permanent Resident of U.S.` · `Non-Permanent U.S. Resident` · `Non-Resident of U.S.` |
    | `additional_info.has_us_bank_account`     | `Yes` / `No`                                                                                                                                                        |
    | `additional_info.has_denied_bank_account` | `Yes` / `No`                                                                                                                                                        |
  </Accordion>

  <Accordion title="USA business (type: business, address_country: USA)">
    **Entity identity**

    | Field                                                                                        | Accepted values                                                                                                                                                                                                                                                                                                                                               |
    | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `business_legal_name`                                                                        | Free text                                                                                                                                                                                                                                                                                                                                                     |
    | `business_type`                                                                              | `limited_liability_company` · `corporation` · `general_partnership` · `limited_liability_partnership` · `sole_proprietor` · `non_profit` · `trust` · `government_organization` · `publicly_traded_company` (aliases also accepted: `llc`, `sole_prop`, `nonprofit`, `government`, `publicly_traded`, `llp`, `partnership`, `sa_de_cv`, `ltda`, `cooperative`) |
    | `business_description`                                                                       | Free text                                                                                                                                                                                                                                                                                                                                                     |
    | `business_industry`                                                                          | Array of NAICS-style codes, e.g. `telecommunications`, `real_estate`                                                                                                                                                                                                                                                                                          |
    | `formation_date`                                                                             | ISO 8601                                                                                                                                                                                                                                                                                                                                                      |
    | `formation_state` · `formation_country`                                                      | `formation_country` = ISO alpha-3                                                                                                                                                                                                                                                                                                                             |
    | `address_street` · `address_city` · `address_state` · `address_zip_code` · `address_country` | Free text; country = ISO alpha-3                                                                                                                                                                                                                                                                                                                              |
    | `ein`                                                                                        | Sent inside `identifying_information`: `[{ "type": "ein", "number": "…" }]`                                                                                                                                                                                                                                                                                   |

    **Company documents — all four groups must be covered.** Verification requires a company document for each of the four groups below; a business with only the formation document stalls with the other three groups in `missing_fields`. One document can cover several groups at once — articles of incorporation / bylaws (`file_bylaws`) count for legal presence, company details, **and** control structure. The tokens below are the representative doc types `missing_fields` reports for each group; the listed alternatives satisfy the same group.

    | Group (token in `missing_fields`)                            | Accepted documents                                                                                                                                       |
    | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `identifying_information: file_business_formation`           | **Legal presence** — certificate of incorporation / business formation document, or articles of incorporation / bylaws                                   |
    | `identifying_information: file_certificate_of_good_standing` | **Company details** — certificate of good standing, incumbency certificate / corporate resolution, EIN letter / state registration, or articles / bylaws |
    | `identifying_information: file_portfolio_statement`          | **Ownership structure** — shareholder registry, ownership document, or information/portfolio statement                                                   |
    | `identifying_information: file_board_minutes`                | **Control structure** — directors registry / board minutes, or articles / bylaws                                                                         |

    **Additional compliance documents.** Some banking partners require supplementary documentation beyond the four groups above. Submit these as top-level `documents[]` entries (`{ "type": "…", "file": … }`) — they are not covered by `missing_fields`, so provide them proactively when known to be required:

    | `documents[].type`     | Document                                     |
    | ---------------------- | -------------------------------------------- |
    | `corporate_resolution` | Corporate resolution authorizing the account |
    | `financial_statements` | Financial statements                         |
    | `tax_id_number`        | Tax identification number document           |

    **Questionnaire**

    | Field                        | Accepted values                                                                                                                                                                                                                           |
    | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `account_purpose`            | `charitable_donations` · `ecommerce_retail_payments` · `investment_purposes` · `purchase_goods_and_services` · `receive_payments_for_goods_and_services` · `internal_treasury` · `third_party_money_transmission` · `operating_a_company` |
    | `source_of_funds`            | `business_loans` · `inter_company_funds` · `investment_proceeds` · `owners_capital` · `sales_of_goods_and_services` · `tax_refund` · `third_party_funds` · `treasury_reserves` · `company_funds` · `investments_loans`                    |
    | `high_risk_industries`       | `Yes` · `No` — always required. `Yes` triggers industry EDD (extra documents: proof of address, source-of-wealth, directors registry)                                                                                                     |
    | `is_nbfi_vasp`               | `Yes` · `No` — always required (is the business a non-bank financial institution or virtual-asset service provider). `Yes` triggers industry EDD                                                                                          |
    | `business_legal_history`     | `Yes` · `No` — always required                                                                                                                                                                                                            |
    | `pep_status`                 | Boolean                                                                                                                                                                                                                                   |
    | `expected_monthly_volume`    | `less_than_50000` · `50000_to_100000` · `100000_to_500000` · `500000_to_1000000` · `1000000_to_5000000` · `5000000_to_10000000` · `more_than_10000000`                                                                                    |
    | `expected_transaction_count` | `less_than_10` · `10_to_25` · `26_to_50` · `51_to_100` · `101_to_500` · `more_than_500`                                                                                                                                                   |

    **Associated persons (UBOs / directors)** — `associated_persons` is an array; each object carries the person's own identity:

    | Field                               | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
    | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `has_ownership`                     | Boolean. A person is recognized as a beneficial owner by `has_ownership: true`, **not** by `role` — **set it on every associated person** (`true` for owners, `false` for non-owner directors/signers). If omitted, the person is not recognized as an owner and surfaces as `associated_persons:has_ownership` in `missing_fields` (advisory — it does not block verification or VA creation).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
    | `ownership_percentage`              | Number 0–100 — the owner's percentage stake. It does **not** by itself mark the person as an owner; pair it with `has_ownership: true`. A business needs **at least one** associated person with `has_ownership: true` **and** `ownership_percentage >= 5` — otherwise `associated_persons:beneficial_owner` surfaces in `missing_fields` and verification does not trigger (see [conditional rules](#conditional-field-rules)).                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
    | `first_name` · `last_name`          | Free text                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
    | `birth_date`                        | ISO 8601                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
    | `email` · `nationality`             | Email string · ISO alpha-3                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
    | `gender`                            | `male` · `female` · `other`. Accepted on create and update. Common shorthands (`M`, `F`, `Male`) are normalized to the canonical value, so a person sent as `M` reads back as `male` — echoing a `GET` response into a `PUT` is safe, but the stored value is the normalized one. A value that maps to none of the three is rejected with `400`. Required by some sponsor banks, whose identity check runs on each associated person; surfaced as `associated_persons:gender` in `missing_fields` when absent.                                                                                                                                                                                                                                                                                                                                                                                     |
    | `phone_number`                      | Phone string, passed through as supplied — not format-validated. Send E.164 (e.g. `+13055551200`): it is what the sponsor banks' identity checks expect, and anything else is forwarded to them unchanged. Accepted on create and update; surfaced as `associated_persons:phone_number` in `missing_fields` when absent.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
    | `pep_status`                        | Boolean — whether this person is, or has been, a politically exposed person. Accepted on create and update; a non-boolean is rejected with `400`. Some sponsor banks ask it of **every** beneficial owner and signer and refuse a person who has no answer on file, so it is surfaced as `associated_persons:pep_status` in `missing_fields` when absent — and for those banks' products it is **blocking**: the product reports `eligible: false` until every associated person has an answer, with no exception for a business that is already `VERIFIED` (unlike `has_ownership`, which is advisory, and `beneficial_owner`, which is grandfathered once verified). Send it per person: Kira does not answer it on anyone's behalf, and omitting the field is **not** the same as declaring `false` — `false` ("not a politically exposed person") is a real answer and clears the requirement. |
    | `document_type` · `document_number` | Free text                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
    | `ssn`                               | Required unless the person's `nationality` is non-US                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
    | `identifying_information: front`    | Document photo                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
    | `identifying_information: back`     | Document photo — required unless `document_type` = `passport`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
  </Accordion>

  <Accordion title="International business (type: business, address_country ≠ USA)">
    All **USA business** fields apply, with these differences:

    | Field                                     | Change                                                                     |
    | ----------------------------------------- | -------------------------------------------------------------------------- |
    | `ein`                                     | Not required                                                               |
    | `address_state`                           | **Still required** (state / province / department of the business address) |
    | `document_number` · `document_country`    | Required — free text; `document_country` = ISO alpha-3                     |
    | `international_entity_type`               | Required — free text, e.g. `Sociedad de Responsabilidad Limitada`          |
    | `additional_info.has_us_bank_account`     | `Yes` / `No`                                                               |
    | `additional_info.has_denied_bank_account` | `Yes` / `No`                                                               |
    | `associated_persons: document_country`    | Required for international UBOs — ISO alpha-3                              |
  </Accordion>
</AccordionGroup>

<Note>
  **The USA virtual-account products require different fields.** They do **not** share an identical KYC set. Rather than hard-coding any one list, read the per-product `missing_fields` map on `GET /v1/users/{id}` and fill what it reports for the product you are creating. See [For AI agents](/guides/for-ai-agents) and the `missing_fields` example in [Webhooks](/guides/webhooks).
</Note>

### A product can require more than verification does

`missing_fields` is keyed by product, and a product may ask for fields that are **not** required to verify the user. This is deliberate: some sponsor banks require data the identity check itself does not.

Two consequences worth designing for:

* **A user can reach `VERIFIED` while a product still reports missing fields.** Verification completing is not a promise that every product is satisfied. Check `missing_fields` for the product you intend to open, not just the user's status.
* **A gap on one product never blocks another.** Fields required only by product A are invisible to product B's `missing_fields`, to its `eligible` flag, and to whether verification can start at all.

So the reliable sequence is: create the user → let verification complete → **re-read `GET /v1/users/{id}` and check `missing_fields[<your product>]`** → fill what it reports → create the virtual account. Creating the account while that product still lists gaps is the common cause of an account that is accepted and then fails during activation at the bank.

<Note>
  Some products require documents that Kira does **not** generate for you — notably `file_fatca`, the signed W-9 or W-8BEN tax self-certification. Its federal tax classification cannot be derived from the data you send us, so it must come from the account holder. If a product needs it, `missing_fields` will say so.
</Note>

## Conditional field rules

`missing_fields` tokens encode these conditions; send the field only when the condition holds.

| Token                                                   | Rule                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ssn:unless_immigration_status:non_us_citizen`          | Send `ssn` for everyone **except** non-US citizens                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `occupation:when_employment_status:employed`            | Send `occupation` (and `current_employer`) only when `employment_status` = `employed`                                                                                                                                                                                                                                                                                                                                                                                    |
| `income_source` (unless employed)                       | Send `income_source` for everyone **except** `employed`                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `identifying_information:back:unless_doc_type:passport` | Send the ID **back** photo for every document type **except** `passport`                                                                                                                                                                                                                                                                                                                                                                                                 |
| `identifying_information:file_proof_of_address`         | Send proof of address when an [EDD trigger](#country-restrictions) applies                                                                                                                                                                                                                                                                                                                                                                                               |
| `identifying_information:expiration`                    | Every identity document you send carries a `number` must also carry an `expiration`. Reported when one is missing an expiry — one document having a date does not cover another that lacks one                                                                                                                                                                                                                                                                           |
| `tos_accepted_version`                                  | Send the terms-of-service version the account holder accepted. Kira stamps the acceptance timestamp on receipt; some products require the acceptance to be on file                                                                                                                                                                                                                                                                                                       |
| `associated_persons:has_ownership`                      | Set `has_ownership` (`true`/`false`) on **every** associated person — a beneficial owner is recognized by this flag, not by `role`. If omitted it is surfaced in `missing_fields` (advisory; does not block verification or VA creation)                                                                                                                                                                                                                                 |
| `associated_persons:beneficial_owner`                   | A business (KYB) must have **at least one** associated person who is a beneficial owner — `has_ownership: true` **and** `ownership_percentage >= 5`. If none qualifies, this token is surfaced in `missing_fields` and verification does not trigger (**blocking** for a new business — the provider's beneficiaries step requires a UBO). Businesses already `VERIFIED` are grandfathered: it is surfaced advisory-only and never re-blocks their next virtual account. |

## Biometric / liveness verification report

Some banking partners require a **biometric verification report** — a verification-result document
proving a face match against the government ID — on a user's record. Kira produces this itself from
your identity provider; you do **not** upload a finished report. Two ways it is produced:

* **Send a selfie (face match).** Include a `selfie` in `identifying_information[].documents[]`
  alongside the ID (the same document shape as `front`/`back`). Kira submits the selfie and the ID to
  the identity provider, which face-matches the selfie against the ID portrait; on approval the
  verification report is attached to the user automatically. No interactive session is required.

  ```json theme={null}
  {
    "identifying_information": [
      {
        "type": "passport",
        "issuing_country": "USA",
        "number": "P1234567",
        "documents": [
          { "type": "front", "file": "<base64-or-https-url>" },
          { "type": "selfie", "file": "<base64-or-https-url>" }
        ]
      }
    ]
  }
  ```

* **Have the user complete a liveness session.** For a higher-assurance check that includes liveness
  (not possible from an uploaded image), call `POST /v1/users/{user_id}/liveness-link` to get hosted
  link(s), then relay each to the right end user. The request body is **optional** — omit it, or supply
  a `redirect` (see below) to send the user back to your own page afterward. Calling it again for a
  subject whose link is still active returns that same link rather than issuing a new one. The response
  is `{ "links": [...] }` with **one** entry for an individual user and **one per beneficial owner** for
  a business; each entry carries a `subject` (`"user"` or `"ubo"`), a `liveness_link`, an `expires_at`
  (the link is valid for **7 days**), and — for `ubo` subjects — a `person_reference_id` and `name` so
  you can route the link to the right person:

  ```json theme={null}
  {
    "links": [
      {
        "subject": "user",
        "liveness_link": "https://…",
        "expires_at": "<ISO timestamp>"
      }
    ]
  }
  ```

  When a user finishes, a [`user.liveness_completed`](/guides/webhooks#event-catalog) webhook fires
  with `data.result` of `"approved"` or `"rejected"` (and, for a beneficial owner, the matching
  `data.person_reference_id`), and on approval Kira attaches the generated report to the user
  automatically. A liveness result never changes the user's main KYC `status`.

  You can request the links **during onboarding** — a business's UBO liveness links are issuable once
  its KYB has been **submitted** (the business need not be fully verified first, since liveness feeds
  the verification). The endpoint returns `422` when there's no verification to attach the check to
  (an individual with no verification started yet, or a business whose KYB hasn't been submitted or has
  no beneficial owners on record), or when the identity provider doesn't support liveness for the subject.

* **Optional: send the user back to your page.** Include a `redirect` object to land the user on your
  own page after the session instead of the default completion screen:

  ```json theme={null}
  {
    "redirect": {
      "success_url": "https://app.example.com/kyc/done",
      "reject_url": "https://app.example.com/kyc/failed"
    }
  }
  ```

  `success_url` is **required** whenever you send a `redirect` (`reject_url` alone is rejected), and
  both URLs must be **HTTPS**. Send a field only if you mean it — an empty value is a `422`, not
  "omitted". This is **opt-in per account**: each URL's host must be on your redirect allowlist
  (subdomains of an allowlisted domain are accepted). A non-HTTPS or non-allowlisted URL — or any
  redirect at all before your allowlist is set up — is rejected with `422` and no link is minted. The redirect is bound to the link when it's issued, so a repeat call for a
  still-active subject keeps the original redirect; supply a **different** `redirect` to mint a fresh
  link with the new one.

  <Note>
    **The allowlist is configured by the Kira team — there is no API or dashboard for it.** To enable
    redirects, [contact Kira support](mailto:support@kirafin.ai) with the exact domains you want
    allowlisted (for example `app.example.com`), and for which environments (sandbox, production, or
    both). Until the team has registered them, every request carrying a `redirect` returns `422`. Send
    the same request again once you're told the allowlist is live — no code change is needed. Reach out
    the same way to add or remove a domain later.
  </Note>

  <Warning>
    The redirect landing is a **UX convenience, not the verification result**. A user can reach your
    `success_url` before Kira has finalized the check. Always treat the
    [`user.liveness_completed`](/guides/webhooks#event-catalog) webhook as the source of truth for the
    outcome — never the page the user landed on.
  </Warning>

## Country restrictions

All codes are ISO 3166-1 **alpha-3**. Rules apply to `address_country` unless noted.

### Blocked

Users with `address_country` in this list are ineligible for any virtual-account product:

`AFG` `BLR` `MMR` `CAF` `CIV` `CUB` `COD` `IRN` `IRQ` `LBR` `LBY` `PRK` `RUS` `SAU` `SOM` `SDN` `SYR` `UKR` `YEM` `VEN` `ZWE`

### Enhanced due diligence (EDD)

When an EDD trigger applies, add a proof-of-address document (`identifying_information: file_proof_of_address`).

**Individuals (53 countries)** — triggered when `address_country` is in:

`DZA` `AGO` `ARM` `AZE` `BHS` `BHR` `BGD` `BMU` `BOL` `VGB` `BRN` `BFA` `KHM` `CMR` `CXR` `CCK` `CRI` `CYP` `EGY` `SLV` `GIB` `GRC` `GTM` `GGY` `HTI` `HND` `IDN` `IMN` `JEY` `JOR` `KAZ` `KEN` `KWT` `LBN` `LIE` `MYS` `MCO` `MNG` `MOZ` `NIC` `NGA` `OMN` `PNG` `PRY` `PHL` `ZAF` `LKA` `THA` `TTO` `TUN` `UGA` `UZB` `VNM`

**Businesses (51 countries)** — the same list **minus `GTM` and `CCK`**. For businesses, proof of address is required if **any one** of these is true:

<Warning>
  `address_country` is in the business EDD list **OR** `formation_country` is in the business EDD list **OR** the company was formed within the **last 180 days**.
</Warning>

## Example payloads

Complete `POST /v1/users` bodies for each category. Adapt the values. Photos and files go in `identifying_information[].documents[]` as `{ "type": "front", "file": … }` — note the key is **`file`** (not `content`), and government-ID photos nest under their parent ID entry rather than sitting loose in the array. Each `file` is a base64 data URI (`data:image/jpeg;base64,…`, JPEG/PNG/PDF) or — on version `2026-04-14`+ — an `https://` URL. See [Create a user — API reference](/api-reference/users/create-a-user) for the full document schema. **The whole request body is capped at 10 MB**, and inline base64 counts against it (≈ +33% overhead) — so for large or multiple files, pass `file` as an `https://` URL (fetched server-side, up to 30 MB per file, and off the body budget) rather than base64; see [Request size & large document uploads](/guides/known-limitations#request-size-and-large-document-uploads).

<Warning>
  **Document files must be real, retrievable bytes.** Verification downloads each `file` at submission time — a placeholder or unreachable `https://` URL (or junk base64) means the documents cannot be uploaded to the verification provider, and the user stays `CREATED` even though `verification_triggered` was `true`. Replace the `<base64>` placeholders below with actual file content before posting.
</Warning>

<CodeGroup>
  ```json USA individual theme={null}
  {
    "type": "individual",
    "first_name": "Ana",
    "last_name": "García",
    "birth_date": "1990-04-22",
    "email": "ana@example.com",
    "phone": "+15551234567",
    "nationality": "USA",
    "address_street": "123 Main St",
    "address_city": "Austin",
    "address_state": "TX",
    "address_zip_code": "78701",
    "address_country": "USA",
    "document_type": "drivers_license",
    "document_number": "DL-987654321",
    "document_country": "USA",
    "employment_status": "employed",
    "occupation": "Software Engineer",
    "current_employer": "Acme Corp",
    "account_purpose": "receive_payments",
    "source_of_funds": "salary",
    "expected_monthly_volume": "10000_to_49999",
    "expected_transaction_count": "11_to_50",
    "pep_status": false,
    "identifying_information": [
      { "type": "ssn", "issuing_country": "USA", "number": "XXX-XX-XXXX" },
      {
        "type": "drivers_license",
        "issuing_country": "USA",
        "number": "DL-987654321",
        "documents": [
          { "type": "front", "file": "data:image/jpeg;base64,<base64>" },
          { "type": "back",  "file": "data:image/jpeg;base64,<base64>" }
        ]
      }
    ]
  }
  ```

  ```json International individual theme={null}
  {
    "type": "individual",
    "first_name": "Carlos",
    "last_name": "Mendez",
    "birth_date": "1988-11-03",
    "email": "carlos@example.com",
    "phone": "+521234567890",
    "nationality": "MEX",
    "address_street": "Av. Reforma 456",
    "address_city": "Mexico City",
    "address_state": "CDMX",
    "address_zip_code": "06600",
    "address_country": "MEX",
    "document_type": "passport",
    "document_number": "G12345678",
    "document_country": "MEX",
    "immigration_status": "Non-Resident of U.S.",
    "employment_status": "self_employed",
    "income_source": "Freelance consulting",
    "account_purpose": "manage_professional_income",
    "source_of_funds": "self_employment_income",
    "expected_monthly_volume": "50000_to_199999",
    "expected_transaction_count": "11_to_50",
    "pep_status": false,
    "additional_info": {
      "has_us_bank_account": "No",
      "has_denied_bank_account": "No"
    },
    "identifying_information": [
      {
        "type": "passport",
        "issuing_country": "MEX",
        "number": "G12345678",
        "documents": [
          { "type": "front", "file": "data:image/jpeg;base64,<base64>" }
        ]
      }
    ]
  }
  ```

  ```json USA business theme={null}
  {
    "type": "business",
    "business_legal_name": "Acme Payments LLC",
    "business_type": "limited_liability_company",
    "business_description": "B2B SaaS payment processing platform",
    "business_industry": ["telecommunications"],
    "formation_date": "2019-03-15",
    "formation_state": "DE",
    "formation_country": "USA",
    "email": "ops@acme.com",
    "phone": "+15551234567",
    "address_street": "100 Commerce Blvd",
    "address_city": "Austin",
    "address_state": "TX",
    "address_zip_code": "78701",
    "address_country": "USA",
    "account_purpose": "receive_payments_for_goods_and_services",
    "source_of_funds": "sales_of_goods_and_services",
    "high_risk_industries": "No",
    "is_nbfi_vasp": "No",
    "business_legal_history": "No",
    "pep_status": false,
    "expected_monthly_volume": "100000_to_500000",
    "expected_transaction_count": "101_to_500",
    "identifying_information": [
      { "type": "ein", "issuing_country": "USA", "number": "XX-XXXXXXX" },
      {
        "type": "business_formation",
        "issuing_country": "USA",
        "documents": [
          { "type": "file_business_formation", "file": "data:application/pdf;base64,<base64>" },
          { "type": "file_certificate_of_good_standing", "file": "data:application/pdf;base64,<base64>" },
          { "type": "file_portfolio_statement", "file": "data:application/pdf;base64,<base64>" },
          { "type": "file_board_minutes", "file": "data:application/pdf;base64,<base64>" }
        ]
      }
    ],
    "associated_persons": [
      {
        "has_ownership": true,
        "ownership_percentage": 100,
        "first_name": "Jane",
        "last_name": "Smith",
        "birth_date": "1985-07-20",
        "email": "jane@acme.com",
        "nationality": "USA",
        "document_type": "passport",
        "document_number": "US123456789",
        "identifying_information": [
          { "type": "ssn", "issuing_country": "USA", "number": "XXX-XX-XXXX" },
          {
            "type": "passport",
            "issuing_country": "USA",
            "number": "US123456789",
            "documents": [
              { "type": "front", "file": "data:image/jpeg;base64,<base64>" }
            ]
          }
        ]
      }
    ]
  }
  ```

  ```json International business theme={null}
  {
    "type": "business",
    "business_legal_name": "Global Trade SRL",
    "business_type": "corporation",
    "business_description": "Commercial real estate transactions across Latin America",
    "business_industry": ["real_estate"],
    "formation_date": "2021-06-01",
    "formation_country": "MEX",
    "email": "admin@globaltrade.mx",
    "phone": "+521234567890",
    "address_street": "Paseo de la Reforma 222",
    "address_city": "Mexico City",
    "address_zip_code": "06600",
    "address_country": "MEX",
    "document_number": "RFC-GLO210601ABC",
    "document_country": "MEX",
    "international_entity_type": "Sociedad de Responsabilidad Limitada",
    "account_purpose": "purchase_goods_and_services",
    "source_of_funds": "sales_of_goods_and_services",
    "high_risk_industries": "No",
    "is_nbfi_vasp": "No",
    "business_legal_history": "No",
    "pep_status": false,
    "expected_monthly_volume": "500000_to_1000000",
    "expected_transaction_count": "26_to_50",
    "additional_info": {
      "has_us_bank_account": "No",
      "has_denied_bank_account": "No"
    },
    "identifying_information": [
      {
        "type": "business_formation",
        "issuing_country": "MEX",
        "documents": [
          { "type": "file_business_formation", "file": "data:application/pdf;base64,<base64>" },
          { "type": "file_certificate_of_good_standing", "file": "data:application/pdf;base64,<base64>" },
          { "type": "file_portfolio_statement", "file": "data:application/pdf;base64,<base64>" },
          { "type": "file_board_minutes", "file": "data:application/pdf;base64,<base64>" }
        ]
      }
    ],
    "associated_persons": [
      {
        "has_ownership": true,
        "ownership_percentage": 100,
        "first_name": "Luis",
        "last_name": "Torres",
        "birth_date": "1979-02-14",
        "email": "luis@globaltrade.mx",
        "nationality": "MEX",
        "document_type": "passport",
        "document_number": "G98765432",
        "document_country": "MEX",
        "identifying_information": [
          {
            "type": "passport",
            "issuing_country": "MEX",
            "number": "G98765432",
            "documents": [
              { "type": "front", "file": "data:image/jpeg;base64,<base64>" }
            ]
          }
        ]
      }
    ]
  }
  ```
</CodeGroup>

## Troubleshooting a KYB that stalls

A business submission has two ways to stop that look nothing alike. Diagnose them in this order.

### 1. It never triggered — `verification_triggered: false`

The payload is incomplete for the category. The `missing_fields` map lists exactly what is outstanding per product; supply it with `PUT /v1/users/{id}` and verification triggers on its own. Iterate until `missing_fields` disappears from the response.

<Note>
  **`eligible: false` on every product is expected while this is happening.** Product eligibility requires the user to reach `VERIFIED`, so it stays `false` throughout submission and review. The signal that your payload is complete is **`missing_fields` disappearing** together with `verification_triggered: true` — not `eligible`.
</Note>

### 2. It triggered but sits in `REVIEW` — a document was declined

Verification reached the provider and one of your documents was rejected as *retryable*. This is **not** a terminal `REJECTED`, so **no `user.verification.failed` webhook fires** and `GET /v1/users/{id}` shows the document as uploaded with no indication that it was declined.

<Warning>
  **Per-document rejection reasons are not exposed through the API today.** A KYB can sit in `REVIEW` because one uploaded file was declined, and there is currently no endpoint, field, or webhook that tells you which file or why. `identifying_information[].documents[]` describes the upload — `type`, `file_name`, `content_type`, `uploaded_at`, and the download state for URL-sourced files — but carries **no review outcome**, so a declined document is indistinguishable from an accepted one. If a KYB lingers in `REVIEW`, contact your Kira representative with the user id and ask for the per-document review result. This is a known gap.
</Warning>

The most common cause is a **name mismatch**:

<Warning>
  **`business_legal_name` must match the company name printed on the documents you upload**, character for character. A submission of `"ACME HOLDINGS S.A.P.I. DE C.V."` against a registration certificate whose registered name reads `"ACME HOLDINGS"` is declined as a company-data mismatch even though both name the same entity — the legal-form suffix is often recorded as a *trade* name, not the registered one. Read the registered name off the document and send exactly that. `doing_business_as` is the right place for the commercial variant.
</Warning>

Other frequent causes: a document that does not cover the group it was sent as (see the four groups above), an unreadable scan, or an expired certificate.

### 3. Beneficiaries are stuck

Each associated person is verified as its own applicant. Two failure modes are easy to hit and are covered in [Known limitations](/guides/known-limitations#identity--user-lifecycle): a partial `PUT` silently drops `nationality` and `document_number`, and omitting `document_type` submits the person as an ID card, which then demands a back image your passport-only upload never provides.

## Bank intent (optional)

`capabilities.requested_banks` is an optional array recording which bank(s) this subclient plans to use. It's intent, not authorization — sending a bank slug here does not grant your account access to it; authorization for a bank is a separate, account-level configuration. Omit `capabilities` entirely to use the standard verification path.

`PUT /v1/users/{id}` also accepts `capabilities.requested_banks` and `verification_mode`. On a `PUT`, `requested_banks` **merges** with whatever is already stored — sending `["austin_capital_trust"]` for a subclient that already declared `["portage"]` results in both being recorded; a `PUT` can only add a bank, never remove one by omission. Any request body key this endpoint doesn't define is rejected with `400` instead of being silently dropped.

For most banks, declaring one this way just records the intent — no other effect. A small number of banks additionally require moving the subclient's existing applicant to a stricter verification level; for those, a `PUT` that declares the bank triggers that move on the applicant already on file (no new applicant is created). When `verification_mode` is `verification_link`, a successful move returns a fresh `verification_link`; a subclient mid-review, or otherwise not yet eligible to move, returns `200` with a descriptive `verification_link_error` instead — never a `4xx`/`5xx` for a legitimately deferred state. Re-sending the same `PUT` is also the retry channel: a deferred move is re-attempted on every request that declares the bank, and once the subclient is already at the target level a repeat request simply returns a fresh `verification_link` (useful when a previous link has expired). A returned link is not lost with the response that carried it — subsequent `GET /v1/users/{id}` calls keep returning it as `verification_link` while `verification_mode` is `verification_link`.

`verification_link_error` is freeform, human-readable text, not a stable enum — don't branch your integration on its wording. `verification_link_error_severity` is the machine-readable field for that: it accompanies `verification_link_error` on the `PUT` response with either `deferred` (nothing is wrong — the move is waiting on something expected, like the applicant's current review finishing, or was correctly skipped by a business rule) or `failed` (the move hit a genuine problem).

## Related

* [Create a user — API reference](/api-reference/users/create-a-user) — the endpoint, full schema, and live "Try it".
* [For AI agents](/guides/for-ai-agents) — `eligible_products` / `missing_fields`, conditional tokens, getting a user to `VERIFIED` in sandbox.
* [Webhooks](/guides/webhooks) — the `user.created` payload with the per-product `missing_fields` map.
* [Known limitations](/guides/known-limitations) — sandbox auto-verification behavior and `status` vs `verification_status`.
