Skip to main content

Introduction

The On-chain Transfers API enables inbound and outbound transfers of cryptocurrencies via their native blockchain networks. Bipa manages hot wallets with institutional-grade security via Fireblocks.

Multi-network USDT

Support for Ethereum, Polygon, Tron, Arbitrum, and Optimism

Bitcoin

Native Bitcoin transfers on the Bitcoin network

Institutional custody

Multi-sig wallets secured by Fireblocks

Auto-conversion

Optional auto-convert inbound transfers to BRL

Supported assets & networks

USDT

NetworkConfirmationsInbound timeOutbound time
Polygon128 blocks~5 minutes~2 minutes
Arbitrum64 blocks~2 minutes~1 minute
Optimism64 blocks~2 minutes~1 minute
Tron20 blocks~1 minute~1 minute
Ethereum12 blocks~3 minutes~3 minutes

Bitcoin

NetworkConfirmationsInbound timeOutbound time
Bitcoin3 blocks~30 minutes~10-60 minutes
Polygon and Arbitrum are recommended for USDT due to lower fees and faster confirmations.

The transfer object

{
  "id": "txn_abc123xyz",
  "customer_id": "cus_a1b2c3d4e5f6",
  "direction": "inbound",
  "asset": "USDT",
  "network": "polygon",
  "amount_gross": "100000000",
  "amount_net": "100000000",
  "fee": "0",
  "decimals": 6,
  "status": "confirmed",
  "address": "0x1234567890abcdef1234567890abcdef12345678",
  "tx_hash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
  "confirmations": 128,
  "confirmations_required": 128,
  "created_at": "2024-01-15T10:30:00Z",
  "confirmed_at": "2024-01-15T10:35:00Z"
}

Attributes

AttributeTypeDescription
idstringUnique transfer identifier
customer_idstringCustomer who owns the transfer
directionstringinbound or outbound
assetstringUSDT or BTC
networkstringBlockchain network
amount_grossstringTotal amount in smallest unit (for outbound: net + fee)
amount_netstringNet amount in smallest unit (credited/received)
feestringNetwork fee in smallest unit (0 for inbound)
decimalsintegerDecimal places for the asset (6 for USDT, 8 for BTC)
statusstringTransfer status
addressstringDeposit address (inbound only)
destination_addressstringRecipient address (outbound only)
tx_hashstringTransaction hash
confirmationsintegerCurrent confirmations
confirmations_requiredintegerRequired confirmations
created_atstringISO 8601 timestamp
confirmed_atstringISO 8601 confirmation timestamp

Transfer statuses

StatusDescription
pendingWaiting for transaction to be broadcast
processingTransaction broadcast, awaiting confirmations
confirmedRequired confirmations reached, credited/sent
failedTransfer failed

Inbound transfer flow

Outbound transfer flow

Endpoints

Fees

Inbound fees

Inbound transfers (deposits) are free. Bipa covers network fees for incoming transactions.

Outbound fees

AssetNetworkFee
USDTPolygon1 USDT
USDTArbitrum1 USDT
USDTOptimism1 USDT
USDTTron1 USDT
USDTEthereum3-25 USDT (dynamic)
BTCBitcoinDynamic (based on mempool)
Bitcoin transfers are always sent with fast priority. Fees vary based on network congestion and are shown before confirmation.

Amount encoding

All monetary amounts are returned as string-encoded integers in the asset’s smallest unit to avoid floating-point precision issues. This is the industry standard for financial and blockchain APIs.

Decimal places by asset

AssetDecimalsSmallest unitExample
USDT60.000001 USDT"100000000" = 100 USDT
BTC81 satoshi"1000000" = 0.01 BTC
Never use floating-point types (float, double) for monetary calculations. Use arbitrary-precision libraries like BigDecimal (Java), Decimal (Python), BigInt (JavaScript), or big.Int (Go).

Amount fields

For every transfer, four amount-related fields are returned:
FieldTypeDescription
amount_grossstringTotal amount in smallest unit
amount_netstringNet amount in smallest unit
feestringFee in smallest unit
decimalsintegerDecimal places for the asset

Conversion examples

USDT (6 decimals):
API value: "100000000" (integer string)
Human value: 100.000000 USDT
Formula: 100000000 / 10^6 = 100 USDT
BTC (8 decimals):
API value: "1000000" (integer string)
Human value: 0.01000000 BTC
Formula: 1000000 / 10^8 = 0.01 BTC

Code examples

// Using BigInt for precision (native, no dependencies)
function toHumanReadable(amountStr, decimals) {
  const amount = BigInt(amountStr);
  const divisor = BigInt(10 ** decimals);
  const whole = amount / divisor;
  const fraction = amount % divisor;
  return `${whole}.${fraction.toString().padStart(decimals, '0')}`;
}

function toSmallestUnit(humanAmount, decimals) {
  const [whole, fraction = ''] = humanAmount.split('.');
  const paddedFraction = fraction.padEnd(decimals, '0').slice(0, decimals);
  return BigInt(whole + paddedFraction).toString();
}

// Example
const transfer = await getTransfer('txn_abc123');
console.log(`Amount: ${toHumanReadable(transfer.amount_net, transfer.decimals)} USDT`);
// Output: "Amount: 100.000000 USDT"

Outbound transfer example

When sending 100 USDT on Polygon (1 USDT fee):
FieldValueHuman-readable
amount_net"100000000"100.000000 USDT (recipient receives)
fee"1000000"1.000000 USDT
amount_gross"101000000"101.000000 USDT (deducted from balance)
decimals6

Webhooks

EventDescription
onchain.transfer.pendingTransfer detected, awaiting confirmations
onchain.transfer.confirmedTransfer confirmed and processed
onchain.transfer.failedTransfer failed