Skip to main content
Same currency, own accounts — the shortest complete transfer. No FX quote, no route, and it usually settles immediately.
You need two NGN accounts of your own, both active with transferReady: true. See get started with accounts if you do not have them.

1. Resolve a destination

A transfer spends a destination, not an account id.
If this returns 404 destination_account_not_found, the target cannot receive. One code covers every cause, so check the account yourself: status, canCredit, currency, transferReady, and whether it is a customer wallet — wallets are excluded from own_account destinations and must be paid by tag.

2. Preview

"2500000" is ₦25,000 — minor units, as a string. The response comes back in decimals:
totalDebit is the number to show a payer: the amount plus the fee, and what actually leaves the account.
Preview does not check the balance and reserves nothing. It can succeed and the transfer can still fail with insufficient_funds.

3. Transfer

The same body, plus an externalReference you chose and stored before sending.
An own-account transfer typically comes back completed:
An external transfer comes back pending instead. That is normal — settlement is asynchronous, and the outcome arrives as a fiat.withdrawal.completed or fiat.withdrawal.failed webhook.

4. Check the ledger

The transfer is the instruction; the transaction is the record of what moved.

Three things that will bite you

  1. A failed create still creates a transfer. If execution fails, you get an error and a transfer row in failed status holding your externalReference. Retrying with that reference returns the failure, not a new attempt — mint a new reference and resolve a fresh destination.
  2. Destinations are single-use and expire in 24 hours. Resolve one per attempt. Do not store a destinationId on a payout row and reuse it.
  3. Approval, if your business uses it, is a dashboard action. A 201 with status: "awaiting_approval" means the API has done all it can.

If it failed

Next