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

# Crypto Withdrawals

> The endpoint reference for on-chain crypto withdrawals under the DeCard-Managed model: confirm the chain, token, and withdrawal switch (network-coin) → submit the on-chain withdrawal (/crypto/v1/withdraw-apply, with SMS verification) → track crediting through the transaction records. Covers SMS verification, address tags, and fees.

## Overview

Crypto withdrawals let your users move the available balance (`availableBalance`) in their account held in custody by DCS **out over a blockchain network to an external on-chain address of their choice**. As a licensed issuer with its own BINs, DCS handles the balance deduction, SMS security check, on-chain transfer, and status tracking for you, turning "available balance → funds credited on-chain" into a single path.

This is the reverse of a deposit: **the user submits a withdrawal → the platform validates it (including the SMS code) and deducts the available balance → the funds are sent on-chain to the destination address → crediting is tracked through the transaction records**.

Three core steps:

1. Call `GET /crypto/v1/network-coin` to confirm that the target chain and token have **withdrawals enabled** (`withdrawEnable`), and read the withdrawal fee and limits (`withdrawFee` / `withdrawMin` / `withdrawMax`) plus whether an address tag is required (`withdrawIsTag`).
2. Have the user obtain an **SMS verification code** (`smsCode`, see [Prerequisites](#prerequisites)), then call `POST /crypto/v1/withdraw-apply` to submit the withdrawal.
3. Call `POST /user-asset/v1/transactions` to query crypto balance movements and follow the on-chain status and `txHash` of this withdrawal.

***

## Prerequisites

Confirm the following before calling this endpoint:

1. **The user is onboarded and has passed KYC**: the user has been assigned an `externalUserId`. See [Managing users](../managing-users/overview).
2. **Withdrawals are enabled for the target chain and token**: query `GET /crypto/v1/network-coin` first, confirm that `withdrawEnable = true` for that `network` plus `coin`, and use `withdrawIsTag` to decide whether `addressTag` is mandatory.
3. **The available balance is sufficient**: the user's `availableBalance` must be greater than or equal to the withdrawal amount plus the fee. For the balance model, see [Account and asset model · Ledgering](../../basic-concepts/ledgering-system).
4. **SMS verification is complete**: a withdrawal moves money out, so it is treated as high risk and requires an SMS verification code `smsCode` (issued via `POST /captcha/v1/send-mobile-code`).
5. **Compliance groundwork**: where the Travel Rule applies, its requirements must be satisfied first. See [Travel Rule](./travel-rule).

## API flow

<Frame>
  <img className="block dark:hidden" src="https://mintcdn.com/dcs-0bf7a937/oUoeUKtwWWK2o-Li/imgs/en/diagrams/va-withdraw-flow-light.svg?fit=max&auto=format&n=oUoeUKtwWWK2o-Li&q=85&s=647b8fa8438de6b75676ed2645fae2f0" alt="Crypto withdrawal (off-ramp) flow" width="622" height="594" data-path="imgs/en/diagrams/va-withdraw-flow-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/dcs-0bf7a937/oUoeUKtwWWK2o-Li/imgs/en/diagrams/va-withdraw-flow-dark.svg?fit=max&auto=format&n=oUoeUKtwWWK2o-Li&q=85&s=404209584b2b0db5b1ec77c2d6a57c4b" alt="Crypto withdrawal (off-ramp) flow" width="622" height="594" data-path="imgs/en/diagrams/va-withdraw-flow-dark.svg" />
</Frame>

## Submitting a withdrawal

**`POST /crypto/v1/withdraw-apply`**

### Request fields

| Field            | Type   | Required    | Description                                                                                               |
| :--------------- | :----- | :---------- | :-------------------------------------------------------------------------------------------------------- |
| `externalUserId` | string | Yes         | External user ID                                                                                          |
| `smsCode`        | string | Yes         | SMS verification code, issued via `POST /captcha/v1/send-mobile-code`                                     |
| `network`        | string | Yes         | Withdrawal chain, for example `TRON` or `ETH` (accepted values follow the `network-coin` response)        |
| `coin`           | string | Yes         | Withdrawal token, for example `USDT` or `USDC` (accepted values follow the `network-coin` response)       |
| `name`           | string | No          | Label for the withdrawal address or beneficiary name (used for address notes and whitelists)              |
| `address`        | string | Yes         | Destination on-chain withdrawal address                                                                   |
| `addressTag`     | string | Conditional | Address tag or memo; required when `withdrawIsTag = true` in `network-coin`                               |
| `amount`         | number | Yes         | Withdrawal amount (must be greater than 0; limits follow `withdrawMin` / `withdrawMax` in `network-coin`) |
| `fee`            | number | Yes         | Withdrawal fee (follows `withdrawFee` in `network-coin`)                                                  |

> **Amount precision**: `amount` and `fee` in the request body are numbers. When storing and displaying them, mind the token's on-chain precision so that no precision is lost.

### Request example (redacted)

```bash theme={null}
curl -X POST "{{dicard-server}}/crypto/v1/withdraw-apply" \
  -H "Content-Type: application/json" \
  -d '{
    "externalUserId": "user_demo_001",
    "smsCode": "000000",
    "network": "TRON",
    "coin": "USDT",
    "name": "my-wallet",
    "address": "T-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "addressTag": "",
    "amount": 100.0,
    "fee": 1.0
  }'
```

> **Authentication**: the example above omits the authentication headers to keep the focus on business fields. Real calls must carry `X-DAPI-API-KEY`, `X-DAPI-SIGN`, `X-DAPI-TIMESTAMP`, and `X-DAPI-NONCE` (HMAC-SHA256 signature). The rules are documented once in the [Authentication guide](../../integration-resources/overview) and are not repeated here.

<Warning>
  All example values are placeholders. **Never** put a real on-chain address, `externalUserId`, `smsCode`, amount, cardholder personal data, or API key/secret into a request or a log. On-chain addresses and user identifiers are sensitive and must be redacted.
</Warning>

### Response

The response uses the standard envelope `{ code, message, messageDetail, data }` (**no `success` boolean**). On success, `code = SYS_SUCCESS`, meaning the withdrawal request has been accepted.

> Base your business logic on the actual status in the transaction records. Do not treat `code = SYS_SUCCESS` in the envelope as proof that the withdrawal has been credited; `code` only confirms that the request was accepted, and the on-chain transfer happens asynchronously.

## Tracking withdrawal progress

Once an on-chain withdrawal is accepted, track it with **`POST /user-asset/v1/transactions`** (query crypto balance movements) to read the on-chain status and the transaction hash `txHash`; for a single record, use `POST /user-asset/v1/transaction-detail`. For field definitions, see [User Balance](../managing-transactions/user-balance) and [Reporting field descriptions](../managing-transactions/reporting-field-descriptions).

## Status and time to credit

* **Acceptance is not the same as crediting**: `code = SYS_SUCCESS` in the envelope only means DCS accepted the request and deducted the balance; the funds are only really at the destination once confirmed on-chain.
* **Time to credit depends on on-chain confirmation speed**: block times and confirmation counts differ by chain, so rely on the confirmation configuration in `network-coin` and the actual state of the target network.

## Error handling

| Situation                                   | Recommended handling                                                                                                                                                                                                                                                                  |
| :------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Envelope `code` is not `SYS_SUCCESS`        | The request was not accepted: use `message` to check the inputs (whether `smsCode` is valid or expired, whether withdrawals are enabled for `network`/`coin`, whether `address`/`addressTag` are valid, whether `amount` is within the limits, and whether the balance is sufficient) |
| SMS code invalid or expired                 | Have the user request a new `smsCode` (`send-mobile-code`) and retry                                                                                                                                                                                                                  |
| Withdrawals disabled for the chain or token | `network-coin` returns `withdrawEnable = false`: tell the user withdrawals are unavailable and offer a chain or token that is enabled                                                                                                                                                 |
| Tag required but missing                    | Chains with `withdrawIsTag = true` must carry `addressTag`; otherwise the transfer may fail to credit or the funds may be lost                                                                                                                                                        |
| Insufficient balance                        | Available balance is less than the withdrawal amount plus the fee: prompt the user to deposit more or lower the amount                                                                                                                                                                |

## Best practices

1. **Query `network-coin` before every withdrawal**: drive your front-end validation from `withdrawEnable`, `withdrawFee`, `withdrawMin`, `withdrawMax`, and `withdrawIsTag` to cut down invalid requests and failed credits.
2. **Enforce address and tag validation**: validate the address format and tag against the chain's rules while the user is still filling the form, and make the tag mandatory when `withdrawIsTag = true`. A wrong address or a missing tag can make the funds unrecoverable.
3. **Never skip SMS verification**: withdrawals move money out, so always require the second factor via `smsCode`.
4. **Judge crediting by the on-chain status**: acceptance is not crediting. Before telling the user the funds have arrived, check the on-chain confirmation status in the transaction records.
5. **Minimise PII**: on-chain addresses and user identifiers are sensitive. Redact them in logs and retain no more than compliance requires.

## Next steps

* [Crypto Deposits](./crypto-deposit) — the opposite direction (on-chain deposit into the available balance), sharing the `network-coin` chain and token matrix with this page
* [Travel Rule](./travel-rule) — the compliance groundwork that withdrawals depend on
* [User Balance](../managing-transactions/user-balance) — semantics of the `free` / `freeze` / `total` balances and how to query balance movements
* [Account and asset model · Ledgering](../../basic-concepts/ledgering-system) — the `availableBalance` / `frozenBalance` model
* [Authentication guide](../../integration-resources/overview) — the shared reference for HMAC-SHA256 signature headers
