Payouts
Paying settled balances out to a wallet — the 2FA step-up for dashboard sessions, API keys with a step-up, the flat network fee and the payout lifecycle.
Last updated Oct 27, 2026
A payout sends part or all of an account's available balance (GET /api/v1/crypto/balances?mode=) on one network and token to an external address, from the hot wallet.
curl -X POST "$API/api/v1/crypto/payouts" \
-H "authorization: Bearer $KEY" -H 'content-type: application/json' -H 'idempotency-key: po-2026-10-27-halden' \
-d '{ "account": "halden_type", "network": "POLYGON_AMOY", "token": "USDT",
"amount": "50.00", "destination_address": "0x…" }'Who may pay out
Payouts need crypto-payouts:create and are refused while a platform admin impersonates the user. On top of that, every payout is stepped up:
- Dashboard (cookie session): each payout carries a fresh TOTP code (
totp_code). The user must have two-factor authentication enabled (Settings → Security); the New payout dialog links there otherwise. Codes are single use, and 5 failed codes in 15 minutes lock step-ups (429 STEP_UP_LOCKED). A wrong or reused code is403. - API keys skip the per-payout code, so the step-up moves to the moment the key is minted: creating a key with
crypto-payouts:create(orcrypto-payouts:manage, or*:*) requires a non-impersonated browser session with 2FA and a validtotp_code. Only such keys carrypayoutStepUpAt; any other key answers403 STEP_UP_REQUIRED. Keys minted before this rule lost payout capability. The operator CLI (mint:api-key) also sets it.
Amount and fee
A flat network fee per payout comes out of the amount (net_amount = amount − fee) and is booked as REVENUE:PAYOUT_NETWORK_FEE: CRYPTO_PAYOUT_FEE_POLYGON (default 0.10), CRYPTO_PAYOUT_FEE_BSC (0.15), CRYPTO_PAYOUT_FEE_BASE (0.10). The amount must exceed the fee. The destination must be a valid address that is not one of the gateway's own (contracts are allowed, unlike refunds).
Lifecycle
| Status | Meaning |
|---|---|
QUEUED | Created: the amount is locked in the ledger (MERCHANT_LOCKED) in the same transaction, so a payout above the balance is 409 INSUFFICIENT_BALANCE and creates nothing. |
BROADCAST | Signed and sent by the worker once the hot wallet can cover net_amount and the gas (it waits for sweeps otherwise). The transaction is journaled before broadcast, so a crash re-sends the same bytes; after 180 s without a receipt it is replaced with the same nonce and fees +20 %. |
COMPLETED | The transaction reached the network's confirmation threshold; the lock is released into the payout (payout.completed). |
FAILED | Reverted or dropped: the lock goes back to the available balance and the gas is expensed (payout.failed). |
The dashboard's Payouts page shows the balances on top, the history below (live confirmation count while BROADCAST), and New payout (source balance, amount, destination pre-filled with the account's settlement address, TOTP).
[!WARNING] When an invoice has no
merchant, the organization's PLATFORM account is the seller: its net is credited toPARTNER_PAYABLEtogether with the markup, the one balance PLATFORM payouts draw from. Every payout therefore draws from the balance the dashboard shows as available.