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

# H5 KYC guidance page

> Whether you want to hand the whole KYC experience over to us or simply want no documents and no face data anywhere near your own backend, the H5 KYC guidance page is the answer: send the user to a DCS-hosted H5 page where they select their nationality, upload their document, complete the face scan and fill in their address themselves. DCS is a licensed card issuer, so the entire KYC flow is verified and stored securely on our side. Your only jobs are to hand the link to the user and to receive the result.

## 📄 Guide

Whether you want to hand the whole KYC experience over to us or simply want no documents and no face data anywhere near your own backend, the H5 KYC guidance page is the answer: send the user to a DCS-hosted H5 page where they select their nationality, upload their document, complete the face scan and fill in their address themselves. DCS is a licensed card issuer, so the entire KYC flow is verified and stored securely on our side. Your only jobs are to hand the link to the user and to receive the result.

H5 mode and [API mode](../kyc/apply-kyc) are alternatives; pick one:

| What you want                                                                                     | Use                                          |
| ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| To collect the documents and address details yourself and submit them through the API in one call | [Apply for KYC (API mode)](../kyc/apply-kyc) |
| To stay out of capture entirely and let the user fill everything in on a DCS-hosted H5 page       | This page (H5 mode)                          |

***

### Mental model: who does what

In H5 mode you do exactly three things; DCS carries everything else.

<Frame>
  <img className="block dark:hidden" src="https://mintcdn.com/dcs-0bf7a937/oUoeUKtwWWK2o-Li/imgs/en/diagrams/pa-h5-kyc-flow-light.svg?fit=max&auto=format&n=oUoeUKtwWWK2o-Li&q=85&s=9f47a5cdba5d37f3f9f99d274be57ee2" alt="Division of work between DCS and the partner in H5 mode" width="638" height="782" data-path="imgs/en/diagrams/pa-h5-kyc-flow-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/dcs-0bf7a937/oUoeUKtwWWK2o-Li/imgs/en/diagrams/pa-h5-kyc-flow-dark.svg?fit=max&auto=format&n=oUoeUKtwWWK2o-Li&q=85&s=ba6940943a5fcc2740dff5d10b98f7ce" alt="Division of work between DCS and the partner in H5 mode" width="638" height="782" data-path="imgs/en/diagrams/pa-h5-kyc-flow-dark.svg" />
</Frame>

| Task                                                                                                             | Who     | Notes                      |
| ---------------------------------------------------------------------------------------------------------------- | ------- | -------------------------- |
| Serve the H5 page (document type selection, Sumsub SDK upload and face scan, career and income details, address) | DCS     | You build no KYC UI at all |
| Receive and securely store the KYC information the user submits                                                  | DCS     |                            |
| Run DCS risk screening and Sumsub identity verification, and advance the ticket status asynchronously            | DCS     |                            |
| Send a webhook whenever the status changes                                                                       | DCS     | Event type `KYC_TICKET`    |
| Send the H5 URL to the user, receive the result and move on to card issuing                                      | Partner |                            |

***

### Step 1: create the user (you)

Call `POST /open-api/customer/v1/create-customer` to create the user and take the `customerId`. See [Create a user](../users/create-customer).

***

### Step 2: apply for the H5 KYC ticket (you)

Call `POST /open-api/kyc-ticket/v1/apply-kyc-h5` to create the ticket.

**Request body (`APIApplyKycH5Request`)**

