Internal transfer
Initiates an intra-platform wallet-to-wallet movement (e.g. the partner’s deposit wallet → an Outlet’s wallet to fund float; or wallet consolidation between two of your Outlet wallets).
Both wallets must belong to your Business; cross-Business transfers
are rejected with WALLET_TYPE_MISMATCH. Server-side type rules also
apply (deposit-shaped wallets cannot debit collection-shaped wallets,
etc.).
Move funds between two wallets that both belong to the authenticated Business. The most common use is funding an outlet’s deposit wallet by transferring from the primary deposit wallet — so the outlet can pay customer winnings without each outlet having to be funded by Kele Operations.
Kind matrix
The server enforces what kinds of wallets can move money between each other:
| Source kind | Destination kind | Behaviour |
|---|---|---|
MAIN | MAIN | ✅ allowed (e.g. primary deposit → outlet deposit) |
COLLECTION | anything | 🚫 422 WALLET_TYPE_MISMATCH: collection wallets cannot debit on /transfer/internal |
| anything | COLLECTION | 🚫 422 WALLET_TYPE_MISMATCH: collection wallets only credit via the bank rail |
| same wallet | same wallet | 🚫 422 sourceWalletId and destinationWalletId resolve to the same wallet |
Collection wallets are inbound-only by design — the only way to credit them is via the customer-facing virtual account, which is the bank rail. Trying to push funds in via this endpoint is a policy violation, not a transient failure.
Idempotency
Pass reference to make the call idempotent on a partner-supplied key. If a prior internal-transfer DEBIT under the same Business already exists with the same reference, the original transactionReference is returned and no new ledger row is created. Idempotent retries are safe and cheap.
The lookup is per Business, not per wallet: matching is done on (businessUserId, customerReference=reference, transactionType=DEBIT, senderPlatform=destinationPlatform=our bank). Reusing the same reference from a different source wallet under the same Business will return the existing record. To get a brand-new ledger row, use a fresh reference.
Cross-tenant ownership
Either wallet belonging to a different Business is rejected with 422 WALLET_TYPE_MISMATCH — /transfer/internal only supports transfers between wallets under the authenticated Business.
Webhook on success
A internalTransfer.successful event is dispatched after the transaction settles. The payload mirrors the synchronous response.
Authorizations
Partner public key (pk_live_… or pk_test_…). Required on every request.
Partner secret. Used in the v1-static profile only.
Body
Amount in whole NGN.
Response
Boolean success flag.
HTTP status code mirrored in the body for convenience.
Aligned to the existing public-doc shape at https://docs.getkele.com/api-reference/transfer/process-transfer.