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

# Quickstart

> Issue your first working card in the sandbox along the shortest path: send an OTP → register the user → get the onboarding guidance link → receive the card-application Webhook → manage the card → fund it and simulate a purchase.

This page walks you through issuing your first working card in the sandbox using the **recommended best-practice path**. As a licensed card issuer with its own BINs, DCS closes the loop on issuing, KYC, authorization and settlement inside a single API set — you do not need to build any card-core capability yourself.

This page follows one minimal path only (mobile registration, hosted guidance page, a single virtual card, a single deposit currency). The full KYC flow, chain/currency matrix, card management and reconciliation are covered on their own pages.

***

## Before you start

Make sure the following are in place (see [First Steps](./first-steps) for details):

* You understand the **DeCard-Managed model**: authorization decisions are made inside DCS; cardholders move funds into DCS custody and DCS derives the card's spending limit from their available assets in real time (see [Overview](./overview)).
* You have received your **API Key / Secret Key**.
* You have given DCS your **network egress IP** (DCS enforces an API whitelist).
* **Webhook callback URL** (required): the card-issuing result in this flow is delivered by Webhook. Without it you cannot complete Step 4.
* **WebSocket connection** (optional): configure it if you want real-time pushes of balance changes, transaction results, etc. See [Webhook & WebSocket](../integration-resources/webhook-websocket).

> Environments — Sandbox: `https://api.thedecard-sandbox.com`; Production: `https://api.thedecard.com`.

> **Authentication**: every request must carry the four headers `X-DAPI-API-KEY` / `X-DAPI-TIMESTAMP` / `X-DAPI-NONCE` / `X-DAPI-SIGN` with an HMAC-SHA256 signature. The full rules (signature concatenation formula, replay protection, code samples) are in the [Authentication Guide](../integration-resources/overview). The examples below **omit the auth headers** to keep the focus on business fields — they are mandatory in real calls. `SecretKey` is only used locally to compute the signature and is never sent over the wire.

> **Path prefix**: DeCard-Managed modules use the `/account/`, `/card/`, `/crypto/`, `/user-asset/`, `/redirect/` and `/simulation/` prefixes directly (no `/open-api/` prefix).

<Warning>
  All mobile numbers, user IDs and addresses below are **masked placeholders** — replace them with your own values.
</Warning>

***

## 📄 Guide

Six steps in total. The overall shape is: **you call the API to initiate, the cardholder completes sensitive operations on a DCS-hosted page, and the result comes back to you over Webhook.**

<Frame>
  <img className="block dark:hidden" src="https://mintcdn.com/dcs-0bf7a937/oUoeUKtwWWK2o-Li/imgs/en/diagrams/va-quickstart-flow-light.svg?fit=max&auto=format&n=oUoeUKtwWWK2o-Li&q=85&s=b9e45417f312e9140486c84e544d3ae3" alt="Quickstart six-step sequence diagram" width="560" height="798" data-path="imgs/en/diagrams/va-quickstart-flow-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/dcs-0bf7a937/oUoeUKtwWWK2o-Li/imgs/en/diagrams/va-quickstart-flow-dark.svg?fit=max&auto=format&n=oUoeUKtwWWK2o-Li&q=85&s=ad2fbb40e0aa33d492ef1a8430a57039" alt="Quickstart six-step sequence diagram" width="560" height="798" data-path="imgs/en/diagrams/va-quickstart-flow-dark.svg" />
</Frame>

> The diagram shows the **production path**. In the sandbox, the deposit and purchase in Step 6 use simulator endpoints instead — see that step for details.

### Step 1: Send the SMS verification code

In the DeCard-Managed model, users register in two stages with "mobile number + SMS OTP". First request a code for the target mobile number:

```http theme={null}
POST /captcha/v1/send-mobile-code
```

```json theme={null}
{
  "mobileCode": "SG",
  "mobile": "91234567",
  "externalUserId": "",
  "behavioral": "REGISTER"
}
```

> `mobileCode` is the ISO 2-letter country code (e.g. `SG` / `CN` / `US`); `mobile` is the local number without the dialing prefix. Leave `externalUserId` empty when registering a new user.
> `behavioral` is a **scenario enum** identifying what the code is for: `REGISTER` (registration) / `CARD_UNFROZEN` (unfreeze a card) / `WITHDRAW` (withdrawal). Use `REGISTER` here.

Successful response (the envelope is always `{code, message, messageDetail, data}`, success code `code = SYS_SUCCESS`):

