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

# Users

> Register your end users, update their data, follow their KYC status and know when they can operate.

Every operation belongs to one of your end users. You register users with the API, Kravata runs an automatic KYC review, and a user can operate once their status is `APPROVED`.

## Register a user

Call [User Registration](/stack/api-reference/users/user-registration) with the user's data and the `x-integration-register` header. In test, you get that value when you [configure your environment](/stack/company-signup#2-configure-your-environment), and it is also shown when you generate your credentials. In production, Kravata gives it to you when it configures your integration.

```bash theme={null}
curl -X POST https://partners-api.kravata.co/api/v1/users/register \
  --cert client.crt --key client.key \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-integration-register: YOUR_INTEGRATION_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "external_user_id": "3f1a9c2e-7b6d-4e11-9a3f-2c8d6e0b5a17",
    "first_name": "John",
    "first_surname": "Doe",
    "birth_date": "1988-11-02",
    "cell_phone_number": "3009876543",
    "email": "john.doe@example.com",
    "document_type": "CC",
    "document_number": "1029384756",
    "document_issue_date": "2015-03-22",
    "document_issuance_country": "Colombia"
  }'
```

The response contains the user ID (`user_id`). Store it: you use it in every user endpoint, for example to register accounts and wallets or to create operations.

<Info>
  The fields that are required depend on the configuration of your integration. The [endpoint reference](/stack/api-reference/users/user-registration) lists the default set. If a required field is missing, the response says which one.
</Info>

<Tip>
  Always send a stable `external_user_id`, your own ID for that user. Kravata uses it to recognize the user when you send them again.
</Tip>

## Update a user

There is no separate update endpoint: send the user again to the same **User Registration** endpoint. Kravata finds the existing user by `external_user_id` first, then by document number, phone and email, in that order.

* If the user exists, Kravata **updates the data** and returns the **same `user_id`**. You don't get a duplicate or a conflict.
  * Names and document data are replaced.
  * Contact data and additional fields are merged; the values you send win.
  * Kravata keeps a history of the previous values.
* If no user matches, a new user is created.

How an update affects the status:

| Current status | After an update |
| - | - |
| `IN_REVIEW`, `PENDING`, `EHD`, `REPROCESSING_REQUIRED` | Goes back to `IN_REVIEW`. |
| `APPROVED`, `REJECTED`, `CANCELED` | Unchanged. The data is still updated. |

<Warning>
  If the document you send belongs to a **different** user of your company, the request is rejected with `400` and a message like `Webhook error: 409 - ... already exists`. Check that you are not mixing up two users.
</Warning>

## Check a user's status

The KYC review is **asynchronous**: right after you register a user, their status is usually `IN_REVIEW`. Don't rely on the registration response for the status. Instead:

* Subscribe to the `user.updated` [webhook](/stack/webhooks), which fires when the status changes.
* Or query [Get Users](/stack/api-reference/users/get-users) (`GET /api/v1/admin/users`). It accepts `page` and `pageSize` (pagination applies only when you send both), `orderBy`, `orderDirection`, and `numberPhone` to filter by part of a phone number.

### Statuses

| Status | Meaning | What to do |
| - | - | - |
| `IN_REVIEW` | Data received; the automatic KYC review is running. | Wait for `user.updated`. |
| `APPROVED` | KYC passed. The user can operate. | Register accounts and wallets, and create operations. |
| `EHD` | Enhanced due diligence: Kravata's compliance team reviews the user manually. | Wait. Kravata contacts you if more information is needed. |
| `REPROCESSING_REQUIRED` | Kravata asked the user to verify their identity again. | Create a new identity-verification link (see below) and have the user complete it. |
| `REJECTED` | KYC failed. Final. | Sending the user again does not reopen the review. |
| `CANCELED` | The user's account was closed. Final. | — |
| `PENDING` | Placeholder without the user's data. You normally don't see it through the API. | — |

<Warning>
  **Only `APPROVED` users can operate.** Operations for any other user are rejected with `403`.
</Warning>

## How the KYC review works

After registration, Kravata checks the user against restrictive lists and PEP lists, applies its compliance rules and computes a risk score. The result sets the status:

* No findings → `APPROVED`.
* A finding that requires a manual review → `EHD`.
* A blocking finding → `REJECTED`.

If a list provider is not available at that moment, the user goes to `EHD` instead of being approved without the check.

The data you send affects the review: date of birth, occupation, PEP declaration, income, expenses, assets and liabilities, residence, document issue date, and source of funds. Send complete and accurate data to avoid unnecessary manual reviews.

### Identity verification link

If your integration doesn't send its own identity-verification results, or when a user is in `REPROCESSING_REQUIRED`, call [Generate KYC Link](/stack/api-reference/users/generate-kyc-link) (`POST /api/v1/provider/kyc/user/generateurl/{user_id}`). Send the user to the `verificationUrl` in the response. When the user finishes the verification, the status is updated and you receive `user.updated`.

## User webhooks

| Event | When | Notes |
| - | - | - |
| `user.created` | A user is registered. | It is sent again every time you send the same user to User Registration, so handle it idempotently by `userId`. |
| `user.updated` | The user's data or **status** changes. | Use it to know when a user becomes `APPROVED`, `EHD` or `REJECTED`. |
| `user.canceled` | The user's account is closed. | — |

The payload is the user as a flat JSON object. It includes `userId`, `status`, the names, `identificationType`, `identificationNro` and `metadata`; `user.updated` also includes `tradeName` and `contactData`. Metadata can include large values, such as documents encoded in Base64. See [Webhooks](/stack/webhooks) to register your endpoint.

## Errors

| Status | Message | Cause |
| - | - | - |
| 400 | `Missing required header: x-integration-register` | The header is missing. |
| 400 | `Integration endpoint not found: ...` | The `x-integration-register` value is wrong or inactive. Check it with your Kravata point of contact. |
| 400 | `Missing required fields: ...` or `Missing or empty required fields for this client: [...]` | A field required by your integration is missing or empty. |
| 400 | `Webhook error: 409 - ...` | The document belongs to another user of your company. |
| 400 | `Webhook error: 422 - ...` | A field has an invalid format. The message shows which. |
| 403 | `User is not APPROVED` | You tried to operate with a user that is not approved. |
| 500 | `User registration failed` | Unexpected error. Retry; if it persists, contact Kravata with the `request_id`. |


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