Create a Balance Transfer
Create a balance_transfer to move funds between internal accounts within your application. Balance transfers are essential for managing liquidity across different account types in the payment platform.
Account Types
Balance transfers move funds between two account types:
- OPERATING_ACCOUNT: The platform's main operating account that holds settlement funds
- FOR_BENEFIT_OF_ACCOUNT: An FBO (For Benefit Of) account that segregates merchant funds
Common Use Cases
1. Increase FBO Buffer
Transfer funds from operating account to FBO account to ensure sufficient balance for merchant payouts:
{
"amount": 4000,
"currency": "USD",
"description": "Need to increase buffer given the high number of NSFs on merchant fee debits",
"source": "OPERATING_ACCOUNT",
"destination": "FOR_BENEFIT_OF_ACCOUNT",
"processor_type": "LITLE_V1",
"tags": {
"purpose": "fbo_buffer",
"reason": "nsf_prevention"
}
}
2. Return Excess Funds
Move surplus funds from FBO account back to operating account:
{
"amount": 10000,
"currency": "USD",
"description": "Return excess FBO balance to operating account",
"source": "FOR_BENEFIT_OF_ACCOUNT",
"destination": "OPERATING_ACCOUNT",
"processor_type": "LITLE_V1",
"tags": {
"purpose": "excess_return",
"period": "month_end"
}
}
3. Emergency Liquidity Management
Quickly move funds to prevent payout failures:
{
"amount": 50000,
"currency": "USD",
"description": "Emergency transfer to cover pending payouts",
"source": "OPERATING_ACCOUNT",
"destination": "FOR_BENEFIT_OF_ACCOUNT",
"processor_type": "LITLE_V1",
"tags": {
"priority": "urgent",
"purpose": "payout_coverage"
}
}
4. Scheduled Balance Reconciliation
Perform end-of-day balance adjustments:
{
"amount": 25000,
"currency": "USD",
"description": "Daily FBO reconciliation transfer",
"source": "OPERATING_ACCOUNT",
"destination": "FOR_BENEFIT_OF_ACCOUNT",
"processor_type": "LITLE_V1",
"tags": {
"schedule": "daily_eod",
"purpose": "reconciliation",
"date": "2025-12-10"
}
}
5. Fee Collection
Transfer platform fees from FBO to operating account:
{
"amount": 5000,
"currency": "USD",
"description": "Weekly platform fee collection",
"source": "FOR_BENEFIT_OF_ACCOUNT",
"destination": "OPERATING_ACCOUNT",
"processor_type": "LITLE_V1",
"tags": {
"purpose": "fee_collection",
"period": "week_49_2025"
}
}
6. Comprehensive Transfer with Error Handling
Create a balance transfer with full validation and error handling:
async function createBalanceTransfer(transferData) {
try {
// Validate required fields
if (!transferData.amount || transferData.amount <= 0) {
throw new Error('Amount must be greater than 0');
}
if (!transferData.source || !transferData.destination) {
throw new Error('Source and destination are required');
}
// Ensure source and destination are different
if (transferData.source === transferData.destination) {
throw new Error('Source and destination must be different');
}
const response = await fetch('https://api.ahrvo.network/payments/na/balance_transfers', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa('username:password')
},
body: JSON.stringify({
amount: transferData.amount,
currency: transferData.currency || 'USD',
description: transferData.description,
source: transferData.source,
destination: transferData.destination,
processor_type: transferData.processor_type,
tags: {
...transferData.tags,
created_by: 'automated_system',
timestamp: new Date().toISOString()
}
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Transfer failed: ${error._embedded?.errors?.[0]?.message || 'Unknown error'}`);
}
const transfer = await response.json();
// Check transfer state
if (transfer.state === 'FAILED') {
console.error('Transfer created but failed:', transfer.id);
// Handle failed transfer
await handleFailedTransfer(transfer);
return null;
}
if (transfer.state === 'PENDING') {
console.log('Transfer pending:', transfer.id);
// Monitor transfer status
await monitorTransferStatus(transfer.id);
}
console.log('Transfer created successfully:', transfer.id);
return transfer;
} catch (error) {
console.error('Error creating balance transfer:', error);
throw error;
}
}
async function handleFailedTransfer(transfer) {
// Log failure for investigation
console.error('Failed transfer details:', {
id: transfer.id,
amount: transfer.amount,
source: transfer.source,
destination: transfer.destination,
created_at: transfer.created_at
});
// Send alert notification
// await sendAlertNotification(transfer);
// Create incident ticket
// await createIncidentTicket(transfer);
}
async function monitorTransferStatus(transferId) {
// Poll transfer status until completed
const maxAttempts = 10;
let attempts = 0;
while (attempts < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
const response = await fetch(
`https://api.ahrvo.network/payments/na/balance_transfers/${transferId}`,
{
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
}
);
const transfer = await response.json();
if (transfer.state === 'SUCCEEDED') {
console.log('Transfer succeeded:', transferId);
return transfer;
}
if (transfer.state === 'FAILED') {
console.error('Transfer failed:', transferId);
await handleFailedTransfer(transfer);
return null;
}
attempts++;
}
console.warn('Transfer still pending after maximum attempts:', transferId);
return null;
}
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | integer | Yes | Amount to transfer in cents (e.g., 4000 = $40.00) |
currency | string | Yes | 3-letter ISO 4217 currency code (e.g., "USD") |
source | string | Yes | Source account type (OPERATING_ACCOUNT or FOR_BENEFIT_OF_ACCOUNT) |
destination | string | Yes | Destination account type (FOR_BENEFIT_OF_ACCOUNT or OPERATING_ACCOUNT) |
description | string | No | Human-readable description of the transfer |
processor_type | string | No | Processor type to use (e.g., "LITLE_V1") |
tags | object | No | Key-value pairs for custom metadata |
Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the balance transfer |
created_at | string | ISO 8601 timestamp of creation |
updated_at | string | ISO 8601 timestamp of last update |
amount | integer | Amount transferred in cents |
currency | string | Currency code |
description | string | Transfer description |
source | string | Source account type |
destination | string | Destination account type |
state | string | Transfer state: SUCCEEDED, FAILED, or PENDING |
processor_type | string | Processor used for the transfer |
reference_id | string | Internal reference ID |
external_reference_id | string | External reference ID (if provided) |
tags | object | Custom metadata |
Transfer States
Balance transfers can have the following states:
- SUCCEEDED: Transfer completed successfully, funds moved
- PENDING: Transfer is being processed
- FAILED: Transfer failed, funds not moved
Best Practices
1. Validate Before Transfer
Always verify account balances before initiating transfers:
async function validateTransfer(amount, source) {
// Check source account has sufficient balance
const balance = await getAccountBalance(source);
if (balance < amount) {
throw new Error(`Insufficient balance in ${source}: ${balance} < ${amount}`);
}
return true;
}
2. Use Descriptive Tags
Tag transfers for easy tracking and reconciliation:
{
"tags": {
"purpose": "payout_funding",
"merchant_count": "50",
"total_payout_amount": "100000",
"date": "2025-12-10",
"initiated_by": "automated_system"
}
}
3. Monitor Transfer State
Always check the transfer state in the response:
const transfer = await createBalanceTransfer(transferData);
if (transfer.state === 'FAILED') {
// Handle failure
await notifyAdmins('Balance transfer failed', transfer);
await rollbackRelatedOperations(transfer);
}
if (transfer.state === 'PENDING') {
// Schedule status check
await scheduleStatusCheck(transfer.id, 5 * 60 * 1000); // Check in 5 minutes
}
4. Document Transfer Purpose
Always include clear descriptions:
{
"description": "FBO buffer increase - Covering $500K in scheduled merchant payouts for 2025-12-11"
}
5. Implement Idempotency
Use reference IDs to prevent duplicate transfers:
async function createIdempotentTransfer(transferData, referenceId) {
// Check if transfer with this reference already exists
const existing = await findTransferByReference(referenceId);
if (existing) {
console.log('Transfer already exists:', existing.id);
return existing;
}
// Create new transfer
return await createBalanceTransfer({
...transferData,
tags: {
...transferData.tags,
reference: referenceId
}
});
}
6. Set Up Automated Monitoring
Monitor balance levels and trigger transfers automatically:
async function monitorAndMaintainBalance() {
const fboBalance = await getAccountBalance('FOR_BENEFIT_OF_ACCOUNT');
const minBuffer = 100000; // $1,000 minimum
const targetBuffer = 500000; // $5,000 target
if (fboBalance < minBuffer) {
const transferAmount = targetBuffer - fboBalance;
console.log(`FBO balance below minimum: $${fboBalance/100}`);
console.log(`Initiating transfer of $${transferAmount/100}`);
await createBalanceTransfer({
amount: transferAmount,
currency: 'USD',
description: `Automated buffer replenishment - Balance below minimum threshold`,
source: 'OPERATING_ACCOUNT',
destination: 'FOR_BENEFIT_OF_ACCOUNT',
tags: {
purpose: 'automated_buffer',
trigger: 'low_balance_alert',
previous_balance: fboBalance.toString()
}
});
}
}
// Run every hour
setInterval(monitorAndMaintainBalance, 60 * 60 * 1000);
7. Handle Errors Gracefully
Implement robust error handling:
async function safeBalanceTransfer(transferData) {
const maxRetries = 3;
let attempt = 0;
while (attempt < maxRetries) {
try {
const transfer = await createBalanceTransfer(transferData);
if (transfer && transfer.state !== 'FAILED') {
return transfer;
}
// If failed, log and retry
console.warn(`Transfer failed on attempt ${attempt + 1}:`, transfer?.id);
} catch (error) {
console.error(`Error on attempt ${attempt + 1}:`, error.message);
if (attempt === maxRetries - 1) {
// Final attempt failed
await sendCriticalAlert('Balance transfer failed after max retries', {
transferData,
error: error.message
});
throw error;
}
}
attempt++;
// Exponential backoff
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
}
}
8. Maintain Audit Trail
Log all transfers for compliance and reconciliation:
async function auditedBalanceTransfer(transferData) {
const auditLog = {
timestamp: new Date().toISOString(),
action: 'balance_transfer_initiated',
amount: transferData.amount,
source: transferData.source,
destination: transferData.destination,
description: transferData.description,
initiated_by: getCurrentUser()
};
await logAuditEvent(auditLog);
const transfer = await createBalanceTransfer(transferData);
await logAuditEvent({
...auditLog,
action: 'balance_transfer_completed',
transfer_id: transfer.id,
state: transfer.state,
reference_id: transfer.reference_id
});
return transfer;
}
Common Scenarios
Daily Reconciliation Flow
async function dailyReconciliation() {
// Get pending payouts
const pendingPayouts = await getPendingPayouts();
const totalPayouts = pendingPayouts.reduce((sum, p) => sum + p.amount, 0);
// Get current FBO balance
const fboBalance = await getAccountBalance('FOR_BENEFIT_OF_ACCOUNT');
// Calculate needed transfer
const minBuffer = 50000; // $500 buffer
const requiredBalance = totalPayouts + minBuffer;
if (fboBalance < requiredBalance) {
const transferAmount = requiredBalance - fboBalance;
await createBalanceTransfer({
amount: transferAmount,
currency: 'USD',
description: `Daily reconciliation - Covering ${pendingPayouts.length} pending payouts`,
source: 'OPERATING_ACCOUNT',
destination: 'FOR_BENEFIT_OF_ACCOUNT',
tags: {
purpose: 'daily_reconciliation',
payout_count: pendingPayouts.length.toString(),
total_payouts: totalPayouts.toString(),
date: new Date().toISOString().split('T')[0]
}
});
}
}
Emergency Fund Transfer
async function emergencyFundTransfer(urgentAmount, reason) {
console.warn('EMERGENCY TRANSFER INITIATED:', reason);
const transfer = await createBalanceTransfer({
amount: urgentAmount,
currency: 'USD',
description: `URGENT: ${reason}`,
source: 'OPERATING_ACCOUNT',
destination: 'FOR_BENEFIT_OF_ACCOUNT',
tags: {
priority: 'critical',
purpose: 'emergency',
reason: reason,
timestamp: new Date().toISOString()
}
});
// Send immediate notification
await sendUrgentNotification('Emergency balance transfer executed', {
amount: urgentAmount,
transfer_id: transfer.id,
reason: reason
});
return transfer;
}