---
title: "Send USDT and USDC payouts with the API"
description: "Build stablecoin payouts from a merchant balance: choose a network, sign a withdrawal, track its final webhook, and handle timeouts without paying twice."
---

# Send USDT and USDC payouts with the API

Use this recipe to send a stablecoin payout from your CoinsSend merchant balance
to an external wallet. A successful withdrawal request starts processing; it
does not mean the recipient has already received the funds.

Your application controls who should be paid and records each payout's business
purpose. CoinsSend's public withdrawal API takes the coin, network, amount, and
destination address.

## 1. Check the recipient, network, and available funds

Enable **Allow Withdrawals** in the merchant dashboard and keep signing
credentials on your backend.

Fetch `GET /v1/coins-and-fee` and `GET /v1/get-coin-rate` to choose a currently
available coin/network pair and check operation flags, minimums, and precision.
See [supported coins and networks](/payments/supported-coins/) for the matching
rules. Do not infer availability from the token symbol alone.

For example, USDC on Arbitrum One uses `coin: "usdc"` and
`network: "arbitrum"`; USDT on TRON uses `coin: "usdt"` and
`network: "tron"`. Confirm that the recipient can receive the selected token
on the exact selected network.

Read your [merchant balances and fees](/payments/guides/merchants/) before
submitting. Static-wallet deposits have a hold, and the amount visible as a
deposit is not necessarily available to withdraw yet.

## 2. Build the withdrawal request

Send a signed `POST /v1/withdrawals` with this JSON body shape:

```json
{
  "network": "tron",
  "coin": "usdt",
  "amount": "50.75",
  "to_address": "RECIPIENT_ADDRESS_ON_THE_SELECTED_NETWORK"
}
```

Replace the destination placeholder with the intended recipient's address.
`amount` is a decimal string in the selected **cryptocurrency**, not USD.
Choose an amount that satisfies the current limits and your available balance.

Withdrawals require a timestamped HMAC-SHA256 signature:

```text
Content-Type: application/json
Merchant: YOUR_MERCHANT_ID
Timestamp: UNIX_TIME_IN_SECONDS
Sign: HMAC_SHA256(BASE64(CANONICAL_JSON_BODY) + "." + TIMESTAMP, API_KEY)
```

Use the canonical serialization in
[Authentication](/payments/guides/authentication/#timestamped-hmac-signature);
arbitrary JSON formatting is not interchangeable. The timestamp must be within
five minutes of server time. The [Node.js withdrawal example](/payments/examples/node-withdrawal/)
shows the complete signing and submission flow.

The exact request and response fields are in the
[withdrawal reference](/payments/guides/withdrawals/).

## 3. Store the accepted request and wait for the outcome

Before submitting, create a local payout record and prevent two workers from
submitting it simultaneously. After a successful response, store
`data.withdrawal_id`, `data.status`, `data.amounts`, and `data.fees`.

`pending` or `processing` means the request is in progress. Verify and
reconcile the signed `withdrawal.success` (with `data.status: "completed"`)
or `withdrawal.failed` event
before updating the final state shown to the customer. Event handling must be
idempotent so duplicate callbacks cannot duplicate ledger changes.

The recipient's net amount can differ from the requested amount according to
the merchant's fee-payer settings. Use the response and signed event's net
amount and fee breakdown; do not promise the requested amount as a guaranteed
received amount.

## 4. Handle timeouts without sending a second payout

The public withdrawal request does **not** document an `Idempotency-Key`
header or a client-supplied payout ID. A network timeout can occur after the
server accepted a withdrawal.

| Result | What your application should do |
|---|---|
| Accepted, with a withdrawal ID | Store the ID and wait for the verified final event. |
| Validation or permission error | Correct the reported fields or merchant setting before considering a new submission. |
| Timeout or connection lost after submission | Mark the attempt as uncertain and reconcile through merchant tooling or support. Do not automatically submit another payout. |
| Verified completed event | Record completion once, correlated to the withdrawal ID. |
| Verified failed event | Reconcile the reported outcome and balance before deciding whether another payout is appropriate. |
| Duplicate final event | Acknowledge the stored outcome without applying it twice. |

Keep your own business ID, recipient, amount, selected network, attempt time,
and returned withdrawal ID in the payout record. A local lock prevents
concurrent submissions, but it cannot prove whether an interrupted remote
request was accepted.

## Can I send batch or automatic recurring payouts?

The documented operation submits one withdrawal. There is no public batch
payout endpoint or recurring-payout scheduler in this contract. An application
that schedules several payouts must track and reconcile each one separately,
respect [rate limits](/payments/guides/rate-limits/), and stop automatic retries
when the result is uncertain.

## Continue the integration

- [Withdrawal fields, permission requirements, and fees](/payments/guides/withdrawals/)
- [Withdrawal webhook schemas](/payments/guides/webhooks/)
- [Request signing and signature test vectors](/payments/guides/authentication/)
- [Receive customer top-ups](/solutions/reusable-deposit-addresses/)
- [Open your merchant dashboard](https://app.coinssend.com/)
