Withdrawals & Transfers
Move customer funds out of their dedicated wallet: withdraw to any Nigerian bank account, or transfer internally to your merchant NGN wallet (instant, atomic, zero fee).
Preflight First
Get a non-committal quote — fee, balance projection, limits, and a verdict — without touching the provider or reserving anything:
const quote = await client.customer.wallet.preflightWithdrawal(
customerId,
walletId,
{ amount: 5000 }
);
console.log(quote.data.fee); // "15"
console.log(quote.data.balance.projectedAfter); // balance after amount + fee
console.log(quote.data.limits.rolling24hRemaining); // headroom under the daily cap
console.log(quote.data.allowed, quote.data.reasons); // e.g. ["insufficient_balance"]Withdraw to a Bank Account
const withdrawal = await client.customer.wallet.withdraw(
customerId,
walletId,
{
amount: 5000,
beneficiaryBankCode: "100004",
beneficiaryAccountNumber: "8135155549",
narration: "Wallet payout",
externalReference: `payout-${orderId}`, // your stable reference
saveBeneficiary: true, // reuse via beneficiaryId next time
},
{ idempotencyKey: crypto.randomUUID() }
);
console.log(withdrawal.data.status); // "pending"
console.log(withdrawal.data.reference); // poll with this — it is stablePoll by reference, not transactionReference. The transaction reference starts as a preliminary pre-… hash and is replaced by the provider session ID once the transfer is accepted.
Track the Withdrawal
const status = await client.customer.wallet.getWithdrawal(
customerId,
walletId,
withdrawal.data.reference
);
// status.data.status → "pending" | "successful" | "failed" | "reversed"
// Failed withdrawals are automatically reversed — funds return to the wallet.Reuse Saved Beneficiaries
const saved = await client.customer.beneficiaries.list();
await client.customer.wallet.withdraw(
customerId,
walletId,
{ amount: 2000, beneficiaryId: saved.data[0].id },
{ idempotencyKey: crypto.randomUUID() }
);Beneficiaries are scoped to your app — shared across your customers, never customer-owned.
Transfer to Your Merchant Wallet
Atomically debit the customer wallet and credit your main NGN wallet — both legs commit together, no provider, no fee:
const transfer = await client.customer.wallet.transferToMerchant(
customerId,
walletId,
{ destination: "merchant", amount: 2000 },
{ idempotencyKey: crypto.randomUUID() }
);
console.log(transfer.data.status); // "successful"Transfer Limits
Every transfer is checked against a versioned limit policy: per-transaction min/max plus a rolling 24-hour cap per bucket (external withdrawals and internal transfers are independent). You can cap individual customers below the platform policy:
// View effective limits + rolling usage
const limits = await client.customer.transferLimits.get(customerId);
// Lower-only per-customer caps; values above the policy are rejected
await client.customer.transferLimits.set(customerId, {
external: { perTransactionMax: 50000, rolling24hMax: 200000 },
});| Rejection code | Meaning |
|---|---|
VALIDATION_ERROR | Below the minimum or above the per-transaction maximum |
DAILY_LIMIT_EXCEEDED | Would exceed the rolling 24-hour cap |
LIMIT_ABOVE_POLICY | Tried to set a per-customer cap above the platform policy |