POST

Create Payout Single

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.

POSThttps://bridge.bifrostcrypto.com/v1/payout
Required scope:payout:write(a new key with no list is read-only)
ᚱ
"In Heimdall's forge, payments are molded with the precision of Svartálfheim's dwarves. Each transaction carries the strength of Mjolnir and the wisdom of Odin."
Brokkr & Sindri, Divine Forgers

Headers

KeyValue
X-Bifrost-Invoke{{api_key}}
Content-Typeapplication/json
Idempotency-Key{{uuid}}

Body Parameters

FieldType
walletrequired
string
crypto_currencyrequired
string
process_byrequired
string
reference_idrequired
string
networkrequired
string
feetakenfromamountrequired
number
webhook_urlrequired
string
fiat_currency
string
crypto_amount
string
fiat_amount
string
customer_info
string
memo
string

Request Body

json
{
  "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

bash
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

json
{
  "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).

NetworkAliases
Ethereumeth, eth-mainnet, erc20
Trontrx, trc20
BinanceSmartChainbnb, bsc, bep20, bnbchain, bnb-smart-chain
Arbitrumarb, arbitrum-evm
Polygonmatic, pol, polygon-pos
Optimismop, op-mainnet
Avalancheavax, avaxc, avalanche-c
Solanasol
Bitcoinbtc
Litecoinltc
Moneroxmr
Xlayerokb

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.

CodeWhenreference_idWhat to do
invalid_parametersA field failed validation; details.fields names it.YesFix the field and resend the item.
duplicate_payment_referencereference_id was already used by an earlier payout.YesUse a new reference_id, or check the earlier payout with GET /v1/list-payout.
duplicate_reference_id_in_requestTwo items of the same request share a reference_id.NoGive each item its own reference_id.
invalid_crypto_amount · invalid_fiat_amount · fiat_currency_requiredThe amount or currency required by process_by is missing or invalid.NoFix the item and resend it.
invalid_network_or_withdrawals_disabled · unsupported_token_or_withdrawals_disabled · token_network_mismatchThe token or network does not accept withdrawals for your account.NoChoose another token or network (GET /v1/summary/tokens).
token_not_activeThe token is not active.YesChoose another token.
memo_requiredThe token needs a memo.NoSend memo.
invalid_formatThe address does not match the network.YesCheck the address and network.
limit_exceededBelow the token minimum after fees, or above the maximum.YesAdjust the amount.
amount_does_not_cover_feesWith feetakenfromamount = 1, fees consume the whole amount.YesIncrease the amount.
insufficient_balanceThe balance does not cover the item and its fees.YesTop up the balance.
daily_payout_limit_exceededThe key's daily payout limit (USD, UTC day) would be exceeded.YesWait for the next UTC day or ask for a higher limit.
price_unavailable · daily_payout_limit_unavailableA quote needed for the item was not available.YesRetry the item later.
error_fetching_active_tokensThe token catalogue could not be loaded.NoRetry the item later.
database_error · internal_errorFailure on our side.YesRetry the item later.
record_not_found · user_not_foundThe account setup is incomplete.YesContact 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.

StatusCodeWhenWhat to do
400json_parse_exception · json_parse_failedThe body is missing or is not valid JSON.Fix the request before sending it again.
400invalid_payment_listThe body is neither a payout object nor a non-empty list.Fix the request before sending it again.
400max_payouts_exceededMore than 50 payouts in one request.Split into requests of up to 50.
400invalid_parametersEvery item failed validation; details.fields is keyed by index.field.Fix the request before sending it again.
400insufficient_total_balanceThe 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 · 400results[].error.codeAn item was refused after validation (see the Item refusal codes table).Fix and resend only the refused items, with a new Idempotency-Key.
404user_not_found · record_not_foundThe account setup is incomplete.Contact support.
409idempotency_key_in_progressA request with this Idempotency-Key is still running.Wait and retry with the same key.
500error_fetching_active_currenciesThe token catalogue could not be loaded. Nothing was processed.Nothing was changed. Retry later.
500internal_errorUnexpected failure; summary and results show what was created.Resend only the items counted in summary.not_processed.

Other responses

Responses that are not the happy path and still need handling in your integration.

400

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.

json
{
  "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"
      }
    }
  ]
}
400

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.

json
{
  "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"
      }
    }
  ]
}
400

Invalid parameters

A field failed validation. details.fields is keyed by index.field (always index 0 here) with the reason for each field.

json
{
  "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"
      }
    }
  }
}
400

Not enough balance

The amount selected by process_by exceeds the available balance after holds. Checked before anything is debited.

json
{
  "error": {
    "code": "insufficient_total_balance",
    "message": "Insufficient balance to cover all payouts",
    "details": {
      "crypto_currency": "USDT",
      "available": "50.00000000",
      "required": "100.500000000000000000"
    }
  }
}
409

Idempotency-Key still running

The first request with this key has not finished yet. Wait and retry with the same key.

json
{
  "error": {
    "code": "idempotency_key_in_progress",
    "message": "A request with this Idempotency-Key is still being processed"
  }
}
400 / 404 / 500

Request-level errors

Nothing is processed. The codes are listed in the Errors table.

json
{
  "error": {
    "code": "json_parse_exception",
    "message": "Exception while parsing JSON payload"
  }
}