| Field              | Type   | Required                     | Constraint | Description                                                                                                                                                             |
| ------------------ | ------ | ---------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kycTicketRef`     | string | Yes                          | ≤50        | Your own idempotency reference, chosen by you (a business order number works well); you can query by it later                                                           |
| `customerId`       | string | Yes                          | ≤50        | The user ID returned by step 1                                                                                                                                          |
| `kycApplyMode`     | string | No                           | ≤20        | How to apply: `H5` (first-time KYC, the default) / `H5-RENEWAL` (update KYC information) / `H5-MIGRATION` (migrate KYC information). Omitting it is treated as `H5`     |
| `sourceCustomerId` | string | Required with `H5-MIGRATION` | ≤50        | The source user identifier: pass that user's `externalUserId` from the DeCard-Managed model. Despite the `...CustomerId` name, this is not a `customerId` of this model |

> Note that `kycApplyMode` follows the endpoint definition. It is not part of the standard flow guide. Send `H5-RENEWAL` when an existing user has to resubmit or refresh their information.
>
> This page covers first-time KYC (`H5`) only. For re-verification after the information expires, see [Updating KYC information](./kyc-renewal); for reusing KYC the user already holds under the DeCard-Managed model, see [KYC information migration](./kyc-migration).

**Minimum request**

```json theme={null}
{
  "kycTicketRef": "kyc-20260617-0001",
  "customerId": "1000000123"
}
```

**Response (`APIApplyKycH5Response`, with the common envelope stripped)**

| Field                        | Type   | Description                                  |
| ---------------------------- | ------ | -------------------------------------------- |
| `kycTicketId`                | string | KYC ticket ID, used in step 3 and in queries |
| `kycTicketRef`               | string | The idempotency reference, echoed back       |
| `customerId`                 | string | Customer ID                                  |
| `status`                     | string | Ticket status; `INIT` on a new ticket        |
| `createTime`                 | string | Creation time                                |
| `errorCode` / `errorMessage` | string | Returned on failure                          |

```json theme={null}
{
  "code": "SYS_SUCCESS",
  "message": "success",
  "messageDetail": null,
  "data": {
    "kycTicketId": "9000000888",
    "kycTicketRef": "kyc-20260617-0001",
    "customerId": "1000000123",
    "status": "INIT",
    "createTime": "2026-06-17T08:00:00+08:00"
  }
}
```

> The common envelope is `{code, message, messageDetail, data}`: `code` is the business status code (`SYS_SUCCESS` on success) and `messageDetail` carries extra error detail (`null` on success). For what the fields mean, see [Authorization declines and error codes](../transactions/decline-codes).

**Idempotency behavior**

* The `kycTicketRef` has not been seen before: a new `INIT` ticket is created.
* The `kycTicketRef` already exists, belongs to the same enterprise and customer, and the ticket is still `INIT`: the existing ticket is returned unchanged, which is what makes retries safe.
* Anything else, such as a mismatched owner or a ticket already at `PENDING` or `PASSED`: the call fails with one of the error codes listed below.

***

### Step 3: exchange the ticket for an H5 link (you)

Call `POST /open-api/card-redirect/v1/guidance-link` with `type=7` to turn the ticket into an H5 URL you can send to the user.

**Request body (the H5 KYC fields of `APIGuidanceRequest`)**

| Field                   | Type   | Required                 | Constraint | Description                                                                                  |
| ----------------------- | ------ | ------------------------ | ---------- | -------------------------------------------------------------------------------------------- |
| `type`                  | string | Yes                      | ≤1         | Fixed at `7` (apply for KYC-H5). See the full enumeration below                              |
| `customerId`            | string | Yes                      | ≤50        | Same as in steps 1 and 2                                                                     |
| `kycTicketId`           | string | Required when type=7     | ≤50        | The `kycTicketId` returned by step 2                                                         |
| `profileId`             | string | **Required when type=7** | ≤50        | Card profile ID; ask the DCS team for it. **It must be sent even though it is easy to miss** |
| `language`              | string | Yes                      | Length 2   | `zh` / `en`; non-Chinese-speaking countries all map to `en`                                  |
| `theme`                 | string | Yes                      | ≤10        | `default`, for example (`blue` supports card information lookup only)                        |
| `mode`                  | string | Yes                      | ≤10        | `light` / `dark`                                                                             |
| `userAgent`             | string | Yes                      | ≤300       | The user's browser user agent                                                                |
| `successfulRedirectUrl` | string | No                       | ≤200       | Where to send the user after a successful completion                                         |
| `errorRedirectUrl`      | string | No                       | ≤200       | Where to send the user after a failure                                                       |
| `submitAutoClosed`      | string | No                       | ≤1         | `Y` closes the page automatically after a successful submission, `N` leaves it open          |
| `primaryColor`          | string | No                       | ≤7         | Primary colour, such as `#FFFFFF`                                                            |

The full `type` enumeration, as defined by the code:

| type | Meaning                                 | Also required                     |
| ---- | --------------------------------------- | --------------------------------- |
| 1    | Card information lookup                 | `cardId`                          |
| 2    | Set PIN                                 | `cardId`, `otpStatus`             |
| 3    | Reset PIN                               | `cardId`, `otpStatus`             |
| 4    | Apply for KYC, liveness (face scan)     | `kycTicketId`                     |
| 5    | Information verification                | `cardOrderId`                     |
| 6    | Supplement card application information | `cardOrderId`                     |
| 7    | Apply for KYC-H5                        | `kycTicketId`, `profileId`        |
| 8    | Update KYC information                  | `kycTicketId`, `errorRedirectUrl` |
| 9    | Migrate KYC information                 | `kycTicketId`, `errorRedirectUrl` |

