# Crypto Payouts

> Send USDT payouts to any crypto wallet.

Section: Build with AI
Source: https://coincircuit.io/docs/guides/crypto-payouts/
Interactive version: https://coincircuit.io/api-reference?tab=guides&guide=crypto-payouts

Tags: Payouts, Balance, Webhooks

Paste this prompt into Claude, ChatGPT, or any AI tool. It works best with the CoinCircuit MCP server connected: https://mcp.coincircuit.io

---

You are helping me add crypto payouts to my existing application using CoinCircuit. I have the CoinCircuit MCP server connected.

**If you have access to the CoinCircuit MCP server, call these tools for the most accurate and detailed schema outputs:**
- Call `get_endpoint` with method `post` and path `/api/v1/recipients` for the recipient creation schema.
- Call `get_endpoint` with method `post` and path `/api/v1/payouts` for the payout creation schema.
- Call `get_schema` with name `PayoutSuccessWebhookDto` for the payout.success webhook payload.
- Call `get_schema` with name `PayoutFailedWebhookDto` for the payout.failed webhook payload.

Use the live MCP data as your source of truth. The details below are a guide, but if the MCP returns something different, trust the MCP.

## API Basics

- **Base URL:** `https://api.coincircuit.io`
- **Auth:** `x-api-key` header on every request.

## Project Context

I have an application that needs to pay users in crypto (USDT) from my CoinCircuit merchant balance to their wallet addresses. Use cases: vendor payments, creator payouts, rewards, or any disbursement to external wallets.

## What I Need You to Implement

### 1. Save a payout recipient

Before you can send a payout, the recipient wallet address must be saved.

**Endpoint:** `POST /api/v1/recipients`
*(Call `get_endpoint` with method `post`, path `/api/v1/recipients` in the CoinCircuit MCP for the full request/response schema.)*

**Required fields:**
- `type` (string) - `"crypto_address"` for a wallet, or `"ngn_bank_account"` for a bank account
- `details` (object) - for `crypto_address`: `{ chain, address }`. `chain` is one of `"bsc"`, `"tron"`, `"solana"`, `"base"`, or `"ethereum"`; `address` is the wallet address.

**Optional fields:**
- `label` (string) - friendly name, e.g. "Alice's BSC wallet"
- `isDefault` (boolean) - make this the default recipient for its type

**Example request body:**
```json
{
  "type": "crypto_address",
  "label": "Alice's BSC wallet",
  "details": { "chain": "bsc", "address": "0xRecipientAddress" }
}
```

**Response (201):** Returns the saved recipient with an `id` (UUID). You'll use this `id` as the `recipientId` when creating payouts. If a matching recipient already exists, the existing one is returned instead of a duplicate.

**Errors:** 400 if the details are invalid or the type is unsupported.

### 2. Create crypto payout

**Endpoint:** `POST /api/v1/payouts`
*(Call `get_endpoint` with method `post`, path `/api/v1/payouts`, section `request` in the CoinCircuit MCP for the exact request body with all field validations.)*

**Required fields:**
- `method` (string) - `"crypto"` (or `"fiat"` for bank transfers)
- `currency` (string) - `"USDT"` or `"NGN"`
- `amount` (string) - amount to send as a string, e.g. `"100.00"`
- `recipientId` (string, UUID) - the saved crypto address ID from step 1

**Optional fields:**
- `narration` (string) - description, e.g. "Creator payout - March"
- `reference` (string) - your unique idempotency reference. **Duplicate references are rejected.** If omitted, CoinCircuit generates one.

**Example request body:**
```json
{
  "method": "crypto",
  "currency": "USDT",
  "amount": "100.00",
  "recipientId": "addr_123456789_abcdef",
  "narration": "Creator payout - March 2026",
  "reference": "PAYOUT-MARCH-ALICE-001"
}
```

**Response (201):** Returns the payout object:
- `data.id` - payout UUID
- `data.status` - starts as `"pending"`, transitions to `"processing"`, `"success"`, or `"failed"`
- `data.amount` - amount the recipient gets
- `data.fee` - fee charged
- `data.total` - total debited from your balance (amount + fee)
- `data.currency` - `"USDT"`
- `data.reference` - your reference or auto-generated
- `data.txHash` - blockchain transaction hash (populated after success)
- `data.recipient` - object with `type: "crypto_address"` and `details: { chain, address }`
- `data.conversion` - if cross-currency, includes `from`, `to`, `rate`, `convertedAmount`. Null if same currency.
- `data.failureReason` - populated if failed

*(Call `get_endpoint` with method `post`, path `/api/v1/payouts`, section `success` in the CoinCircuit MCP for the complete response schema.)*

**Errors:** 400 (validation error, insufficient balance, daily limit exceeded), 404 (recipient address not found)

### 3. Track payout status via webhooks

Payouts are async. The API returns immediately with `"pending"` status. Wait for webhooks:

*(Call `get_schema` with name `PayoutSuccessWebhookDto` in the CoinCircuit MCP for the full payout.success webhook payload. Call `get_schema` with name `PayoutFailedWebhookDto` for the failure payload.)*

**Payout events** (envelope: `{ event: string, data: { payout: PayoutObject } }`):
- `payout.created` - payout submitted and processing
- `payout.success` - funds delivered on-chain. `data.payout.txHash` has the transaction hash, `data.payout.completedAt` has the timestamp.
- `payout.failed` - payout failed. `data.payout.failureReason` explains why. Funds are returned to your balance.

## Constraints

- `amount` is a string (e.g. `"100.00"`)
- Duplicate `reference` values are rejected. Use unique references for idempotency.
- Crypto payouts support USDT (on Tron, BSC) and USDC (on Base).
- Payouts are async. Never assume success from the API response. Always wait for webhooks.
- Fiat payouts go only to the merchant's own bank account. Use crypto payouts for third-party disbursements.
