Skip to Content
Wallet & payouts

Wallet & payouts

Read your wallet balance, manage the bank accounts you withdraw to, and initiate withdrawals. Withdrawals move real money, so they sit behind extra guardrails - read Withdrawals carefully before enabling them.

All endpoints share the prefix /v1/wallet and authenticate with an API key.

EndpointScope
GET /v1/wallet/balancewallet:read
GET /v1/wallet/deposit-accountswallet:read
GET /v1/wallet/payout-accountspayouts:read
GET /v1/wallet/bankspayouts:read
POST /v1/wallet/payout-accounts/resolvepayouts:read
POST /v1/wallet/payout-accountspayouts:write
GET /v1/wallet/withdrawalswallet:withdrawals:read
POST /v1/wallet/withdrawalswallet:withdrawals:create

payouts:write and wallet:withdrawals:create are finance write scopes. On a live key, both require an IP allowlist - key creation is rejected without one.

Get balance

GET /v1/wallet/balance
{ "message": "Wallet balance retrieved", "data": { "balance": 250000, "availableBalance": 245000, "pendingWithdrawals": 5000, "minimumWithdrawalAmount": 1000, "currency": "NGN" } }

availableBalance is what you can withdraw right now - it excludes funds reserved by pending withdrawals.

Deposit accounts

GET /v1/wallet/deposit-accounts

Virtual bank accounts customers (or you) can transfer into to fund the wallet. Requires the business to have completed at least Level 1 (BVN) verification.

{ "message": "Deposit accounts retrieved", "data": { "verificationLevel": 1, "isEligible": true, "accounts": [ { "id": "ba1a2b3c-...", "bankName": "Example Bank", "accountNumber": "9012345678", "accountName": "Acme Foods Ltd", "qrCodeUrl": "https://.../qr/ba1a2b3c.png", "provider": "paystack" } ], "requiredLevel": 1, "requiredAction": null } }

If isEligible is false, accounts is empty and requiredAction explains what to do (e.g. complete BVN verification).

Payout accounts

Payout accounts are the bank accounts you withdraw to.

List payout accounts

GET /v1/wallet/payout-accounts
{ "message": "Payout accounts retrieved", "data": [ { "id": "pa1a2b3c-...", "bankName": "Example Bank", "accountNumber": "0123456789", "accountName": "Acme Foods Ltd", "isDefault": true } ] }

List banks

GET /v1/wallet/banks

Returns the banks you can resolve and pay out to.

Query parameters

ParameterTypeDescription
currencystringCurrency to list banks for (defaults to your business currency)
providerstringRestrict to a specific payment provider

Resolve an account

POST /v1/wallet/payout-accounts/resolve

Verifies a bank account number and returns the registered account name. Resolve before adding an account so you store the correct name.

Body

FieldTypeRequiredDescription
account_numberstringYesAccount number to resolve
bank_codestringYesBank code from List banks
providerstringNoPayment provider to resolve through
{ "account_number": "0123456789", "bank_code": "044" }

Add a payout account

POST /v1/wallet/payout-accounts

Body

FieldTypeRequiredDescription
bank_namestringYesBank name
account_numberstringYesAccount number
account_namestringYesResolved account name
bank_codestringNoBank code
providerstringNoPayment provider

Response 201 Created - the new payout account. Send an Idempotency-Key header to avoid duplicates.

Withdrawals

Withdrawals transfer funds from your wallet to a payout account. Because a leaked key could otherwise drain a wallet, API-initiated withdrawals require all of the following:

  1. The key holds the wallet:withdrawals:create scope.
  2. The (live) key has an IP allowlist.
  3. The key has a withdrawal policy configured (set when creating/updating the key):
    • maxPerTransaction - maximum amount per withdrawal
    • maxPerDay - maximum total per rolling day
    • allowedPayoutAccountIds - only these payout accounts may be paid out to
  4. The requested payout_account_id is in the policy’s allowlist, and the amount is within both caps.

If any condition fails, the request is rejected before any money moves:

errorMeaning
ip_allowlist_requiredThe key has no IP allowlist
withdrawal_policy_requiredThe key has no withdrawal policy
payout_account_not_allowedThe payout account isn’t in the policy allowlist
amount_exceeds_per_transaction_limitAmount is over maxPerTransaction
amount_exceeds_daily_limitAmount would exceed maxPerDay for today

All of Cloove’s standard withdrawal safeguards (minimum amount, verification-level limits, atomic balance checks, and audit logging) still apply.

List withdrawals

GET /v1/wallet/withdrawals

Returns paginated withdrawal history (page, limit).

Initiate a withdrawal

POST /v1/wallet/withdrawals

Body

FieldTypeRequiredDescription
amountnumberYesAmount to withdraw
payout_account_iduuidYesA payout account in the key’s policy allowlist
{ "amount": 25000, "payout_account_id": "pa1a2b3c-..." }

Response 201 Created

{ "message": "Withdrawal submitted", "data": { "withdrawal": { "id": "w1a2b3c4-...", "type": "WALLET_WITHDRAWAL", "amount": 25000, "currency": "NGN", "status": "PENDING", "externalReference": "CLV-WD-...", "createdAt": "2026-01-15T10:00:00.000Z" }, "fee": { "amount": 50, "currency": "NGN" } } }

Always send an Idempotency-Key header when initiating a withdrawal. The transfer is processed asynchronously - track it with the wallet.withdrawal.completed and wallet.withdrawal.failed webhooks.

Last updated on