```json theme={null}
{
  "code": "SYS_SUCCESS",
  "message": null,
  "messageDetail": null,
  "data": ""
}
```

> `data` is always an empty string for this endpoint — the code is delivered by SMS, not in the response body. Judge success by `code = SYS_SUCCESS`.
> About the envelope: `messageDetail` is usually `null` on success; on failure, or when the end user needs to be prompted, it is an **object** (with `message` / `title` / `type` / `icon` / `action` / `linkTitle` / `linkUrl`). To keep the focus on business fields, the remaining examples on this page show the envelope in its success form only.

### Step 2: Register the user

Complete registration with the SMS code from the previous step:

```http theme={null}
POST /account/v1/register
```

```json theme={null}
{
  "mobileCode": "SG",
  "mobile": "91234567",
  "smsCode": "<SMS code>"
}
```

> Mobile registration requires `mobileCode` + `mobile` + `smsCode` **all three together**.
> DCS also supports an email registration path (`email` + `emailCode`), which is **strictly mutually exclusive** with the mobile path — sending `smsCode` and `emailCode` in the same request is rejected with `SMS_EMAIL_CODE_MUTUALLY_EXCLUSIVE`. This page uses the mobile path only; for the email path see [Signing Up a Customer](../how-to-use/signing-up-a-customer/overview).

The `data` field of the response **returns `externalUserId` directly** (the user ID, a plain string). Every later endpoint identifies the user by it — store it:

```json theme={null}
{
  "code": "SYS_SUCCESS",
  "message": null,
  "messageDetail": null,
  "data": "4dc3e854-xxxx-xxxx-xxxx-91a98b3d832c"
}
```

### Step 3: Get the onboarding guidance link

**This is the recommended way to issue a card.** Both KYC and the card application happen inside a DCS-hosted H5 page — the cardholder's documents and selfie go straight to DCS without passing through your backend, so you do not take on that compliance and storage burden.

```http theme={null}
POST /redirect/v2/guidance-link
```

```json theme={null}
{
  "action": "KYC_GUIDE",
  "externalUserId": "4dc3e854-xxxx-xxxx-xxxx-91a98b3d832c",
  "successRedirectUrl": "https://your-app.example.com/kyc/success",
  "errorRedirectUrl": "https://your-app.example.com/kyc/failed",
  "language": "en"
}
```

> `action`, `externalUserId`, `successRedirectUrl` and `errorRedirectUrl` are **all required**.
> `language` uses the **lower-case hyphenated** form and is **case-sensitive**: `zh` / `en` / `ko` / `ja` / `zh-Hant` / `th` / `vi`. A value outside this whitelist is silently downgraded to the default language rather than rejected — stick to this list exactly.
> For the full parameter set (`theme` / `mode` / `primaryColor` / `selectCardPageShow`, etc.) see [H5 KYC / Card Onboarding Guidance Page](../integration-resources/h5-kyc-guidance).

The `data` field of the response is the one-time guidance link (a plain string). Hand it to the cardholder to open in a browser:

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

> ⏱ **The link is valid for 5 minutes** (the server-side secret has a 300-second TTL); once expired the page stops working and you must request a new one.
> Therefore **do not deliver the link over asynchronous channels such as email or support tickets** — generate it and redirect while the cardholder is in an active session. If the user abandons and comes back, call this endpoint again for a fresh link rather than caching and reusing one.

> **Two pre-checks will reject the request outright — handle them during integration**:
>
> * If the user's KYC is already `PASS` or `PENDING`, the API returns `OPERATION_UNSUPPORTED` — re-initiation is not allowed.
> * If the daily application rate limit is hit, the API returns `KYC_APPLY_LIMIT_EXCEEDED` — throttle your retries and surface a message to the user.

### Step 4: Receive the card-application Webhook

Once the cardholder submits on the hosted page, **the result is pushed to you as a `CARD_APPLY` Webhook**. In this flow the partner never initiates the card application, so this is the only way to obtain `applyId` (once issuing succeeds, `cardId` can also be read from the card query endpoint in Step 5).

> The same event is **delivered over both Webhook and WebSocket** (the server pushes WS first, then the Webhook), with identical payloads. Consuming the Webhook alone is enough for this flow; if you also consume WebSocket, de-duplicate on your side.

The `data` structure of the `CARD_APPLY` event:

