Skip to content

Direct Transfers

This guide provides comprehensive examples for sending money using Flutterwave's direct transfer API. Direct transfers allow you to send funds instantly to bank accounts, wallets, and mobile money accounts.

Overview

Direct transfers support three types:

  • Bank transfers - Send to bank accounts
  • Wallet transfers - Send to Flutterwave wallets
  • Mobile money transfers - Send to mobile money accounts

Basic Bank Transfer

Simple Bank Transfer

typescript
import { Flutterwave } from 'flutterwave-node-v4';

const flutterwave = new Flutterwave({
  clientId: process.env.CLIENT_ID!,
  clientSecret: process.env.CLIENT_SECRET!,
  environment: 'sandbox',
});

const transfer = await flutterwave.api.transfers.directTransfer({
  action: 'instant',
  type: 'bank',
  reference: `transfer-${Date.now()}`,
  payment_instruction: {
    source_currency: 'NGN',
    destination_currency: 'NGN',
    amount: {
      value: 10000,
      applies_to: 'source_currency',
    },
    recipient: {
      bank: {
        account_number: '0690000031',
        code: '044',
      },
    },
    sender: {
      name: {
        first: 'John',
        last: 'Doe',
      },
    },
  },
});

console.log('Transfer ID:', transfer.id);
console.log('Transfer Status:', transfer.status);
console.log('Reference:', transfer.reference);

Transfer with Narration

Add a description to the transfer:

typescript
const transfer = await flutterwave.api.transfers.directTransfer({
  action: 'instant',
  type: 'bank',
  reference: `transfer-${Date.now()}`,
  narration: 'Payment for services rendered',
  payment_instruction: {
    source_currency: 'NGN',
    destination_currency: 'NGN',
    amount: {
      value: 50000,
      applies_to: 'source_currency',
    },
    recipient: {
      bank: {
        account_number: '0690000031',
        code: '044',
      },
    },
    sender: {
      name: {
        first: 'Jane',
        last: 'Smith',
      },
    },
  },
});

Transfer with Trace ID

Use a trace ID for tracking and debugging:

typescript
const traceId = `trace-${Date.now()}`;

const transfer = await flutterwave.api.transfers.directTransfer(
  {
    action: 'instant',
    type: 'bank',
    reference: `transfer-${Date.now()}`,
    payment_instruction: {
      source_currency: 'NGN',
      destination_currency: 'NGN',
      amount: {
        value: 25000,
        applies_to: 'source_currency',
      },
      recipient: {
        bank: {
          account_number: '0690000031',
          code: '044',
        },
      },
      sender: {
        name: {
          first: 'Michael',
          last: 'Johnson',
        },
      },
    },
  },
  traceId,
);

console.log('Trace ID:', traceId);
console.log('Transfer ID:', transfer.id);

Wallet Transfers

Transfer to Flutterwave Wallet

typescript
const transfer = await flutterwave.api.transfers.directTransfer({
  action: 'instant',
  type: 'wallet',
  reference: `wallet-transfer-${Date.now()}`,
  narration: 'Wallet credit',
  payment_instruction: {
    source_currency: 'NGN',
    destination_currency: 'NGN',
    amount: {
      value: 5000,
      applies_to: 'source_currency',
    },
    recipient: {
      wallet: {
        identifier: '0690000031',
        provider: 'flutterwave',
      },
    },
    sender: {
      name: {
        first: 'Sarah',
        last: 'Williams',
      },
    },
  },
});

console.log('Wallet Transfer ID:', transfer.id);
console.log('Status:', transfer.status);

Scheduled Transfers

Schedule a Future Transfer

typescript
const scheduledDate = new Date();
scheduledDate.setDate(scheduledDate.getDate() + 7); // 7 days from now

const transfer = await flutterwave.api.transfers.directTransfer({
  action: 'scheduled',
  type: 'bank',
  reference: `scheduled-${Date.now()}`,
  scheduled_date: scheduledDate.toISOString(),
  payment_instruction: {
    source_currency: 'NGN',
    destination_currency: 'NGN',
    amount: {
      value: 100000,
      applies_to: 'source_currency',
    },
    recipient: {
      bank: {
        account_number: '0690000031',
        code: '044',
      },
    },
    sender: {
      name: {
        first: 'David',
        last: 'Brown',
      },
    },
  },
});

console.log('Scheduled Transfer ID:', transfer.id);
console.log('Scheduled for:', scheduledDate.toISOString());

Cross-Currency Transfers

Transfer with Currency Conversion

typescript
const transfer = await flutterwave.api.transfers.directTransfer({
  action: 'instant',
  type: 'bank',
  reference: `cross-currency-${Date.now()}`,
  payment_instruction: {
    source_currency: 'USD',
    destination_currency: 'NGN',
    amount: {
      value: 100, // $100 USD
      applies_to: 'source_currency',
    },
    recipient: {
      bank: {
        account_number: '0690000031',
        code: '044',
      },
    },
    sender: {
      name: {
        first: 'Emma',
        last: 'Davis',
      },
    },
  },
});

