Skip to content

Transfer

Send cryptocurrency from a customer's HD wallet to any on-chain address.

Endpoint

POST /api/v1/wallet/:customerID/transfer

Authentication

Requires API key in the X-API-Key header. Also requires the wallet:transfer permission on the key.

Path Parameters

ParameterTypeDescription
customerIDstringYour internal customer identifier

Request Body

FieldTypeRequiredDescription
networkstringYesBlockchain network code
currencystringYesToken to send
to_addressstringYesRecipient blockchain address
amountstringYesAmount in base units (see table below)
indexintegerNoDerivation index to send from (default: 0)

Amount Units

Always pass amounts as integer strings in the network's smallest unit. Never pass decimals.

NetworkCurrencyUnitDecimals1 human unit =
BTCBTCsatoshi8100000000
LTCLTCsatoshi8100000000
BCHBCHsatoshi8100000000
ETHETHwei181000000000000000000
BSCBNBwei181000000000000000000
POLMATIC/POLwei181000000000000000000
ETHUSDTmicro-USDT61000000
ETHUSDCmicro-USDC61000000
BSCUSDTwei-USDT181000000000000000000
BSCUSDCwei-USDC181000000000000000000
POLUSDTwei-USDT181000000000000000000
POLUSDCwei-USDC181000000000000000000
TRXTRXsun61000000
TRXUSDTsun-USDT61000000
SOLSOLlamport91000000000
SOLUSDCmicro-USDC61000000
XRPXRPdrop61000000

BSC/POL USDT and USDC are 18 decimals, not 6. This is correct for BEP-20/ERC-20 on these networks.

Blocked Combinations

The following combinations are rejected with 400 Bad Request:

NetworkCurrencyReason
SOLUSDTUSDT does not exist as a native SPL token on Solana
TRXUSDCUSDC does not exist as a TRC-20 token on Tron

Use USDT on ETH, BSC, POL, or TRX. Use USDC on ETH, BSC, POL, or SOL.

Example Requests

javascript
// Send 0.01 BTC (= 1,000,000 satoshis)
const response = await fetch(
  'https://payments-api-dev-966260606560.europe-west2.run.app/api/v1/wallet/user_12345/transfer',
  {
    method: 'POST',
    headers: {
      'X-API-Key': 'live_sk_your_key_here',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      network: 'BTC',
      currency: 'BTC',
      to_address: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
      amount: '1000000',
      index: 0
    })
  }
);

const transfer = await response.json();
console.log('Transaction Hash:', transfer.tx_hash);
console.log('Status:', transfer.status);
javascript
// Send 50 USDT on Ethereum (6 decimals → 50,000,000)
const response = await fetch(
  'https://payments-api-dev-966260606560.europe-west2.run.app/api/v1/wallet/user_12345/transfer',
  {
    method: 'POST',
    headers: { 'X-API-Key': 'live_sk_your_key_here', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      network: 'ETH',
      currency: 'USDT',
      to_address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
      amount: '50000000',   // 50 USDT — ETH USDT is 6 decimals
      index: 0
    })
  }
);
const transfer = await response.json();
console.log('tx_hash:', transfer.tx_hash);
javascript
// Send 50 USDT on BSC (18 decimals → 50 × 10^18)
const response = await fetch(
  'https://payments-api-dev-966260606560.europe-west2.run.app/api/v1/wallet/user_12345/transfer',
  {
    method: 'POST',
    headers: { 'X-API-Key': 'live_sk_your_key_here', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      network: 'BSC',
      currency: 'USDT',
      to_address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
      amount: '50000000000000000000',   // 50 USDT — BSC USDT is 18 decimals
      index: 0
    })
  }
);
python
import requests

# Send 100 USDT on Tron (6 decimals)
response = requests.post(
    'https://payments-api-dev-966260606560.europe-west2.run.app/api/v1/wallet/user_12345/transfer',
    headers={
        'X-API-Key': 'live_sk_your_key_here',
        'Content-Type': 'application/json'
    },
    json={
        'network': 'TRX',
        'currency': 'USDT',
        'to_address': 'TYourRecipientAddress',
        'amount': '100000000',   # 100 USDT on TRX (6 decimals)
        'index': 0
    }
)