| Field           | Type    | Description                                                                                                    |
| :-------------- | :------ | :------------------------------------------------------------------------------------------------------------- |
| `applyId`       | string  | Application ID, needed for the extra-document branch                                                           |
| `cardId`        | string  | Card ID, populated only after the card is issued                                                               |
| `categoryId`    | Long    | Card category ID, decided when the cardholder picks a card on the hosted page — the partner does not supply it |
| `network`       | string  | Card network (e.g. `VISA`)                                                                                     |
| `currency`      | string  | Card currency                                                                                                  |
| `applyRef`      | string  | Idempotency reference of the application                                                                       |
| `status`        | string  | Application status, see the table below                                                                        |
| `errorCode`     | string  | Failure reason, provided only when `FAILED`                                                                    |
| `needExtraInfo` | boolean | Whether extra documents are required, see the branch below                                                     |

`status` has only **three external values** (internal intermediate states such as KYC-passed and card-created are all collapsed into `PENDING`):

| Value     | Terminal | Meaning and action                                              |
| :-------- | :------- | :-------------------------------------------------------------- |
| `PENDING` | No       | In progress. If `needExtraInfo = true`, follow the branch below |
| `SUCCEED` | Yes      | Card issued, `cardId` is usable, go to Step 5                   |
| `FAILED`  | Yes      | Issuing failed, read `errorCode` for the reason                 |

<Warning>
  Return `2xx` **within 2 seconds** of receiving the Webhook (DCS uses a 2000 ms connect timeout and a 2000 ms socket timeout). Persist first and process asynchronously, otherwise the delivery is treated as a timeout and retried. See [Webhook & WebSocket](../integration-resources/webhook-websocket).
</Warning>

#### Branch: `needExtraInfo = true`

When KYC requires additional documents from the cardholder, the Webhook carries `needExtraInfo = true`. Request a second guidance link with the **same `applyId`**:

```http theme={null}
POST /redirect/v2/guidance-link
```

```json theme={null}
{
  "action": "KYC_EXTRA_DOC",
  "externalUserId": "4dc3e854-xxxx-xxxx-xxxx-91a98b3d832c",
  "applyId": "A100001",
  "successRedirectUrl": "https://your-app.example.com/kyc/success",
  "errorRedirectUrl": "https://your-app.example.com/kyc/failed"
}
```

> For `KYC_EXTRA_DOC`, `applyId` is **required** — omitting it returns `OPERATION_UNSUPPORTED`; an `applyId` that does not belong to the user returns `PERMISSION_DENIED`; and an application whose `needExtraInfo` is not `true` also returns `OPERATION_UNSUPPORTED`. **Drive this off the Webhook's `needExtraInfo` — do not call it speculatively.**

After the cardholder submits the documents, DCS pushes `CARD_APPLY` again until `status` reaches the terminal `SUCCEED` or `FAILED`.

### Step 5: Card management

Once you have `cardId`, day-to-day card management is available.

**List cards / query a single card**

```http theme={null}
GET /card/v2/detail?externalUserId=4dc3e854-xxxx-xxxx-xxxx-91a98b3d832c
```

> `externalUserId` is required; `cardId` is optional — leave it empty to return all of the user's cards, or pass it to query one.
> The response includes `cardStatus` (`NORMAL` / `FROZEN` / `CANCELLED`), `cardNo` (masked), `cardHolder`, `physicalCardStatus` and more.

**Show the full card number to the cardholder**

For PCI compliance, the full PAN, CVV and expiry are **never** returned to your backend by the API. Use the `CARD_INFO` guidance page so the cardholder views them directly on the DCS-hosted page:

```http theme={null}
POST /redirect/v2/guidance-link
```

```json theme={null}
{
  "action": "CARD_INFO",
  "externalUserId": "4dc3e854-xxxx-xxxx-xxxx-91a98b3d832c",
  "cardId": "1234567890123456789",
  "successRedirectUrl": "https://your-app.example.com/card/back",
  "errorRedirectUrl": "https://your-app.example.com/card/error"
}
```

> The four actions `CARD_INFO` / `CREATE_PHYSICAL_CARD` / `ACTIVE_PHYSICAL_CARD` / `UPDATE_PIN` **must carry `cardId`**, and the user must already hold a card — otherwise the request is rejected.

**Freeze / unfreeze**

```http theme={null}
POST /card/v2/block
```

```json theme={null}
{
  "externalUserId": "4dc3e854-xxxx-xxxx-xxxx-91a98b3d832c",
  "cardId": "1234567890123456789",
  "block": true
}
```

> `block = true` freezes, `false` unfreezes. In the response, `data` is a boolean indicating whether this freeze or unfreeze operation succeeded (`true` = success / `false` = failure).