console.log('Source Amount:', transfer.payment_instruction.amount.value, 'USD');
console.log('Destination Amount (NGN):', transfer.destination_amount);

Bulk Transfers

Process Multiple Transfers

typescript
interface TransferRecipient {
  accountNumber: string;
  bankCode: string;
  amount: number;
  name: {
    first: string;
    last: string;
  };
}

async function processBulkTransfers(recipients: TransferRecipient[]) {
  const results = [];

  for (const recipient of recipients) {
    try {
      const transfer = await flutterwave.api.transfers.directTransfer({
        action: 'instant',
        type: 'bank',
        reference: `bulk-${Date.now()}-${recipient.accountNumber}`,
        payment_instruction: {
          source_currency: 'NGN',
          destination_currency: 'NGN',
          amount: {
            value: recipient.amount,
            applies_to: 'source_currency',
          },
          recipient: {
            bank: {
              account_number: recipient.accountNumber,
              code: recipient.bankCode,
            },
          },
          sender: {
            name: recipient.name,
          },
        },
      });

      results.push({
        success: true,
        transferId: transfer.id,
        reference: transfer.reference,
        recipient: recipient.accountNumber,
      });
    } catch (error) {
      results.push({
        success: false,
        error: error instanceof Error ? error.message : 'Unknown error',
        recipient: recipient.accountNumber,
      });
    }
  }

  return results;
}

// Usage
const recipients: TransferRecipient[] = [
  {
    accountNumber: '0690000031',
    bankCode: '044',
    amount: 5000,
    name: { first: 'Alice', last: 'Johnson' },
  },
  {
    accountNumber: '0690000032',
    bankCode: '044',
    amount: 7500,
    name: { first: 'Bob', last: 'Smith' },
  },
  {
    accountNumber: '0690000033',
    bankCode: '044',
    amount: 10000,
    name: { first: 'Carol', last: 'Williams' },
  },
];

const results = await processBulkTransfers(recipients);
console.log('Bulk transfer results:', results);

Account Verification

Verify Account Before Transfer

typescript
async function verifyAndTransfer(
  accountNumber: string,
  bankCode: string,
  amount: number,
) {
  // First, verify the account
  const verification = await flutterwave.api.banks.resolve({
    account: {
      number: accountNumber,
      code: bankCode,
    },
    currency: 'NGN',
  });

  console.log('Account Name:', verification.name);
  console.log('Account Number:', verification.number);

  // Confirm with user (in a real app)
  const confirmed = true; // User confirms the name

  if (!confirmed) {
    throw new Error('Transfer cancelled by user');
  }

  // Proceed with transfer
  const transfer = await flutterwave.api.transfers.directTransfer({
    action: 'instant',
    type: 'bank',
    reference: `verified-${Date.now()}`,
    payment_instruction: {
      source_currency: 'NGN',
      destination_currency: 'NGN',
      amount: {
        value: amount,
        applies_to: 'source_currency',
      },
      recipient: {
        bank: {
          account_number: accountNumber,
          code: bankCode,
        },
      },
      sender: {
        name: {
          first: 'Verified',
          last: 'Sender',
        },
      },
    },
  });

  return {
    verification,
    transfer,
  };
}

// Usage
const result = await verifyAndTransfer('0690000031', '044', 15000);
console.log('Verified account:', result.verification.name);
console.log('Transfer ID:', result.transfer.id);

Idempotency

Prevent Duplicate Transfers

typescript
import { v4 as uuidv4 } from 'uuid';

const idempotencyKey = uuidv4();

const transfer = await flutterwave.api.transfers.directTransfer(
  {
    action: 'instant',
    type: 'bank',
    reference: `idempotent-${Date.now()}`,
    payment_instruction: {
      source_currency: 'NGN',
      destination_currency: 'NGN',
      amount: {
        value: 20000,
        applies_to: 'source_currency',
      },
      recipient: {
        bank: {
          account_number: '0690000031',
          code: '044',
        },
      },
      sender: {
        name: {
          first: 'Test',
          last: 'User',
        },
      },
    },
  },
  undefined, // traceId
  idempotencyKey,
);

// If you retry with the same idempotency key, you'll get the same transfer

Error Handling

Robust Transfer with Error Handling

typescript
import {
  BadRequestException,
  UnauthorizedRequestException,
} from 'flutterwave-node-v4';

