Transfers
Transfer money instantly between WasaaPay wallets. Transfers are processed in real-time with immediate balance updates.
Overview
The Transfers API allows you to move funds between wallets. You can:
- Preview transfers before executing (see fees and recipient details)
- Send money to any WasaaPay wallet using account number
- Track transfers with your own reference IDs
- Receive webhooks for transfer status changes
Transfer Flow
- Preview - Get transfer details including fees and verify recipient
- Create - Execute the transfer with idempotency protection
- Webhook - Receive notification when transfer completes
API Reference
Preview Transfer
Preview a transfer to see fees and verify the recipient before executing. This does not debit any funds.
Create Transfer
Execute a transfer between wallets. Requires idempotency key.
POST
/partner/transfersGet Transfer
Retrieve details of a specific transfer.
GET
/partner/transfers/:transferIdCode Examples
Complete Transfer Flow (JavaScript)
transfer.js
1async function sendMoney(fromWallet, toAccount, amount, description) {2 const API_KEY = process.env.WASAAPAY_API_KEY;3 const BASE_URL = 'https://api.wasaapay.com/api/v1/partner';45 // Step 1: Preview the transfer6 const previewResponse = await fetch(`${BASE_URL}/transfers/preview`, {7 method: 'POST',8 headers: {9 'Content-Type': 'application/json',10 'X-API-Key': API_KEY,11 },12 body: JSON.stringify({13 sourceWalletId: fromWallet,14 destinationAccountNumber: toAccount,15 amount: amount,16 }),17 });1819 const preview = await previewResponse.json();2021 if (!preview.success || !preview.data.canProceed) {22 throw new Error(preview.data.insufficientFunds23 ? 'Insufficient funds'24 : 'Transfer cannot proceed');25 }2627 console.log('Transfer Preview:');28 console.log(` Recipient: ${preview.data.destinationWallet.ownerName}`);29 console.log(` Amount: ${preview.data.amount} KES`);30 console.log(` Fee: ${preview.data.fee} KES`);31 console.log(` Total: ${preview.data.totalDebit} KES`);3233 // Step 2: Execute the transfer34 const idempotencyKey = `transfer_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;3536 const transferResponse = await fetch(`${BASE_URL}/transfers`, {37 method: 'POST',38 headers: {39 'Content-Type': 'application/json',40 'X-API-Key': API_KEY,41 'X-Idempotency-Key': idempotencyKey,42 },43 body: JSON.stringify({44 sourceWalletId: fromWallet,45 destinationAccountNumber: toAccount,46 amount: amount,47 narration: description,48 }),49 });5051 const transfer = await transferResponse.json();5253 if (!transfer.success) {54 throw new Error(transfer.message);55 }5657 console.log(`Transfer ${transfer.data.status}: ${transfer.data.reference}`);58 return transfer.data;59}6061// Usage62sendMoney('wallet_abc123', '0712345678', 1000, 'Payment for services')63 .then(result => console.log('Transfer ID:', result.id))64 .catch(error => console.error('Transfer failed:', error.message));
Webhooks
Transfer events are delivered to your webhook URL. See the Webhooks documentation for setup instructions.
Transfer Events
| Event | Description |
|---|---|
transfer.completed | Transfer was successful |
transfer.failed | Transfer failed |
Webhook Payload
Webhook Payload
{"event": "transfer.completed","timestamp": "2024-01-15T10:30:01Z","partnerId": "partner_abc123","environment": "PRODUCTION","signature": "sha256=abc123...","data": {"transferId": "txn_xyz789","reference": "TRF-20240115-ABC123","externalReference": "INV-2024-001","amount": "1000.00","fee": "10.00","status": "COMPLETED"}}
Error Codes
| Code | Description | Resolution |
|---|---|---|
INSUFFICIENT_FUNDS | Source wallet doesn't have enough balance | Add funds to the source wallet |
WALLET_NOT_FOUND | Source or destination wallet doesn't exist | Verify wallet IDs/account numbers |
WALLET_INACTIVE | One of the wallets is suspended or closed | Contact support |
SAME_WALLET | Source and destination are the same | Use different wallets |
AMOUNT_TOO_LOW | Amount below minimum (1 KES) | Increase amount |
AMOUNT_TOO_HIGH | Amount exceeds maximum limit | Reduce amount or split transfer |
DAILY_LIMIT_EXCEEDED | Daily transfer limit reached | Wait until tomorrow or request limit increase |
Best Practices
- Always preview first: Use the preview endpoint to verify recipient and fees before executing
- Use idempotency keys: Generate unique keys to prevent duplicate transfers on retry
- Store external references: Use externalReference to link transfers to your own records
- Handle webhooks: Implement webhook handlers for reliable status tracking
- Add narrations: Include descriptive narrations for better transaction history