Sign in
Payments and Payouts/Payout API

Payout API

Send withdrawals from your merchant balance to any blockchain address.

The Payout API lets you programmatically withdraw funds from your merchant balance to any blockchain address.

For all Payout endpoints, you must use a separate Payout API key to generate the sign signature. This key is different from your regular API key and must be generated in your project settings.

Create payout

Creates a withdrawal request from your merchant balance.

POST/v1/payout

Request parameters

FieldTypeRequiredDescription
currencystringyesWithdrawal currency (see References)
networkstringyesNetwork code (see References)
amountstringyesWithdrawal amount
to_addressstringyesRecipient blockchain address
order_idstringnoIdempotency key — unique within a project. A repeated POST with the same order_id does not create a new payout — the existing one is returned instead
url_callbackstringnoURL for payout webhooks. Omit to disable webhooks for this payout
memostring | nullnoDestination tag / memo. Currently used only by TON and SOL networks; max 255 chars
from_currencystringnoSource balance to debit and auto-convert into currency at the moment of payout. Lets you pay out in volatile assets (BTC, ETH, …) while keeping your balance in a stable coin like USDT — you don't have to hold the volatile crypto yourself. Pass "USDT" to debit the USDT balance
fee_optionstringnoHow fees are charged. deduct (default) — network + platform fees subtracted from amount, the recipient gets amount - fees. add — fees added on top, the merchant is debited amount + fees, the recipient receives exactly amount

Idempotency. Within a project, a payout is unique by order_id. Re-sending the same POST with the same order_id is safe — the API returns the existing payout instead of creating a duplicate. Always pass an order_id for production payouts.

Request examples

Shell
curl -X POST https://api.2328.io/api/v1/payout \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyShop/1.0 (+https://myshop.example)" \
  -H "project: YOUR_PROJECT_UUID" \
  -H "sign: YOUR_HMAC_SIGNATURE" \
  -d '{"currency":"TRX","network":"TRX-TRC20","amount":"1.00","to_address":"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t","order_id":"9ed25264-8be4-439f-acf5-2a8732538d27","url_callback":"https://your-site.com/webhook/payout","memo":null,"fee_option":"deduct"}'

Response example

JSON
{
  "state": 0,
  "result": {
    "uuid": "019dea62-1727-72aa-ac2c-eaf2ade193ef",
    "order_id": "9ed25264-8be4-439f-acf5-2a8732538d27",
    "status": "pending",
    "currency": "TRX",
    "network": "TRX-TRC20",
    "amount": "1.00",
    "merchant_amount": "1",
    "network_amount": "0.89",
    "amount_usd": "0.33",
    "to_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
    "memo": null,
    "txid": null,
    "block_number": null,
    "error_type": null,
    "created_at": "2026-05-02T23:29:50+03:00",
    "updated_at": "2026-05-02T23:29:50+03:00"
  }
}

Fees. Default fee_option: deduct — network + platform fees are subtracted from amount (recipient gets amount - fees). Pass fee_option: add to charge fees on top — the recipient gets exactly amount and the merchant is debited amount + fees.

Calculate payout

Estimates withdrawal amounts and fees without creating a payout or debiting your balance. Use it to show users the exact amount they will receive (or pay) before they confirm.

POST/v1/payout/calc

Request parameters

Identical to Create payout — same fields, same signing. order_id, url_callback, to_address and memo are accepted but ignored: no payout is persisted and no callbacks are sent.

Request example

Shell
curl -X POST https://api.2328.io/api/v1/payout/calc \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyShop/1.0 (+https://myshop.example)" \
  -H "project: YOUR_PROJECT_UUID" \
  -H "sign: YOUR_HMAC_SIGNATURE" \
  -d '{"currency":"USDT","network":"TRX-TRC20","amount":"100","fee_option":"add"}'

Response example

JSON
{
  "state": 0,
  "result": {
    "currency": "USDT",
    "network": "TRX-TRC20",
    "amount": "100",
    "fee_option": "add",
    "merchant_amount": "103.00000000",
    "network_amount": "100",
    "total_fee": "3.00000000",
    "total_fee_usd": "3.00000000"
  }
}

Preview only. This endpoint is read-only — no balance is debited and no payout record is created. Call it as often as you need to render fee breakdowns in your UI.

Payout status

Get the status of a payout request.

GET/v1/payout/status/{uuid}

Path parameters

FieldTypeRequiredDescription
uuidstringyesPayout UUID (from result.uuid on creation)

