# Withdrawing crypto through the API: quote, idempotency and approval

> A withdrawal is the one call that moves money out. The API makes it hard to do twice by accident and impossible for a leaked key to approve its own request, as long as you use it the way it was designed.

Updated 2026-10-10 · Payaries · https://www.payaries.com/guides/crypto-withdrawals-api

## 1. Check what is withdrawable

A confirmed deposit is credited immediately, but it is withdrawable only once the sweep to your Payaries wallet settles. `GET /v1/merchants/me/balance` separates the two.

| Field | Meaning |
|---|---|
| `available` | Withdrawable now: credited and physically on your Payaries wallet |
| `pendingSweep` | Credited, but the sweep hasn't confirmed. A withdrawal against it gets `409` |
| `pending` | Reserved by a withdrawal already in flight |
| `frozen` | An admin hold, normally zero |

## 2. Quote it

`GET /v1/withdrawals/quote` applies the same pricing and checks as the real request and charges nothing.

**Quote**

```
GET /v1/withdrawals/quote?chainId=tron-mainnet&asset=USDT_TRC20&amount=1000

{
  "chainId": "tron-mainnet", "asset": "USDT_TRC20", "requestedAmount": "1000",
  "platformFee": "5", "platformFeePaidFrom": "transaction",
  "networkFee": "6.28125", "networkFeeIncluded": false, "gasSponsored": true,
  "netAmount": "988.71875"
}
```

`networkFeeIncluded: true` means gas is covered and shown as included. `gasSponsored: false` means that chain's [gas station](https://www.payaries.com/blog/gas-station-network-fees) is off and your own Payaries wallet must hold native currency to pay gas. A too-small amount is refused here with `BELOW_MINIMUM_WITHDRAWAL` or `AMOUNT_TOO_SMALL_FOR_NETWORK_FEE`.

## 3. Create it, idempotently

With an API key the `Idempotency-Key` header is **required** (8 to 128 characters of `A-Za-z0-9_-:.`). Generate one per logical withdrawal, store it with your payout record, and reuse it on every retry. Withdraw to any address; it needn't be pre-registered.

**Request**

```
POST /v1/withdrawals
X-Api-Key: <your api key>
X-Timestamp: <unix ms>
X-Signature: <hex HMAC-SHA256>
Idempotency-Key: wd_2026_10_10_0001

{ "chainId": "tron-mainnet", "asset": "USDT_TRC20", "destinationAddress": "T...", "amount": "50.00" }
```

**Response**

```
201 Created
{
  "id": "670...", "chainId": "tron-mainnet", "asset": "USDT_TRC20", "destinationAddress": "T...",
  "requestedAmount": "50.00", "feeAmount": "0.50", "networkFeeAmount": "0", "netAmount": "49.50",
  "status": "processing", "autoApproved": true, "createdVia": "api", "createdAt": "..."
}
```

- The same key with the same parameters returns the **original** withdrawal (`200`), not a new one.
- The same key with different parameters is `409`.
- A retry is re-signed with a fresh timestamp but keeps the same `Idempotency-Key`.
- `requestedAmount` is reserved from `available` only; `netAmount`, after fees, is what is sent.

## 4. Approval: who decides

| Created via | Approval |
|---|---|
| A signed-in dashboard session | Executes at once. It requires 2FA on the account and a fresh single-use code, which is the approval |
| An API key, below the auto-approve threshold for that chain and asset | `autoApproved: true`, executes immediately |
| An API key, at or above the threshold | `status: pending_approval` until a person approves or rejects it in the dashboard |

Approving, rejecting and setting the threshold are dashboard-only; an API key gets `403`. If an AI agent or any automated process holds a key, set a low threshold so its withdrawals wait for you, or give it no withdrawal tool at all. Chain and asset pairs with no threshold of your own use the platform's default ceiling.

## 5. Follow it

The lifecycle is `pending_approval` → `processing` → `broadcast` → `confirmed`, or → `rejected`, `cancelled` or `failed`. A `withdrawal.status_changed` webhook fires on every transition. Withdrawal webhooks have no stored status or resend endpoint, so if you suspect a miss, poll `GET /v1/withdrawals/:id`.

- `POST /v1/withdrawals/:id/cancel` works only while `pending_approval`, and releases the reservation.
- If the chain's gas station is paused, a withdrawal waits in `processing` for up to 24 hours, then fails and releases your reservation.
- Bitcoin withdrawals aren't supported yet; `btc-mainnet` is refused with `400` before anything is reserved.
- Transactions are irreversible once confirmed. Double-check the destination address and chain.

## Questions

**Is an Idempotency-Key required to create a withdrawal?**
Yes, with an API key. The header is required, 8 to 128 characters. The same key with the same parameters returns the original withdrawal; the same key with different parameters returns 409.

**Can an API key approve its own withdrawal?**
No. Approving, rejecting and setting the auto-approve threshold are dashboard-session actions. An API key gets 403, so a leaked key can't approve its own request or raise its own ceiling.

**Why does my withdrawal return 409 Insufficient balance when my invoices are paid?**
A withdrawal is debited from the available balance only. Funds from a freshly confirmed invoice sit in pendingSweep until the sweep to your Payaries wallet settles, and a frozen amount is excluded as well.

## Try it

Create a test key and run a real invoice: [Payaries — www.payaries.com](https://www.payaries.com/signup). Payaries is a multi-chain crypto payment gateway — https://www.payaries.com

## Keep reading

- https://www.payaries.com/guides/crypto-payment-api-errors-and-retries — Crypto payment API errors, rate limits and safe retries
- https://www.payaries.com/guides/sign-crypto-payment-api-requests — How to sign crypto payment API requests (HMAC-SHA256)
- https://www.payaries.com/blog/gas-station-network-fees — Gas station: how network fees work on token payments
- https://www.payaries.com/ai-agents — Build a crypto payment integration with an AI agent, safely
- https://www.payaries.com/guides/crypto-payment-api-quickstart — Crypto payment API quickstart: from signup to your first payout
- https://www.payaries.com/developers
- https://www.payaries.com/guides/handle-underpaid-and-late-crypto-payments — Handling underpaid, late and unmatched crypto payments