> A naming discrepancy worth knowing: the code labels `type=4` as apply for KYC (liveness) and defines a `type=8` for updating KYC information, whereas the API reference calls `type=4` face recognition and lists no `type=8`. This page follows the code.

**Minimum request**

```json theme={null}
{
  "type": "7",
  "customerId": "1000000123",
  "kycTicketId": "9000000888",
  "profileId": "<ask DCS for this>",
  "language": "en",
  "theme": "default",
  "mode": "light",
  "userAgent": "Mozilla/5.0 ..."
}
```

**Response**

The H5 URL comes back in the `data` field of the common envelope, as a plain string. Send that URL to the user.

```json theme={null}
{
  "code": "SYS_SUCCESS",
  "message": "success",
  "messageDetail": null,
  "data": "https://h5.thedecard-sandbox.com/kyc?token=..."
}
```

> H5 links expire, so get the user through the flow while the link is still valid. If it does expire, call this endpoint again for a fresh one, as long as the ticket is still `INIT`.

***

### Step 4: receive the result (webhook, with a query as backup)

**Webhook** (recommended; DCS pushes to you). Once your webhook is configured, every ticket status change arrives as a `KYC_TICKET` event.

| When                                                 | Event       | status    |
| ---------------------------------------------------- | ----------- | --------- |
| The user makes their final submission on the H5 page | KYC\_TICKET | `PENDING` |
| The asynchronous review approves the ticket          | KYC\_TICKET | `PASSED`  |

For the webhook payload, see [Webhook events and schema](../webhooks/events-and-schema).

**Query as backup** (you): call `GET /open-api/kyc-ticket/v1/detail` at any time with either `kycTicketId` or `kycTicketRef` to read the current status.

***

### KYC ticket statuses (H5 mode)

| Status    | Description                                                            | Terminal | What you do next                                                                        |
| --------- | ---------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------- |
| `INIT`    | The ticket exists and is waiting for the user to submit on the H5 page | No       | Send the H5 link to the user; call `guidance-link` again for a fresh link if it expires |
| `PENDING` | The user has submitted and DCS is reviewing asynchronously             | No       | Wait for the webhook, or poll                                                           |
| `PASSED`  | Approved                                                               | Yes      | Move on to the [virtual card application](../cards/virtual-card)                        |

> Standard `H5` mode normally shows only `INIT`, `PENDING` and `PASSED`; a problem in review usually leaves the ticket at `PENDING` pending manual intervention. The newer `H5-RENEWAL` route has its own statuses, `RENEWAL_INIT / PENDING / RETRY / PASS / REJECT`, which map back onto the ticket as `INIT / PASSED / REJECTED`. Regenerating a link for the same `kycTicketRef` still requires the ticket to be `INIT`: `NEED_VERIFY` and `PENDING` return in progress, `PASSED` returns already approved, and `REJECTED` returns verification failed. For the statuses used by `H5-MIGRATION`, see [KYC information migration](./kyc-migration).

***

### apply-kyc-h5 error codes

| Error code                                         | Description                                                                                  |
| -------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `DAPI_KYC_TICKET_CUSTOMER_NOT_FOUND`               | The user the KYC ticket refers to does not exist                                             |
| `DAPI_KYC_TICKET_CUSTOMER_NOT_FOUND_IN_ENTERPRISE` | That user does not belong to this enterprise                                                 |
| `DAPI_KYC_TICKET_REF_NOT_UNIQUE`                   | The `kycTicketRef` is not unique; it clashes with an existing ticket under a different owner |
| `DAPI_KYC_TICKET_IS_IN_PROCESSING`                 | The ticket is being processed (already `PENDING`)                                            |
| `DAPI_KYC_TICKET_ALREADY_PASSED`                   | The ticket has already been approved; apply for the virtual card directly                    |

For how the apply-kyc-h5 error codes are grouped, see [KYC reject codes](./kyc-reject-codes); for the general and authorization error codes, see [Authorization declines and error codes](../transactions/decline-codes).

***

### Prerequisites

* You hold the Enterprise ApiKey / SecretKey — see [First steps](../../getting-started/first-steps).
* You have created the user (step 1) — see [Create a user](../users/create-customer).
* You have the `profileId` (card profile ID) from the DCS team.

***

## Next steps

* Once KYC is `PASSED`, go to [Apply for a virtual card](../cards/virtual-card) and issue the card by submitting `kycTicketId` and `customerId` with `cardApplyMode=NORMAL`. The same `kycTicketId` can be reused for any further cards you issue to that user.
* Prefer to submit documents through the API rather than a hosted page? See [Apply for KYC](./apply-kyc).
* To read a ticket's current status at any time, see [Query KYC](./query-kyc).
