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

# KYC Overview

> What Etherfuse collects to verify a customer, how to track where their verification stands, and the extra requirements that apply in specific countries.

Every customer must clear identity verification before their wallet can transact.

## Choosing a flow

| Flow | You submit | Customer submits |
| - | - | - |
| [**KYC WebSDK**](https://docs.etherfuse.com/guides/kyc-websdk.md) (the default) | Nothing | Everything |
| [**KYC API**](https://docs.etherfuse.com/guides/kyc-api.md) | Personal data, identity document, questionnaire answers, and any [region-specific documents](#country-specific-requirements) | Liveness selfie, and any questionnaire you do not answer over the API |
| [**Sumsub shared token**](https://docs.etherfuse.com/guides/kyc-sumsub-shared-token.md) | A share token, then questionnaire answers and any [region-specific documents](#country-specific-requirements) | Any questionnaire you do not answer over the API, plus liveness if the shared session did not cover it |

## Requirements List

| Requirement | What satisfies it | How it can be submitted |
| - | - | - |
| `personal_data` | Name, date of birth, tax id, country, and residential address. | WebSDK · API · Shared token |
| `identity_document` | A government-issued photo ID. Any valid one works. | WebSDK · API · Shared token |
| `proof_of_address` | A utility bill, bank statement, tax bill, employment certificate, property deed, or invoice, issued within the last three months. Requested only when the identity document carried no address. | WebSDK · API · Shared token |
| `occupation` | Job title and an occupation category. | WebSDK · API |
| `selfie` | A live face capture. | WebSDK · Shared token |
| `customer_agreement` | The customer accepting Etherfuse's agreements. | WebSDK · API |

Some countries add specific additional requirements. See [Country-specific requirements](#country-specific-requirements).

Supply `userInfo.email` when [creating the customer](https://docs.etherfuse.com/guides/kyc-websdk.md#the-flow).

## Requirements unlock in stages

A customer does not owe every requirement at once. Verification runs in stages, and each stage asks for its own requirements. Today a customer moves through these stages in this order:

1. **Routing.** The country in their personal data decides which requirements apply. Nothing else can be sent until this lands.
2. **Identity.** `personal_data`, `identity_document`, `selfie`, `occupation`, and any [country-specific](#country-specific-requirements) requirement.
3. **Proof of address.** Only when the identity document carried no address.
4. **Agreement.** `customer_agreement`.

Drive your integration from the [requirements breakdown](#the-per-requirement-breakdown), not from this list. We add and reorder stages, and some countries may gain stages of their own.

The rules:

* **The breakdown lists only the current stage, plus requirements already satisfied.** A requirement from a later stage is not in the list at all until the customer reaches that stage.
* **Send a requirement only once it is in the list as `pending` or `action_required`.** If you send it earlier, the request is accepted with a `202`, then refused in the background. A [`kyc_updated`](https://docs.etherfuse.com/guides/verifying-webhooks) webhook arrives with `needsWork: true`, and nothing retries it.
* **A stage moves on by itself.** When everything the current stage asks for has landed, it is submitted for review and the overall `status` becomes `submitted`. When the review clears, the next stage's requirements appear in the list and `status` returns to `in_progress`. Both moves fire `kyc_updated`. Review is usually a few seconds; a manual review takes longer.
* **Proof of address is the one exception.** You may send it before its stage, as long as the identity stage has not been submitted yet. Send it in the same request as the identity document. It is used only if the proof of address stage is reached.

So the loop for anything you submit over the API is:

**1. Read the breakdown**

[`GET /ramp/customer/{customer_id}/kyc?requirements=true`](https://docs.etherfuse.com/api-reference/kyc/get-kyc-status)

**2. Send what is pending**

Everything in the list with `status: "pending"` or `"action_required"` and `requiresLaunch: false`.

**3. Wait for the next kyc\_updated**

It fires when the stage is submitted, and again when the next stage arrives. Without a webhook registered, read the breakdown again after a few seconds.

**4. Repeat until only launch rows are left**

When everything outstanding reads `requiresLaunch: true`, [launch the customer](https://docs.etherfuse.com/guides/user-launch-flows.md#identity-verification). When nothing is outstanding, wait for `status: "approved"`.

## Tracking verification status

### The overall status

```bash Sandbox theme={null}
curl -H "Authorization: <api_key>" \
  https://api.sand.etherfuse.com/ramp/customer/<customer_uuid>/kyc
```

```bash Production theme={null}
curl -H "Authorization: <api_key>" \
  https://api.etherfuse.com/ramp/customer/<customer_uuid>/kyc
```

```json theme={null}
{
  "customerId": "123e4567-e89b-12d3-a456-426614174000",
  "status": "submitted",
  "currentRejectionReason": null,
  "needsWork": false,
  "approvedAt": null
}
```

| Status | Description |
| - | - |
| `not_started` | No verification started |
| `in_progress` | Started, but not everything is submitted. Detail in `needsWork` and `userMessage` |
| `submitted` | Submitted, awaiting review (manual or automated) |
| `approved` | Cleared; the customer can transact |
| `denied` | A rejected verdict, or a previously-approved customer whose approval was revoked. Reason in `currentRejectionReason` |

### The per-requirement breakdown

Add **`?requirements=true`** for a step-by-step account of what the customer still needs. It is opt-in: omit the parameter and the response is unchanged.

```bash theme={null}
curl "https://api.sand.etherfuse.com/ramp/customer/<customer_uuid>/kyc?requirements=true" \
  -H "Authorization: <api_key>"
```

```json theme={null}
{
  "customerId": "123e4567-e89b-12d3-a456-426614174000",
  "status": "in_progress",
  "requirements": [
    { "type": "personal_data",      "status": "satisfied", "requiresLaunch": false },
    { "type": "identity_document",  "status": "satisfied", "requiresLaunch": false },
    { "type": "occupation",         "status": "pending",   "requiresLaunch": false },
    { "type": "selfie",             "status": "pending",   "requiresLaunch": true  }
  ]
}
```

This customer is on the identity stage. `customer_agreement` is not in the list yet, so it cannot be sent yet. Once the identity stage clears, the same call returns:

```json theme={null}
{
  "customerId": "123e4567-e89b-12d3-a456-426614174000",
  "status": "in_progress",
  "requirements": [
    { "type": "personal_data",      "status": "satisfied", "requiresLaunch": false },
    { "type": "identity_document",  "status": "satisfied", "requiresLaunch": false },
    { "type": "occupation",         "status": "satisfied", "requiresLaunch": false },
    { "type": "selfie",             "status": "satisfied", "requiresLaunch": true  },
    { "type": "customer_agreement", "status": "pending",   "requiresLaunch": false }
  ]
}
```

Each `type` matches a requirement in the [Requirements List](#requirements-list) or a [country-specific](#country-specific-requirements) one. `requiresLaunch: true` marks the rows only the WebSDK can satisfy.

> [!WARNING]
>
> **`requirements` is absent until the customer has been routed to their country's requirements**, so it is missing immediately after their personal data is submitted. Read it again a moment later. After that it **grows** as the customer moves through the [stages](#requirements-unlock-in-stages): `proof_of_address` and `customer_agreement` appear only when their stage is reached. Re-read it after every `kyc_updated`; no single response is the full list.

> [!NOTE]
>
> We reserve the right to add new requirements, and will update these docs when we do. Some countries may also gain requirements of their own.

### Requirement statuses

| `status` | Meaning | What to do |
| - | - | - |
| `satisfied` | Accepted. | Nothing. |
| `pending` | The current stage asks for this, and nothing has been provided yet. | Supply it. |
| `awaiting_review` | Provided, not yet reviewed. | Wait. |
| `action_required` | **Reviewed and rejected.** | Send it again, corrected. |

## Country-specific requirements

> [accordion] 🇲🇽 Mexico
>
> | Requirement | What satisfies it | How it can be submitted |
> | - | - | - |
> | `personal_data` | Both CURP and RFC, entered as text. | WebSDK · API |
>
> Send `additionalInfo.curp` and `taxId` (RFC) through [`POST /ramp/customer/{customer_id}/verification`](https://docs.etherfuse.com/api-reference/kyc/submit-verification-data), or let the customer enter them in the widget. CURP and RFC are required.
>
> > [!NOTE]
> >
> > **In sandbox, use first name `CARLOS`, last name `RAMIREZ LOPEZ` and RFC `HEGL900101AB6`.** The RFC check still runs, and the sandbox registry returns this person for that RFC. With any other name, personal data is rejected.

## Reference

**Bank account status**

A bank account must be **`active`** before you can create orders against it. Check the `status` in the registration response or via [GET /ramp/bank-accounts](https://docs.etherfuse.com/api-reference/bank-accounts/list-bank-accounts.md); a [`bank_account_updated`](https://docs.etherfuse.com/guides/verifying-webhooks) webhook fires when it changes.

> [!NOTE]
>
> In **sandbox**, register the bank account with the placeholder RFC `XEXX010101000` to mark it `active` immediately. Any other RFC is treated as a real registration and will not auto-approve.

**Webhooks**

Register a webhook for `kyc_updated` events to receive status updates. See [POST /ramp/webhook](https://docs.etherfuse.com/api-reference/webhooks/create-webhook.md) for setup and payload details.

**Approved:**

```json theme={null}
{
  "kyc_updated": {
    "customerId": "123e4567-e89b-12d3-a456-426614174000",
    "walletPublicKey": "9Qx7r...",
    "status": "approved",
    "approved": true,
    "updateReason": "KYC approved"
  }
}
```

**Denied:**

```json theme={null}
{
  "kyc_updated": {
    "customerId": "123e4567-e89b-12d3-a456-426614174000",
    "walletPublicKey": "9Qx7r...",
    "status": "denied",
    "approved": false,
    "updateReason": "Identity verification could not be completed."
  }
}
```

**Data isolation**

Partners can only access KYC data they submitted:

| Accessor | Data Visibility |
| - | - |
| Partner (via API key) | Only data where `source_organization_id` matches their org |
| Wallet Owner | All data for their wallet |
| Admin | All data |

This prevents partners from accessing PII submitted by other partners or directly by users.


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