Payouts
Two ways money leaves, and they are different products with different keys:
| Chain payout | Withdrawal | |
|---|---|---|
| Pays | Any address you name, usually your users | Your own address, verified with us in advance |
| From | Your payout wallet on that network | Your balance with us |
| Destination in the request | Yes | No, never |
| Signed | Yes | No |
| Scope | chain_payouts:write | withdrawals:write |
| Events | chain_payout.* | withdrawal.* |
Before either, estimate what it will cost.
Chain payouts
POST /v1/chain-payouts
Authorization: Bearer <your key>
Idempotency-Key: <uuid>
X-Bankcore-Request-Signature: v1,kid=...,ts=...,sig=...
Content-Type: application/json
{
"amount": {"amount": "500", "currency": "USDT_TRC20"},
"network": "tron",
"destination": {"address": "T...", "memo": null},
"reference": "payout-000123"
}
| Field | |
|---|---|
amount | The amount the destination receives. currency must be one we pay on network, and the amount may not be finer than the asset's decimals |
network | tron, bsc, ethereum, base, bitcoin or stellar |
destination.address | An address of network |
destination.memo | Stellar only. Absent or null on every other network |
reference | Yours. 1–120 printable ASCII, no space |
tenant | Optional. Your tenant slug; a mismatch with the key is 403 tenant_mismatch and nothing is recorded |
Unknown fields are refused at every level. The request must be signed.
| Network | Currencies paid |
|---|---|
tron | USDT_TRC20 |
bsc | USDT_BEP20 |
ethereum | USDT_ERC20, USDC_ERC20 |
base | USDC_BASE |
bitcoin | BTC |
stellar | XLM |
201 means accepted, not sent.
{
"id": "3f1c2a9e-...",
"status": "PENDING",
"review": null,
"amount": {"amount": "500", "currency": "USDT_TRC20"},
"network": "tron",
"destination": {"address": "T...", "memo": null},
"from_address": "T...",
"tx_hash": null,
"failure_reason": null,
"reference": "payout-000123",
"unresolved": false,
"created_at": "2026-09-26T09:02:11Z",
"settled_at": null
}
Follow it with GET /v1/chain-payouts/{payout_id} or the chain_payout.* events.
status | |
|---|---|
PENDING | Held, not yet signed. Waits while the wallet cannot pay, or for review |
SUBMITTED | Signed and broadcast. Not a payment yet |
SETTLED | A confirmed transaction proven to pay exactly amount to destination from from_address. tx_hash is set |
FAILED | Never signed, or failed on chain. The hold is returned |
UNKNOWN | Not known yet, with unresolved: true. Never a refusal and never a success: do not retry |
RECOVERY_REQUIRED | It may have left; we are establishing what happened. Treat it like UNKNOWN |
Treat any status you do not recognise as unresolved, never as failed.
review is null, "required" or "released". "required" means the payout waits for a person on our side and polling does not move it; "released" means a person let it through. Branch on the value.
from_address is your payout wallet on that network, the same address GET /v1/whoami returns in payout_wallets. It is set before anything is signed; it does not mean sent.
Two capacities
A payout draws on two things: your balance with us, which the amount is held against, and your position in the payout wallet, which the coins leave from.
| Refusal | Whose to fix |
|---|---|
422 insufficient_balance | Yours: your balance does not cover it |
503 wallet_short | Yours: your position in the payout wallet does not cover it. Top it up (below) |
503 network_not_enabled | Ours: no payout wallet on that network yet, or payouts are switched off. Ask us |
503 temporarily_halted | Ours: everything is stopped while a discrepancy is looked at |
The three 503s are retriable in form. Only wallet_short is fixed by you sending money; do not spin on the other two.
Limits and review
Each tenant has its own payout policy: a cap per payout, a rolling 24-hour total, a rolling 24-hour total to addresses not paid before, and thresholds at or above which a payout is accepted and held for review. An address counts as known 24 hours after its first settled payout.
Above a cap the request is refused (amount_above_limit, daily_limit_exceeded with detail.limit) and nothing is held. At or above a review threshold it is accepted with review: "required". GET /v1/chain-payouts/estimate returns your current numbers. Tell us your real volumes and we set them once.
Funding your payout wallet
A transfer into your payout wallet credits your balance by itself and appears in your statement as ON_RAMP, with the transaction in GET /v1/register?kind=ON_RAMP. No event is sent: poll GET /v1/float.
- Send us the addresses you will send from, and wait for our confirmation that they are verified. A transfer from an address we have not verified, or one we recorded before verifying it, is not credited and waits for a person.
- Read the wallet address from
GET /v1/whoami→payout_wallets. An empty map means your wallet does not exist yet: ask us rather than guess. - Send only the currencies we pay on that network. Ask us before sending anything else.
Withdrawals to your verified address
Where the money goes is not a field on the request. It comes from your verified address for that currency, read again at the moment of sending, so the worst a stolen withdrawal key can do is move your money to your own address. You cannot add or change an address through the API; ask us.
GET /v1/destinations
One live address per currency, with verified and, if refused, rejected_reason.
POST /v1/withdrawals
Authorization: Bearer <your key>
Idempotency-Key: topup-2026-10-03
{"amount": {"amount": "100", "currency": "USDT_TRC20"}, "partner_reference": "topup-2026-10-03"}
The amount, fee included, leaves your balance when the withdrawal is created. Follow it with GET /v1/withdrawals/{withdrawal_id} or withdrawal.* events. On a settled withdrawal:
| Field | |
|---|---|
amount | What you asked for |
customer_fee | What we charged |
network_fee | What the network kept, as reported. null means not reported, not zero |
tx_hash | The transaction. null when none was given, or when one batch carried more than one withdrawal |
amount − customer_fee − network_fee is what arrives.
| Refusal | |
|---|---|
422 no_verified_destination | Nothing verified for that currency. detail.state: none_on_file, not_verified or rejected |
422 daily_limit_exceeded | Over your rolling 24-hour withdrawal limit. detail has limit_usd, spent_usd, remaining_usd |
GET /v1/withdrawals/limit answers the same three numbers before you ask.
Proving a payment
POST /v1/payments/proof
Asks whether one transaction paid a given amount to a given destination. Answers PROVEN, NOT_PROVEN or UNREADABLE; only PROVEN is grounds to act. It reads the chain and writes nothing. Scope: withdrawals:read.