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

# Completing a KYC

> Verify a user's identity once so Agentcard can issue them cards. Hosted page or your own UI.

Issued cards are real cards backed by real money, so the user verifies their identity once before the first one. It takes about two minutes: a government ID, a short face scan, and any details the document did not carry. There is no SSN requirement outside the US, and any national ID works from any supported country.

KYC is only for Issuing. A user who pays with their own card through the [Vault](/vault/quickstart) never sees it.

## Read the status

Every KYC response is the same object with exactly one `status`:

```bash theme={null}
curl "https://api.agentcard.sh/api/v2/kyc?user_id=user_7g8h9i" \
  -H "Authorization: Bearer $ORG_TOKEN"
```

```json theme={null}
{ "object": "kyc", "status": "requires_verification", "iframe_url": "https://in.sumsub.com/websdk/p/…" }
```

| Status                  | Meaning                             | What to do                                                            |
| ----------------------- | ----------------------------------- | --------------------------------------------------------------------- |
| `awaiting_documents`    | Waiting for the ID images.          | Hand the user the `iframe_url`, or upload the images yourself.        |
| `needs_information`     | The review asked for typed details. | Hand the user the `iframe_url`, or submit `required_fields` yourself. |
| `requires_verification` | Ready for the face scan.            | Hand the user the `iframe_url`.                                       |
| `pending`               | Under review.                       | Wait.                                                                 |
| `approved`              | Verified.                           | Issue cards.                                                          |
| `rejected`              | Not verified.                       | Show the `reason`. The user did not pass.                             |

## Option A: hosted (recommended)

Whenever the status is actionable, hand the user the `iframe_url`. The hosted page collects whatever the verification still needs, documents, typed details, the face scan, and no identity data passes through your servers. Open it in a new tab, a WebView, or embed it.

The link is short-lived. Always surface the one from your freshest status read or `identity.verification.updated` event rather than storing it.

## Option B: your own UI

Drive the upload endpoints from your own screens and use the hosted page only for the face scan.

1. `POST /api/v2/kyc/documents/front` and `POST /api/v2/kyc/documents/back` with the ID images. The back-of-ID response is the branch point: `needs_information`, `requires_verification`, or `rejected`. It also returns `extracted` fields to prefill your form and `warnings` you can show the user.
2. `POST /api/v2/kyc/information` with the `required_fields` the review asked for.
3. At `requires_verification`, show the `iframe_url` for the face scan.

Details for each call are in the [Identity verification](/api-reference/identity-verification/overview) reference.

## Learn the outcome

Subscribe to `identity.verification.updated` on a [webhook endpoint](/api-reference/webhook-endpoints/overview), or poll `GET /api/v2/kyc`. Treat the webhook as the record and the conversation as a claim: the user saying "done" is not `approved`.

## Already verified them elsewhere?

If you run your own KYC on Sumsub, share the applicant with a one-time token and the user skips document capture and the face scan entirely:

```bash theme={null}
curl -X POST https://api.agentcard.sh/api/v2/kyc/import \
  -H "Authorization: Bearer $ORG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"user_id": "user_7g8h9i", "share_token": "_act-jwt-…", "user_ip": "203.0.113.7"}'
```

The accounts must be paired first, and the import can still come back `needs_information` for a residential address. Ask us to enable sharing for your organization.

## Sandbox

Sandbox verifications never reach a reviewer. They report only `requires_verification`, `pending`, `approved`, or `rejected`, and the `iframe_url` is a test-mode chooser. Drive the outcome yourself:

```bash theme={null}
curl -X POST https://api.agentcard.sh/api/v2/kyc/simulate \
  -H "Authorization: Bearer $ORG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"user_id": "user_7g8h9i", "outcome": "approved"}'
```

`outcome` is `approved`, `rejected`, or `requires_input` (a retryable bounce back to `requires_verification`). Re-simulating overwrites the previous outcome, so one test user can walk every path. Live tokens are refused with `403 sandbox_only`.