transfer = response.json()
print(f"Transaction: {transfer['tx_hash']}")
print(f"Gas Fee: {transfer['gas_fee']}")
bash
curl -X POST https://payments-api-dev-966260606560.europe-west2.run.app/api/v1/wallet/user_12345/transfer \
  -H "X-API-Key: live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "network": "SOL",
    "currency": "USDC",
    "to_address": "YourRecipientSolanaAddress",
    "amount": "50000000",
    "index": 0
  }'

Response

Success (200 OK)

FieldTypeDescription
tx_hashstringMain transaction hash
from_addressstringSender address (customer's derived wallet)
to_addressstringRecipient address
amountstringAmount delivered to recipient in base units (gross minus any fee)
currencystringToken sent
networkstringBlockchain network
gas_feestringNetwork gas fee in base units (native token)
statusstringpending — transaction is broadcast, awaiting confirmation
timestampstringISO 8601 timestamp
fee_amountstring(optional) Fee collected in base units — omitted if no fee configured
fee_tx_hashstring(optional) Fee transaction hash. Empty for BTC/LTC/BCH (fee is part of main tx). Omitted if no fee.
json
{
  "tx_hash": "0x1a2b3c4d5e6f...",
  "from_address": "0x8B3192f2f0f0D7E4F5C3A1B9E2D7A6C5B4D3E2F1",
  "to_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
  "amount": "49500000",
  "currency": "USDT",
  "network": "ETH",
  "gas_fee": "2100000000000000",
  "status": "pending",
  "timestamp": "2026-06-26T12:00:00Z",
  "fee_amount": "500000",
  "fee_tx_hash": "0xdeadbeef..."
}

The amount in the response is what the recipient receives — gross minus fee. If no fee is configured, amount equals the amount from the request.

Error Responses

400 — Blocked combination

json
{ "error": "USDT is not supported on Solana — use USDT on ETH, BSC, POL, or TRX" }
json
{ "error": "USDC is not supported on Tron — use USDC on ETH, BSC, POL, or SOL" }

500 — Broadcast failed

json
{ "error": "transfer failed: insufficient funds: need 150000 satoshis, have 80000 (fee 12000)" }

Transaction Status

The response always returns status: "pending". Track confirmation via webhooks:

StatusWebhookMeaning
pendingTransaction broadcast, awaiting block inclusion
confirmedwallet.transfer.confirmedTransaction included in block
failedwallet.transfer.failedTransaction rejected

WaaS Fee Collection

If your business has configured a WaaS fee (see POST /api/v1/settings/waas/fees), the fee is automatically applied to every transfer on networks where you have a receiving address set:

  • Gross amount is what the customer initiates (the amount field you send)
  • Net amount goes to the recipient (amount in the response)
  • Fee goes directly on-chain to your configured fee address (not through any platform intermediary)

Fee mechanics per chain type:

ChainHow fee is collected
BTC, LTC, BCHSingle transaction with 3 outputs: recipient + your fee address + change
SOL (native)Single transaction with 2 transfer instructions
ETH, BSC, POL, TRX, XRP, SOL USDCTwo sequential transactions — main transfer first, then fee

Block Explorers

NetworkExplorerURL Format
BitcoinMempool.spacehttps://mempool.space/tx/{tx_hash}
LitecoinBlockchairhttps://blockchair.com/litecoin/transaction/{tx_hash}
Bitcoin CashBlockchairhttps://blockchair.com/bitcoin-cash/transaction/{tx_hash}
EthereumEtherscanhttps://etherscan.io/tx/{tx_hash}
BSCBscScanhttps://bscscan.com/tx/{tx_hash}
PolygonPolygonScanhttps://polygonscan.com/tx/{tx_hash}
TronTronscanhttps://tronscan.org/#/transaction/{tx_hash}
SolanaSolscanhttps://solscan.io/tx/{tx_hash}
XRPXRPScanhttps://xrpscan.com/tx/{tx_hash}

Rach Payments API