Response example

JSON
{
  "state": 0,
  "result": {
    "uuid": "019dff1f-0dbd-7277-8d45-271e7775388f",
    "order_id": "4dfdcc84402b1185b71cbe399321533e",
    "status": "completed",
    "currency": "TRX",
    "network": "TRX-TRC20",
    "amount": "3.00",
    "merchant_amount": "3.00",
    "network_amount": "3.00",
    "amount_usd": "1.04",
    "to_address": "THauRv5tcucQRohXg8NiyGTk16DX1XQG5x",
    "memo": null,
    "txid": "9242e533703704ef3eaba840f70b4a26333e72c943377ee375fea17badb53def",
    "block_number": null,
    "error_type": null,
    "created_at": "2026-05-07T00:08:38+03:00",
    "updated_at": "2026-05-07T00:08:54+03:00",
    "from_currency": "USDT",
    "debited_amount": "1.050735",
    "debited_currency": "USDT"
  }
}

For this GET request the signature is computed from an empty body: hash_hmac('sha256', base64_encode(''), $apiKey)

Response fields

Fields returned in result from POST /v1/payout and GET /v1/payout/status/{uuid}:

FieldTypeDescription
uuidstringPayout UUID assigned by the system
order_idstringYour internal payout identifier (unique within the project)
statusstringCurrent payout status (see below)
currencystringWithdrawal currency
networkstringNetwork code
amountstringWithdrawal amount as requested
merchant_amountstringAmount debited from the merchant balance
network_amountstringAmount actually sent on-chain (after network + platform fees)
amount_usdstringUSD equivalent of the withdrawal amount
to_addressstringRecipient blockchain address
memostring | nullDestination tag / memo (TON, SOL). null otherwise
txidstring | nullBlockchain transaction hash. null until the transaction is sent
block_numberint | nullBlock number where the transaction was included. null until included
error_typestring | nullReason for failure when status = failed (see Error types below). null otherwise
created_atstring (ISO 8601)Payout creation time
updated_atstring (ISO 8601)Last status change time
from_currencystring | nullSource balance the payout was debited from when auto-conversion was used (e.g. USDT for a BTC payout). null if no conversion happened
debited_amountstring | nullAmount actually debited from the source balance after conversion. Present only when auto-conversion is used
debited_currencystring | nullCurrency of debited_amount — the balance from which funds were debited

Payout statuses

The status field can take the following values:

StatusDescription
pendingCreated, awaiting processing
completedCompleted successfully — txid is set
failedSending error — see error_type
cancelledCancelled

Error types

When status = failed, the error_type field describes why:

CodeDescription
aml_riskPayout blocked by AML risk checks (recipient address flagged as high-risk)

Webhook notifications

When a payout's status changes, the system sends a POST webhook to the url_callback URL passed when the payout was created. If url_callback was not provided, no webhooks are sent for that payout.

  • Method: POST
  • Content-Type: application/json
  • Signature: sign field in the request body, computed with the Payout API key (the same key used to sign payout requests).

The payload mirrors the result object from GET /v1/payout/status/{uuid} plus a sign field for verification.

Payload

JSON
{
  "uuid": "019dff1f-0dbd-7277-8d45-271e7775388f",
  "order_id": "4dfdcc84402b1185b71cbe399321533e",
  "status": "completed",
  "currency": "TRX",
  "network": "TRX-TRC20",
  "amount": "3.00",
  "merchant_amount": "3.00",
  "network_amount": "3.00",
  "amount_usd": "1.04",
  "to_address": "THauRv5tcucQRohXg8NiyGTk16DX1XQG5x",
  "memo": null,
  "txid": "9242e533703704ef3eaba840f70b4a26333e72c943377ee375fea17badb53def",
  "block_number": null,
  "error_type": null,
  "created_at": "2026-05-07T00:08:38+03:00",
  "updated_at": "2026-05-07T00:08:54+03:00",
  "from_currency": "USDT",
  "debited_amount": "1.050735",
  "debited_currency": "USDT",
  "sign": "925ad7bf3d6841864101f7cc2c7e30652e70a06cdb04dbe07a0129480000ce4a"
}

Verifying the signature. Use the same algorithm as for payment webhooks, but sign with your Payout API key instead of the regular API key. Strip the sign field, JSON-encode the remaining payload, Base64-encode it, then compute hash_hmac('sha256', $base64, $payoutApiKey) and compare with the received sign.