Skip to main content

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

ParameterTypeRequiredDescription
amountintegerYesAmount to transfer in cents (e.g., 4000 = $40.00)
currencystringYes3-letter ISO 4217 currency code (e.g., "USD")
sourcestringYesSource account type (OPERATING_ACCOUNT or FOR_BENEFIT_OF_ACCOUNT)
destinationstringYesDestination account type (FOR_BENEFIT_OF_ACCOUNT or OPERATING_ACCOUNT)
descriptionstringNoHuman-readable description of the transfer
processor_typestringNoProcessor type to use (e.g., "LITLE_V1")
tagsobjectNoKey-value pairs for custom metadata

Response Fields

FieldTypeDescription
idstringUnique identifier for the balance transfer
created_atstringISO 8601 timestamp of creation
updated_atstringISO 8601 timestamp of last update
amountintegerAmount transferred in cents
currencystringCurrency code
descriptionstringTransfer description
sourcestringSource account type
destinationstringDestination account type
statestringTransfer state: SUCCEEDED, FAILED, or PENDING
processor_typestringProcessor used for the transfer
reference_idstringInternal reference ID
external_reference_idstringExternal reference ID (if provided)
tagsobjectCustom 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;
}

Interactive API Reference