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

# Crypto Withdraw

> Cash out USDT or USDC from the merchant balance to an address the end user chose. Your server approves it first.

Starts a cashout for the user the client token belongs to. Because the request comes from the end user, AssetPay asks your server before anything is sent: see [The approval call](/docs/guides/crypto#the-approval-call).

**Authentication:** Client token (`Authorization` header)

## Request

```http theme={null}
POST https://api.assetpay.gg/client/crypto/withdraw
Content-Type: application/json
Authorization: CLIENT_TOKEN

{
  "chain": "BSC",
  "token": "USDT",
  "address": "0x9c1f0e2d3b4a59687766554433221100ffeeddcc",
  "amountCents": 2500,
  "requestId": "cashout_7f3a91c2"
}
```

| Field         | Type   | Required | Description                                                                                                        |
| ------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `chain`       | string | Yes      | `ETH`, `BSC` or `SOL`                                                                                              |
| `token`       | string | Yes      | `USDT` or `USDC`                                                                                                   |
| `address`     | string | Yes      | The user's wallet address on that chain (26-64 chars)                                                              |
| `amountCents` | number | Yes      | Amount debited from the merchant balance, in USD cents. The network fee comes out of it                            |
| `requestId`   | string | Yes      | Your idempotency key, 8-64 chars of `A-Z a-z 0-9 _ -`. Repeating it for the same user returns the first withdrawal |

## Response

`201 Created`

```json theme={null}
{
  "requestId": "...",
  "success": true,
  "data": {
    "id": "b0f3c1e2-...",
    "type": "crypto_withdraw",
    "status": "awaiting_approval",
    "steamId": "76561198012345678",
    "chain": "BSC",
    "token": "USDT",
    "amount": "25.00",
    "amountCents": 2500,
    "feeCents": 0,
    "receiveCents": 2500,
    "cryptoAmount": "25",
    "address": "0x9c1f0e2d3b4a59687766554433221100ffeeddcc",
    "txHash": null,
    "reason": null,
    "createdAt": "2026-09-20T10:00:00.000Z",
    "updatedAt": "2026-09-20T10:00:00.000Z",
    "completedAt": null
  }
}
```

| Field          | Type           | Description                                                                            |
| -------------- | -------------- | -------------------------------------------------------------------------------------- |
| `status`       | string         | Starts as `awaiting_approval`. See [Cashout statuses](/docs/guides/crypto#cashout-statuses) |
| `amountCents`  | number         | Debited from the merchant balance and held at once                                     |
| `feeCents`     | number         | Flat network fee of the chain, taken out of `amountCents`                              |
| `receiveCents` | number         | What the user receives: `amountCents - feeCents`                                       |
| `cryptoAmount` | string         | Token amount that is sent                                                              |
| `txHash`       | string \| null | Set once the payout is `sent`                                                          |
| `reason`       | string \| null | Your refusal reason, when your server refused it                                       |

Every later status change arrives as a `{ "withdrawal": ... }` [webhook](/docs/guides/crypto#webhooks). On `rejected`, `failed` and `cancelled` the full amount is back in the merchant balance.

## Rate Limits

Shared across the crypto withdraw endpoints of all your users.

| Merchant Status | Limit              |
| --------------- | ------------------ |
| Verified        | 120 requests / min |
| Unverified      | 5 requests / min   |

## Errors

| Code | Key                                | When                                                                                                                   |
| ---- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| 2524 | `ONCHAIN_CLIENT_WITHDRAW_DISABLED` | Cashouts are switched off (HTTP 503)                                                                                   |
| 2522 | `ONCHAIN_MERCHANT_NOT_VERIFIED`    | The merchant is not verified (HTTP 403)                                                                                |
| 2525 | `ONCHAIN_CLIENT_WITHDRAW_LIMIT`    | A daily limit is reached. `details.scope` is `user` or `merchant`, `details.remainingCents` is what is left (HTTP 403) |
| 2526 | `ONCHAIN_CLIENT_WITHDRAW_BUSY`     | Another cashout of this merchant is being created, retry in a moment (HTTP 409)                                        |
| 2508 | `ONCHAIN_WITHDRAW_BELOW_MIN`       | The user would receive less than \$1.00 after the fee (HTTP 400)                                                       |
| 2509 | `ONCHAIN_WITHDRAW_INVALID_ADDRESS` | The address is not valid for this chain (HTTP 400)                                                                     |
| 2503 | `ONCHAIN_UNSUPPORTED_TOKEN`        | The token is not supported on this chain (HTTP 400)                                                                    |
| 2501 | `ONCHAIN_CHAIN_PAUSED`             | The chain is paused (HTTP 503)                                                                                         |
| 2512 | `ONCHAIN_INSUFFICIENT_HOT_BALANCE` | AssetPay cannot cover this payout right now, retry later (HTTP 503)                                                    |
| 1600 | `INSUFFICIENT_BALANCE`             | The merchant balance does not cover the cashout (HTTP 400)                                                             |
| 1005 | `RATE_LIMITED`                     | Rate limit exceeded                                                                                                    |
