> ## 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. Nothing is sent until your server answers the [approval call](https://assetpay.gg/docs/guides/crypto.md#the-approval-call) with `{ "action": "approve" }`.

**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`, `SOL` or `TRON` |
| `token` | string | Yes | `USDT` or `USDC`. Only `USDT` on `TRON` |
| `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](https://assetpay.gg/docs/guides/crypto.md#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](https://assetpay.gg/docs/guides/crypto.md#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) |
| 2527 | `ONCHAIN_CLIENT_WITHDRAW_NO_CALLBACK` | No active callback URL or no API secret, so the cashout cannot be approved (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 |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.