async function safeTransfer(transferData: any) {
  try {
    const transfer =
      await flutterwave.api.transfers.directTransfer(transferData);

    console.log('Transfer successful!');
    console.log('Transfer ID:', transfer.id);
    console.log('Reference:', transfer.reference);
    console.log('Status:', transfer.status);

    return {
      success: true,
      data: transfer,
    };
  } catch (error) {
    if (error instanceof BadRequestException) {
      console.error('Invalid transfer data:', error.message);
      return {
        success: false,
        error: 'Invalid transfer details',
        message: 'Please check your transfer information and try again',
      };
    } else if (error instanceof UnauthorizedRequestException) {
      console.error('Authentication failed:', error.message);
      return {
        success: false,
        error: 'Authentication error',
        message: 'Please check your credentials',
      };
    } else {
      console.error('Transfer failed:', error);
      return {
        success: false,
        error: 'Transfer failed',
        message: 'An unexpected error occurred',
      };
    }
  }
}

// Usage
const result = await safeTransfer({
  action: 'instant',
  type: 'bank',
  reference: `safe-${Date.now()}`,
  payment_instruction: {
    source_currency: 'NGN',
    destination_currency: 'NGN',
    amount: {
      value: 12000,
      applies_to: 'source_currency',
    },
    recipient: {
      bank: {
        account_number: '0690000031',
        code: '044',
      },
    },
    sender: {
      name: {
        first: 'Safe',
        last: 'Transfer',
      },
    },
  },
});

if (result.success) {
  console.log('Transfer completed:', result.data.id);
} else {
  console.error('Transfer failed:', result.message);
}

Transfer Status Tracking

Check Transfer Status

typescript
async function trackTransfer(transferId: string) {
  const transfer = await flutterwave.api.transfers.retrieve(transferId);

  console.log('Transfer Status:', transfer.status);
  console.log('Reference:', transfer.reference);
  console.log('Amount:', transfer.payment_instruction.amount.value);

  return transfer;
}

// Usage
const transferId = 'txf_abc123';
const status = await trackTransfer(transferId);

List Recent Transfers

typescript
async function getRecentTransfers(limit: number = 10) {
  const transfers = await flutterwave.api.transfers.list({
    page: 1,
    size: limit,
  });

  console.log(`Found ${transfers.data.length} transfers`);

  transfers.data.forEach((transfer) => {
    console.log(
      `${transfer.reference}: ${transfer.status} - ${transfer.payment_instruction.amount.value}`,
    );
  });

  return transfers;
}

// Usage
await getRecentTransfers(20);

Best Practices

1. Always Generate Unique References

typescript
function generateTransferReference(): string {
  const timestamp = Date.now();
  const random = Math.random().toString(36).substring(7);
  return `transfer-${timestamp}-${random}`;
}

const transfer = await flutterwave.api.transfers.directTransfer({
  reference: generateTransferReference(),
  // ... other fields
});

2. Validate Amount Before Transfer

typescript
function validateTransferAmount(amount: number): void {
  if (amount <= 0) {
    throw new Error('Amount must be greater than zero');
  }

  if (amount > 1000000) {
    throw new Error('Amount exceeds maximum limit');
  }

  // Check for decimal places (max 2)
  if (amount.toString().includes('.')) {
    const decimals = amount.toString().split('.')[1];
    if (decimals.length > 2) {
      throw new Error('Amount can have at most 2 decimal places');
    }
  }
}

// Usage
try {
  validateTransferAmount(15000.5);
  // Proceed with transfer
} catch (error) {
  console.error('Validation failed:', error.message);
}

3. Log All Transfers

typescript
interface TransferLog {
  timestamp: string;
  reference: string;
  amount: number;
  currency: string;
  recipient: string;
  status: string;
  transferId?: string;
  error?: string;
}

const transferLogs: TransferLog[] = [];

async function loggedTransfer(transferData: any) {
  const log: TransferLog = {
    timestamp: new Date().toISOString(),
    reference: transferData.reference,
    amount: transferData.payment_instruction.amount.value,
    currency: transferData.payment_instruction.source_currency,
    recipient:
      transferData.payment_instruction.recipient.bank?.account_number ||
      'unknown',
    status: 'pending',
  };

  try {
    const transfer =
      await flutterwave.api.transfers.directTransfer(transferData);

    log.status = 'success';
    log.transferId = transfer.id;

    return transfer;
  } catch (error) {
    log.status = 'failed';
    log.error = error instanceof Error ? error.message : 'Unknown error';

    throw error;
  } finally {
    transferLogs.push(log);
  }
}

4. Implement Retry Logic

typescript
async function transferWithRetry(
  transferData: any,
  maxRetries: number = 3,
): Promise<any> {
  let lastError;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const transfer =
        await flutterwave.api.transfers.directTransfer(transferData);
      console.log(`Transfer successful on attempt ${attempt}`);
      return transfer;
    } catch (error) {
      lastError = error;
      console.error(`Transfer attempt ${attempt} failed:`, error);

      if (error instanceof BadRequestException) {
        // Don't retry bad requests
        throw error;
      }

      if (attempt < maxRetries) {
        const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
        console.log(`Retrying in ${delay}ms...`);
        await new Promise((resolve) => setTimeout(resolve, delay));
      }
    }
  }

  throw lastError;
}

Released under the MIT License.