Skip to main content

1. Open an account that can actually be paid

Opening is one call; becoming payable is two. Do not treat the 201 from POST /v1/accounts as the end of the flow.
The branch that matters is the second call’s status:
Never show a customer a funding instruction built from an account that is still transferReady: false.

2. Give each customer their own wallet

A wallet is an ordinary account with customerId set.
Three constraints govern this flow, all of them consequences of customers being tier 1:
  • NGN only. Any other currency is 403 international_customer_wallet_not_supported, even if your own accounts in that currency work.
  • No crypto. Crypto needs tier 2.
  • ₦50,000 ceiling, for both the balance and the daily outflow.
If those do not fit, the alternative shape is one account of your own per currency with per-user balances in your ledger — you keep your business’s tier limits and lose per-user account numbers.

3. Separate your money from your customers’

Never infer ownership from the account name. Filter on it.
A safeguarding-style reconciliation — do your customers’ wallets add up to what you are holding for them? — is two queries and a sum:
balance is a string of minor units, and it can exceed what a JSON number holds exactly. Sum it with a big-integer or decimal type in production code — the jq line above is fine for a sandbox check and wrong for a ledger.
Filters on balance are in minor units too. balance:gt=0 is “has any money”; balance:gte=100000 on an NGN account means ₦1,000, not ₦100,000.

4. Reconcile

Balances are a snapshot; transactions are the record. Use both, for different jobs. Finding accounts that never became payable is worth a scheduled job — a 202 that never completed is silent otherwise:
Anything in that list older than a few minutes needs its detail re-issued.

What you cannot do here

  • Close an account. There is no endpoint. It is a dashboard action, and a frozen account cannot be closed at all until the hold is lifted.
  • Rename an account or change its metadata. Both are fixed at creation.
  • Choose your own account number. The provider allocates it.
  • Move an account between customers. customerId is set once.
The first of these is the one that bites: an offboarding flow built entirely on this API stops one step short of done, because archiving a customer requires their wallets to be closed first, and closing is not something this API can do.