Process Transfer
Initiates a single money transfer to a specified account, ensuring secure and reliable delivery.
Pay an external bank account from a deposit-eligible wallet.
Selecting the source wallet (walletId)
If your Business owns more than one wallet — typical once you create outlets — pass walletId in the request body to pick which wallet to debit. walletId accepts either:
- the 10-digit NUBAN of a primary wallet, or
- a
wal_<26-char-ULID>identifier returned inoutlet.depositWallet.walletId.
walletId is optional on the v1-static profile and defaults to your primary wallet for backward compatibility, but partners on the v2-hmac profile must send it on every payout. Sending a walletId you do not own returns 401 WALLET_NOT_OWNED. Sending an outlet collection wallet returns 422 WALLET_TYPE_MISMATCH (see below).
Source wallet rules
Source wallet_kind | Behaviour |
|---|---|
MAIN (primary deposit, outlet deposit) | ✅ allowed |
COLLECTION (outlet collection) | 🚫 422 WALLET_TYPE_MISMATCH: collection wallets cannot fund external payouts |
The check is structural, not balance-based. A collection wallet with a healthy balance still cannot debit out. Move the funds to a deposit wallet via POST /transfer/internal first.
Idempotency
Pass reference (≤ 255 chars) to make the call idempotent on a partner-supplied key. Behaviour:
- First call: a new transaction is created with
customerReference = your reference. The response carries the Kele-sidetransactionReference. - Retry with the same
referencefor the same wallet: the originaltransactionReferenceis returned. No new debit. No new outgoing webhook.
Use this on every retry path you have. The contract is that re-sending the same body is safe.
Status lifecycle
The synchronous response returns transactionStatus = PENDING or PROCESSING. The terminal state — SUCCESS, FAILED, or REVERSED — arrives via:
- The
bankTransfer.successorbankTransfer.failedwebhook. - Or by polling
GET /transaction/{transactionReference}(don’t poll faster than 1 Hz; let the requery cycle do its job).
Don’t ship money to your customer until the transaction is in a terminal state. PROCESSING is not “done.”
Sandbox testing
Three magic destination account numbers in the sandbox environment force specific outcomes:
accountNumber | Outcome |
|---|---|
9960000000 | Delayed failure — transactionStatus transitions PENDING → PROCESSING → FAILED via the requery cycle |
9960000001 | Instant failure — same terminal state, faster |
| any other 10-digit number | Success |
Use these to exercise your own bankTransfer.failed handler before going live.
Authorizations
Body
The amount of money to be paid out.
The name of the sender initiating the transfer.
The name associated with the transfer account.
The account number for the transfer, must be 10 digits.
The code of the bank associated with the account, must be 3 digits.
Selects which of your wallets to debit. Accepts either the 10-digit NUBAN of a primary wallet or the wal_<26-char-ULID> identifier of an outlet deposit wallet. Optional on the v1-static profile (defaults to the primary wallet); required for partners on the v2-hmac profile that own multiple wallets. Wallets you do not own return 401 WALLET_NOT_OWNED. Collection wallets return 422 WALLET_TYPE_MISMATCH: collection wallets cannot fund external payouts.
Response
Account number used in the transaction, if available
Product type involved in the transaction
Amount of the transaction
Status of the transaction
Unique transaction reference
Type of transaction (e.g., DEBIT or CREDIT)
Comment on the transaction
transaction session Id
Timestamp of when the transaction was created