<Warning>
  **Unfreezing requires second-factor verification**: when `block = false` you must also supply `smsCode` or `emailCode`. Issue it first with the Step 1 endpoint using `behavioral = CARD_UNFROZEN`. Freezing does not require it.
</Warning>

> Physical card application and activation, PIN reset and the remaining capabilities are covered in [Managing Cards](../how-to-use/managing-cards/overview).

### Step 6: Fund the account and simulate a purchase

**Deposit — production**

Fetch the user's crypto deposit address and transfer on-chain to it:

```http theme={null}
GET /crypto/v2/deposit-address?externalUserId=4dc3e854-...&network=<chain>&coin=<coin>
```

> All three parameters are required. v2 returns `network` / `address` / `coin` / `fxRate` / `status`.

<Warning>
  This endpoint does not return the minimum deposit amount or the required on-chain confirmation count. **If you need to tell the cardholder either of those, read them from the `network-coin` configuration (`minConfirm` and related fields) or maintain that configuration yourself.** The supported chain and currency matrix is in [Crypto Deposit](../how-to-use/virtual-accounts/crypto-deposit).
</Warning>

**Deposit — sandbox**

In the sandbox, use the simulator to credit the user's account directly, which keeps integration testing simple:

```http theme={null}
POST /simulation/v1/deposit
```

```json theme={null}
{
  "chain": "<chain>",
  "currency": "USDT",
  "amount": 100.0,
  "address": "<the user's deposit address>"
}
```

> This simulates funds arriving at the user's crypto deposit address. Take `address` from the previous step.

**Confirm the balance**

```http theme={null}
GET /user-asset/v1/balance?externalUserId=4dc3e854-xxxx-xxxx-xxxx-91a98b3d832c
```

```json theme={null}
{
  "code": "SYS_SUCCESS",
  "message": null,
  "messageDetail": null,
  "data": [
    { "asset": "USDT", "logo": "", "network": "", "free": 100.0, "freeze": 0.0, "total": 100.0 }
  ]
}
```

> Balance fields: `free` = available, `freeze` = frozen, `total` = total (these are the actual API field names). The response is an **array** — iterate by `asset`.

**Simulate a purchase**

Use the simulator to raise a card authorization and verify your Webhook handling and limit changes:

```http theme={null}
POST /simulation/v2/fund-auth
```

```json theme={null}
{
  "externalUserId": "4dc3e854-xxxx-xxxx-xxxx-91a98b3d832c",
  "cardId": "1234567890123456789",
  "authType": "EXPEND",
  "amount": 10.0,
  "currency": "USD"
}
```

```json theme={null}
{
  "code": "SYS_SUCCESS",
  "message": null,
  "messageDetail": null,
  "data": { "approved": true, "errorCode": "" }
}
```

> Prefer **v2** — it locates the card by `cardId`, which chains directly off the `cardId` from Step 4. v1 locates the card by `cardMantissa` (last four digits) and is kept for compatibility only.
> `authType` enum: `EXPEND` (purchase) | `REFUND` (refund) | `REVERSAL` (purchase reversal).
> Authorization is decided **inside DCS** against the user's available assets; `approved` is the result. Sandbox simulation produces real transaction records, triggers Webhooks and updates balances, but involves no real funds. Full scenarios in [Simulating Transactions](../how-to-use/simulating-transactions/overview).

Your first working card is now issued, funded, and has completed a simulated purchase.

***

## Next steps

* **Full KYC flow and state machine** → [Compliance](../basic-concepts/compliance-kyc-flow) — the complete swimlane for application, review, POA supplementation and AML handling
* **All guidance-page actions and parameters** → [H5 KYC / Card Onboarding Guidance Page](../integration-resources/h5-kyc-guidance) — the seven actions, language and theme customization, link validity
* **Physical cards, replacement, PIN reset** → [Managing Cards](../how-to-use/managing-cards/overview) — full card lifecycle management
* **Crypto deposit chain/currency matrix and on-chain withdrawal** → [Virtual Accounts](../how-to-use/virtual-accounts/crypto-deposit) — multi-chain deposit and withdrawal
* **All sandbox simulation scenarios** → [Simulating Transactions](../how-to-use/simulating-transactions/overview) — purchase, refund, reversal and every other transaction type
* **Auth headers and the HMAC-SHA256 signature rule** → [Authentication Guide](../integration-resources/overview) — required headers, concatenation formula, replay protection and code samples
* **Webhook & WebSocket real-time push** → [Webhook & WebSocket](../integration-resources/webhook-websocket) — payload structures for all 9 event types, retry and idempotency handling
