Create Payout Single
La referencia de la API está en inglés. Las guías, la autenticación, los webhooks y los códigos de error están en español.
Creates one payout (withdrawal) to a destination wallet.
The body is a single payout object, and the response has the same shape as a batch of one: 200 when the payout is created, 400 when it is refused. A refusal after validation comes in results[0].error, not at the top level; only request-level problems (invalid parameters, balance pre-check, malformed body) answer with a top-level error. In a created result, crypto_amount is the net amount the recipient receives (lower than what you sent when feetakenfromamount is 1), fiat_amount is its value in fiat_currency, network_fee is the network fee for the transfer (reported even when your account does not pay it) and memo is null unless the token uses one.
https://bridge.bifrostcrypto.com/v1/payoutpayout:write(una clave nueva sin lista es de solo lectura)"En la forja de Heimdall, los pagos se moldean con la precisión de los enanos de Svartálfheim. Cada transacción lleva la fuerza de Mjolnir y la sabiduría de Odin."Brokkr & Sindri, Forjadores Divinos
Headers
| Key | Value | Description |
|---|---|---|
X-Bifrost-Invoke | {{api_key}} | Private API key |
Content-Type | application/json | - |
Idempotency-Key | {{uuid}} | Optional, up to 255 characters (a longer key is ignored and the request runs without idempotency). Scoped to your account and kept for 24 hours: resending the same key returns the original status and body instead of processing again, even if the body differs. While the first request is still running (up to 15 minutes) a resend gets 409 idempotency_key_in_progress. Not stored when the request fails before any payout is attempted (top-level 4xx or 500), so a retry with the same key is processed normally. |
Body Parameters
| Field | Type | Description |
|---|---|---|
walletrequired | string | Destination address, 20 to 120 characters (surrounding spaces are trimmed). A length outside that range fails validation (invalid_parameters). The format is then checked against the network: EVM is 0x plus 40 hex characters; Tron, Bitcoin and the other networks use their own formats. A mismatch returns invalid_format on that item. |
crypto_currencyrequired | string | Token symbol, case-sensitive (e.g.: USDT, BTC, ETH). Must be active for withdrawals; usdt is refused. |
process_byrequired | string | crypto_amount or fiat_amount. Selects which amount is used; the other amount, if sent, is ignored. |
reference_idrequired | string | Your reference for this payout, 1 to 255 characters (string or integer). Unique per account across all payouts. Sending one that already exists creates nothing: the item is refused with duplicate_payment_reference and duplicate: true, carrying the original payout's references. The duplicate is detected only after the amount, balance and daily-limit checks, so a resend can also be refused by one of those. |
networkrequired | string | One of: Bitcoin, Ethereum, BinanceSmartChain, Tron, Solana, Ripple, Cardano, Dogecoin, Polygon, Avalanche, ArbitrumOne, Celo, Optimism, Arbitrum, Cronos, Base, BitcoinCash, BitcoinSV, BitcoinGold, Monero, OpBnb, Xlayer, Litecoin, TON. Case-insensitive; the aliases in Network aliases are also accepted (erc20, bsc, trc20...) and the payout is stored and returned with the canonical name. Withdrawals must be enabled for the token on that network, otherwise the item returns invalid_network_or_withdrawals_disabled, unsupported_token_or_withdrawals_disabled or token_network_mismatch. |
feetakenfromamountrequired | number | 0 or 1 (number or string). 1 = fees deducted from the amount (the recipient receives less; the debit is the amount you sent). 0 = fees added on top of the debit (the recipient receives the full amount). When your account pays the network fee and holds enough of the network's native coin (e.g. ETH on Ethereum), that fee is debited from the native balance instead of the token. |
webhook_urlrequired | string | Callback URL for status updates, up to 756 characters. Must be https:// and resolve to a public address; a private, local or blocked destination fails validation (invalid_parameters on webhook_url). |
fiat_currency | string | Required when process_by is fiat_amount (otherwise the item returns fiat_currency_required). Fiat code, case-insensitive (e.g. USD, BRL). When omitted with crypto_amount, your account's preferred currency is used, or USD, to compute fiat_amount in the response. |
crypto_amount | string | Required when process_by is crypto_amount (otherwise the item returns invalid_crypto_amount). Plain decimal only: digits with an optional dot, string or JSON number. Scientific notation, hex, thousands separators, signs and surrounding spaces are rejected. Decimals beyond the token's precision are truncated. |
fiat_amount | string | Required when process_by is fiat_amount (otherwise the item returns invalid_fiat_amount). Converted to crypto at the current price. Same format rules as crypto_amount. |
customer_info | string | Your customer identifier, up to 200 characters, no whitespace. |
memo | string | Required for tokens that need a destination memo, such as TON (otherwise the item returns memo_required). When sent it must be 8 to 15 digits, for any token; it is discarded for tokens that do not use one. |
Request Body
{
"wallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"crypto_currency": "USDT",
"fiat_currency": "USD",
"crypto_amount": "100.50",
"process_by": "crypto_amount",
"reference_id": "order_12345",
"network": "Ethereum",
"feetakenfromamount": 0,
"webhook_url": "https://your-site.com/webhook",
"customer_info": "customer123"
}cURL Example
curl -X POST "https://bridge.bifrostcrypto.com/v1/payout" \
-H "X-Bifrost-Invoke: YOUR_API_KEY" \
-H "Idempotency-Key: 3f1c8b2a-9d54-4f6e-8c31-7b0a2e5d9411" \
-H "Content-Type: application/json" \
-d '{
"wallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"crypto_currency": "USDT",
"fiat_currency": "USD",
"crypto_amount": "100.50",
"process_by": "crypto_amount",
"reference_id": "order_12345",
"network": "Ethereum",
"feetakenfromamount": 0,
"webhook_url": "https://your-site.com/webhook",
"customer_info": "customer123"
}'Response Example
{
"message": "payment_processing_completed",
"summary": {
"total_requested": 1,
"successful": 1,
"failed": 0
},
"results": [
{
"index": 0,
"reference_id": "order_12345",
"internal_reference": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"crypto_amount": "100.5",
"fiat_amount": "100.50000000",
"crypto_currency": "USDT",
"fiat_currency": "USD",
"network_fee": "0.00100000",
"wallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"memo": null,
"network": "Ethereum",
"process_by": "crypto_amount",
"status": "created"
}
]
}Network aliases
On create, network also accepts these aliases. Case, spaces, hyphens and underscores are ignored (ERC-20, erc_20 and erc20 are the same). Responses, webhooks and listings always return the canonical name. bnb means BinanceSmartChain (for opBNB send OpBnb) and arb means Arbitrum (for ArbitrumOne send ArbitrumOne).
| Network | Aliases |
|---|---|
Ethereum | eth, eth-mainnet, erc20 |
Tron | trx, trc20 |
BinanceSmartChain | bnb, bsc, bep20, bnbchain, bnb-smart-chain |
Arbitrum | arb, arbitrum-evm |
Polygon | matic, pol, polygon-pos |
Optimism | op, op-mainnet |
Avalanche | avax, avaxc, avalanche-c |
Solana | sol |
Bitcoin | btc |
Litecoin | ltc |
Monero | xmr |
Xlayer | okb |
Item refusal codes
Codes an entry can carry in results[].error.code. The rest of the batch continues and nothing is debited for the refused item. Codes marked No in reference_id come back without it: match those items by index.
| Code | When | reference_id | What to do |
|---|---|---|---|
invalid_parameters | A field failed validation; details.fields names it. | Yes | Fix the field and resend the item. |
duplicate_payment_reference | reference_id was already used by an earlier payout. | Yes | Use a new reference_id, or check the earlier payout with GET /v1/list-payout. |
duplicate_reference_id_in_request | Two items of the same request share a reference_id. | No | Give each item its own reference_id. |
invalid_crypto_amount · invalid_fiat_amount · fiat_currency_required | The amount or currency required by process_by is missing or invalid. | No | Fix the item and resend it. |
invalid_network_or_withdrawals_disabled · unsupported_token_or_withdrawals_disabled · token_network_mismatch | The token or network does not accept withdrawals for your account. | No | Choose another token or network (GET /v1/summary/tokens). |
token_not_active | The token is not active. | Yes | Choose another token. |
memo_required | The token needs a memo. | No | Send memo. |
invalid_format | The address does not match the network. | Yes | Check the address and network. |
limit_exceeded | Below the token minimum after fees, or above the maximum. | Yes | Adjust the amount. |
amount_does_not_cover_fees | With feetakenfromamount = 1, fees consume the whole amount. | Yes | Increase the amount. |
insufficient_balance | The balance does not cover the item and its fees. | Yes | Top up the balance. |
daily_payout_limit_exceeded | The key's daily payout limit (USD, UTC day) would be exceeded. | Yes | Wait for the next UTC day or ask for a higher limit. |
price_unavailable · daily_payout_limit_unavailable | A quote needed for the item was not available. | Yes | Retry the item later. |
error_fetching_active_tokens | The token catalogue could not be loaded. | No | Retry the item later. |
database_error · internal_error | Failure on our side. | Yes | Retry the item later. |
record_not_found · user_not_found | The account setup is incomplete. | Yes | Contact support. |
Errors
Every error has the shape error.code, error.message and, when useful, error.details. Authentication, scope and rate-limit errors (401, 403, 429) apply to every endpoint and are described under Authentication.
| Status | Code | When | What to do |
|---|---|---|---|
| 400 | json_parse_exception · json_parse_failed | The body is missing or is not valid JSON. | Fix the request before sending it again. |
| 400 | invalid_payment_list | The body is neither a payout object nor a non-empty list. | Fix the request before sending it again. |
| 400 | max_payouts_exceeded | More than 50 payouts in one request. | Split into requests of up to 50. |
| 400 | invalid_parameters | Every item failed validation; details.fields is keyed by index.field. | Fix the request before sending it again. |
| 400 | insufficient_total_balance | The items of one currency add up to more than the available balance. Nothing is debited. | Top up the balance or send fewer payouts. |
| 200 · 207 · 400 | results[].error.code | An item was refused after validation (see the Item refusal codes table). | Fix and resend only the refused items, with a new Idempotency-Key. |
| 404 | user_not_found · record_not_found | The account setup is incomplete. | Contact support. |
| 409 | idempotency_key_in_progress | A request with this Idempotency-Key is still running. | Wait and retry with the same key. |
| 500 | error_fetching_active_currencies | The token catalogue could not be loaded. Nothing was processed. | Nothing was changed. Retry later. |
| 500 | internal_error | Unexpected failure; summary and results show what was created. | Resend only the items counted in summary.not_processed. |
Otras respuestas
Respuestas que no son el camino feliz y aun así necesitan tratamiento en su integración.
Payout refused
The payout passed validation but was refused. The HTTP status is 400 and the reason is in results[0].error.code. The codes and what each one means are listed under Item refusal codes in Create Payout Multiple. Nothing is debited.
{
"message": "payment_processing_completed",
"summary": {
"total_requested": 1,
"successful": 0,
"failed": 1
},
"results": [
{
"index": 0,
"reference_id": "order_12345",
"error": {
"code": "insufficient_balance",
"message": "Insufficient balance for this operation"
}
}
]
}reference_id already used
No second payout is created. The result carries the original payout's internal_reference, its current status (pending, completed, failed or refunded) and when it was created.
{
"message": "payment_processing_completed",
"summary": {
"total_requested": 1,
"successful": 0,
"failed": 1
},
"results": [
{
"index": 0,
"reference_id": "order_12345",
"internal_reference": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"duplicate": true,
"original_status": "pending",
"original_created_at": "2026-01-16 10:00:00",
"error": {
"code": "duplicate_payment_reference",
"message": "A payout with this reference_id already exists"
}
}
]
}Invalid parameters
A field failed validation. details.fields is keyed by index.field (always index 0 here) with the reason for each field.
{
"error": {
"code": "invalid_parameters",
"message": "Invalid parameters provided",
"details": {
"fields": {
"0.crypto_amount": "Crypto amount must be a positive decimal number",
"0.network": "Invalid network"
}
}
}
}Not enough balance
The amount selected by process_by exceeds the available balance after holds. Checked before anything is debited.
{
"error": {
"code": "insufficient_total_balance",
"message": "Insufficient balance to cover all payouts",
"details": {
"crypto_currency": "USDT",
"available": "50.00000000",
"required": "100.500000000000000000"
}
}
}Idempotency-Key still running
The first request with this key has not finished yet. Wait and retry with the same key.
{
"error": {
"code": "idempotency_key_in_progress",
"message": "A request with this Idempotency-Key is still being processed"
}
}Request-level errors
Nothing is processed. The codes are listed in the Errors table.
{
"error": {
"code": "json_parse_exception",
"message": "Exception while parsing JSON payload"
}
}