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

# Sub-client verification

> From registering a sub-client to one that can open an account, and everything that can stop it.

A sub-client exists the moment you create it and can do nothing until it is verified. This is the only lifecycle that gates every other one — no account is opened and no money moves until it reaches a terminal approval.

## What advances it

| From                   | To          | What moves it                                              |
| ---------------------- | ----------- | ---------------------------------------------------------- |
| `CREATED`              | `VERIFYING` | The checks start on their own once the record is complete  |
| `VERIFYING`            | `REVIEW`    | Kira compliance is reviewing the case                      |
| `VERIFYING` / `REVIEW` | `VERIFIED`  | Approved — the sub-client can now begin opening an account |
| `VERIFYING` / `REVIEW` | `REJECTED`  | Declined on the data you supplied                          |

You do not drive these transitions. What you control is the data: submit it complete, and fix it with an update when something is wrong.

<Note>
  **Branch on `status`.** It is the sub-client's lifecycle, and the field the table above describes. `verification_status` reports the result of the identity check itself — useful to display, not the one to gate a flow on.
</Note>

## What announces it

| Event                           | When                                                                      |
| ------------------------------- | ------------------------------------------------------------------------- |
| `user.created`                  | The record exists                                                         |
| `user.updated`                  | The record changed                                                        |
| `user.status_changed`           | **Every** transition above, carrying `previous_status` and `new_status`   |
| `user.verification.accepted`    | Approved                                                                  |
| `user.verification.failed`      | Terminally declined, with the reasons in `data.reasons[]`                 |
| `user.liveness_completed`       | A liveness check finished, with `data.result` and which person it was for |
| `user.document.download.failed` | A document you supplied by URL could not be fetched                       |

<Warning>
  **A rejection tells you it happened, not what to fix.** `user.verification.failed` carries `data.reasons[]` and no read of the sub-client returns it, so capture it on arrival — but expect a short label, not a compliance explanation. What you act on is `missing_fields`, and an RFI when something specific is needed.
</Warning>

## Where an RFI interrupts it

A request for information is raised when something more is needed. It is not a failure — it is the check asking rather than declining.

| Event               | What it means for you                                                   |
| ------------------- | ----------------------------------------------------------------------- |
| `rfi.raised`        | Something needs answering. Whatever it blocks stays blocked             |
| `rfi.item_returned` | One answer was not accepted and is being asked for again                |
| `rfi.resolved`      | Satisfied — what it was blocking is unblocked                           |
| `rfi.not_resolved`  | Closed unsatisfied, with `resolution_reason` of `expired` or `rejected` |

The item is the unit: an RFI can have several, each answered separately, and one returned item does not undo the others. Answering one sends you nothing back.

<Note>
  **A document rejected on its own does not move `status`.** When a check cannot finish until something is resupplied, `verification_status` reads `needs_action` and `status` stays where it was. Read `verification_status` to know that something is waiting on you — see [User values](/reference/users/values#verification_status).
</Note>

A URL with no event filter receives `rfi.*` along with everything else. A URL with a filter receives it only if the filter names these events. [Webhooks overview](/webhooks/overview) has the subscription rules.

**Design for this from the start.** An RFI is answered by your customer, not by your engineers, so the fields it names have to reach whoever can supply them.

## What to build

1. **Listen for `user.status_changed`** and treat it as the source of truth for where a sub-client is.
2. **Capture `data.reasons[]` from `user.verification.failed`** on arrival. You cannot go back for it.
3. **Surface `missing_fields`** from the sub-client to whoever can act on it.
4. **Have a path for an RFI** before you have one.

<Card title="Account opening" icon="arrow-right" href="/lifecycles/account-opening">
  Turning an approved sub-client into an account that can receive money.
</Card>
