Overview
Payouts are the core functionality of the Zuba platform, enabling you to send money to beneficiaries worldwide through various payment rails including international transfers, SWIFT, US domestic rails (ACH and Fedwire), crypto networks, local bank transfers, and mobile money.Payout Lifecycle
Beneficiaries & Accounts
Beneficiary Management
Before sending payouts, you must create beneficiaries with their payment details:Account Types
Zuba supports multiple account types for maximum flexibility:- Bank Accounts
- Crypto Wallets
- Mobile Money
For Ghana, Kenya, Uganda, Cameroon and Côte d’Ivoire,
bankCode is
Zuba’s internal bank identifier. Fetch the supported banks and their
codes for a country from GET /v1/banks?country=<ISO-3166-1-alpha-2>
(e.g. ?country=CM), which returns [{ "name", "bankCode" }] sorted
by name. The list is scoped to banks Zuba can currently pay, so for
Kenya and Uganda it is the payable subset rather than every registered
bank. For Nigeria, the default when country is omitted, bankCode is
the CBN short code or the NIP long code, and a beneficiary account
accepts either.bic is optional on iban accounts.iban account type, with a GB IBAN:bankCode accepts the 3-digit CBN short code (e.g. 044) or the 6-digit
NIP long code (e.g. 000014); crAccount is the 10-digit account number.bankCode is Zuba’s internal Ghana bank identifier (e.g. gh_0001);
crAccount is the 8–20 digit account number.bankCode is Zuba’s internal Kenya bank identifier (e.g. ke_0001 for KCB,
ke_0003 for Absa); crAccount is the 5–20 digit account number. KES is
dual-rail — the same currency also pays out to a mobile money wallet (see
the Mobile Money tab).bankCode is Zuba’s internal Cameroon bank identifier (e.g. cm_0001 for
Afriland First Bank, cm_0006 for BGFIBANK); crAccount is the 8–24 digit
account number. XAF is dual-rail — the same currency also pays out to a
mobile money wallet (see the Mobile Money tab).senderInfo: firstName and lastName,
phoneNumber in international format, fundOrigin (SALARY, BUSINESS, or
INVESTMENT), gender (M or F), and country (the sender’s own country
as an ISO 3166-1 alpha-2 code), or the payout is rejected at creation. The
same set applies to XAF mobile money — see the Mobile Money tab. To send
as your own workspace, omit senderInfo entirely — your account is then used
as the business originator. Supply senderInfo only to pay out on behalf of
a distinct third party; a business third-party sender must include
companyName, registrationNumber, and country (its jurisdiction of
incorporation).XAF Bank Payout (Cameroon)
bankCode is Zuba’s internal Côte d’Ivoire bank identifier (e.g. ci_0009
for Ecobank, ci_0013 for Société Générale); crAccount is the 8–24 digit
account number. XOF is dual-rail — the same currency also pays out to a
mobile money wallet (see the Mobile Money tab), and the bank rail serves
Côte d’Ivoire only.firstName
and lastName in senderInfo for an individual sender (or companyName
for a business sender). If senderInfo is omitted, your workspace’s legal
name is used as the originator’s first and last name instead. Unlike
Cameroon, Côte d’Ivoire does not need phoneNumber, fundOrigin, or
gender.Ugandan Bank AccountbankCode is Zuba’s internal Uganda bank identifier (e.g. ug_0020 for
Stanbic, ug_0002 for Absa); crAccount is the 5–20 digit account number.
UGX is dual-rail — the same currency also pays out to a mobile money
wallet (see the Mobile Money tab). The Ugandan shilling has no minor
unit, so amount must be a whole number on both rails.UGX bank payouts are in early access — a gated corridor. An unpinned
workspace’s UGX bank payout is rejected at creation with
RAIL_UNAVAILABLE.
UGX mobile money is gated on the same corridor. Enablement can be
granted per rail, so ask your account manager for the rails you need.ARS payouts are in beta — a gated corridor. Contact your account
manager to have it enabled for your workspace; until it is, a payout to
ARS is rejected at creation.
accountNumber, accountType and
accountHolderName are all required. accountType is not inferred: a CBU
and a CVU are both 22 digits and cannot be told apart by shape.senderInfo: firstName, lastName, and dateOfBirth (YYYY-MM-DD).
This is required whether you are paying as your own workspace or on behalf
of a third party.ARS Bank Payout (Argentina)
ZMW (Zambian kwacha), MWK (Malawian kwacha) and EGP (Egyptian
pound) are available in the sandbox only. Payouts to them settle with
simulated outcomes. See
Sandbox testing.
In production these currencies are not yet available and a payout to one
is rejected at creation.MZN (Mozambican metical) is simulated in the sandbox too, over mobile
money rather than a bank account. See
Mozambique (MZN) mobile money.
In production an MZN payout is rejected at creation unless your workspace
has been enabled for it.
Zambian Bank Account (sandbox only)
MWK and EGP: any bankCode is accepted in
the sandbox.Creating Payouts
Single Payout
After creating a beneficiary, use their ID to create payouts (recommended approach). Currency Fields:inputCurrency is the currency from your account you’ll be paying from, while currency is what the beneficiary will receive (automatic conversion if different):
senderInfo is optional. When omitted, your workspace is used as the
business originator. Some corridors require it: GHS mobile money needs the
sender’s first and last name, and XAF mobile money needs the sender’s name
and phone number. On a crypto payout an individual sender must also carry
dateOfBirth; see Travel Rule.When a quote is required
A quote (POST /v1/quotes, passed as quoteId) is required only for payouts funded from an NGN balance into USD, EUR or GBP. On every other pair a quoteId is optional: pass one to lock the rate for the payout, or omit it and the payout is priced at the current rate.
A cross-currency payout on a pair listed by GET /v1/fx/cross-rate-quotes/corridors goes out only where your pricing policy covers it; a listed pair nothing prices is rejected with CORRIDOR_NOT_ENABLED, with or without a quote. On such a pair the amount must also sit inside that corridor’s per-transaction band, or the payout is rejected with AMOUNT_OUT_OF_BOUNDS. Same-currency payouts carry no band on the payout path. A cross-currency pair the listing has never carried, such as sends into NGN, is priced at the current rate whether or not your policy prices it; a pair the listing carries for other clients but not for you is one your policy does not cover, and is refused. A pair whose stored rate is zero or negative is rejected with INSUFFICIENT_LIQUIDITY; a pair with no stored rate at all, or whose rate has expired, is rejected with a 400 carrying the message FX rate not available for <source> to <destination> conversion and no error code.
Batch Payouts
Process multiple payouts in a single API call. Every payout in the batch is validated independently and requires its ownroute:
Sending from a sub-account
If your workspace has sub-accounts, pass the sub-account’s ID in the optionalZuba-Account-Id header on POST /v1/payouts to send the payout from that
sub-account’s balance: the amount and fee are debited from the sub-account,
and the payout records which sub-account it belongs to. Omit the header to send
from your master (workspace) balance, exactly as the examples above.
The named sub-account must be one you own and must be active. A header naming
an account you do not own is rejected with 403 not_account_owner, and a
sub-account that is not yet active is rejected with 422 ACCOUNT_NOT_ACTIVE.
Sub-accounts are a gated feature — contact your account manager to enable them.
Payment Routes
Select the rail with theroute field on each payout request:
EUR and GBP payouts are delivered to IBAN accounts over Zuba’s
international transfer network. The rail is selected automatically from the
beneficiary’s
iban account and surfaces as international on payout reads
and in the dashboard. The former sepa_credit / sepa_inst route values
have been retired and are no longer routable.Crypto rail
Setroute: "crypto" to send a stablecoin (USDC or USDT) on-chain to a crypto
wallet beneficiary. The currency is the stablecoin and is debited from your
same-currency balance (no FX). The destination network and address come from the
beneficiary’s wallet account (see Crypto Wallets above), so the request only
references the beneficiary by id:
Crypto Payout
paid only once the
transaction has broadcast; if it is declined or signing fails, the held funds are
returned to your balance automatically. Crypto payouts are availability-gated per
environment. Contact your account manager to enable them.
Once the transfer is broadcast, GET /v1/payouts/{id} returns the on-chain
transaction hash in the txHash field (null for non-crypto payouts or before
broadcast). Use it to look the transfer up on a block explorer for the payout’s
network.
Travel Rule
In production every crypto payout is screened under the Travel Rule, whatever the amount, so both parties must be identifiable. OmitsenderInfo and your
workspace is the business originator, which needs nothing more. Otherwise:
The beneficiary refusal is a
400 whose message names dateOfBirth and
carries no code. Add the date of birth with PUT /v1/beneficiaries/{id} and
resubmit. The sandbox does not run Travel Rule screening, so a crypto payout
that succeeds there without a date of birth is refused in production.
Mobile money (XOF, XAF, GHS, KES, and UGX)
Setroute: "mobile_money" to pay out XOF to a beneficiary’s mobile money
wallet across West Africa, XAF to a wallet in Cameroon, GHS to a wallet
in Ghana, KES to a wallet in Kenya, or UGX to a wallet in Uganda. The
destination provider and phone
number come from the beneficiary’s mobile account (see Mobile Money under
Account Types above):
Mobile Money Payout
XOF/XAF/GHS/KES/UGX, or an
out-of-range amount is rejected at creation.
UGX amounts must be whole numbers. The Ugandan shilling has no minor unit, so
a fractional amount is rejected at creation on both the mobile and bank rails.
Ghana (GHS), Kenya (KES), Uganda (UGX), Cameroon (XAF), and Côte d’Ivoire (XOF)
are dual-rail. The same currency also pays out over a bank account
(route: "bank_transfer"); the rail is chosen by the beneficiary’s account type
(mobile vs bank_account). XOF mobile money covers six West African countries
but the XOF bank rail serves Côte d’Ivoire only. XAF payouts carry extra sender
KYC on both rails (fundOrigin, gender, and country) — see the Bank
Account tab. GHS mobile requires the sender’s first and last name via senderInfo
(no phone, unlike Cameroon), or the payout is rejected:
GHS Mobile Money Payout (Ghana)
senderInfo. Uganda is in early
access on either rail — see the gate note under Mobile Money in
Beneficiary account types. Supply the beneficiary and amount only:
KES Mobile Money Payout (Kenya)
amount:
UGX Mobile Money Payout (Uganda)
firstName, lastName, phoneNumber, fundOrigin, gender, and
country in senderInfo — the same set the bank rail requires — or the payout
is rejected at creation:
XAF Mobile Money Payout (Cameroon)
RAIL_UNAVAILABLE. Enablement can be granted per rail, so ask your account
manager for the rails you need.
USD rails
For USD payouts, three rails are surfaced byGET /v1/payouts/available-rails?accountId=<uuid>:
fedwire: domestic wire to a US bank. Requires routing + account number on the beneficiary.ach: batched same-day-to-3-day transfer to a US bank. Same required fields as fedwire.swift: international wire via SWIFT. Requires BIC + IBAN/account number + the beneficiary’s bank country. Destination coverage spans most major markets and expands as corridors are enabled, so it is not a fixed list:GET /v1/payouts/available-rails?accountId=<uuid>reports, per saved account, whether SWIFT currently serves that account’s destination country (availableplus anunavailableReasonwhen it does not).
route on every payout request; the beneficiary account just has to match it: a swift account for route "swift", a US bank_account for "ach" / "fedwire". In the dashboard, the rail chosen when the account was saved is remembered so senders aren’t re-prompted. Beneficiary accounts intended for SWIFT must be created with type: "swift" and supply swiftCode, accountNumber, beneficiaryCountry, and (optionally) bankName / beneficiaryAddress in data.
SWIFT payouts also require a purpose-of-payment document: request an upload URL from POST /v1/payouts/pop-documents/upload-url, upload the document, and pass the returned key as purposeOfPaymentDocumentKey on the payout. A SWIFT payout without it is rejected at creation.
A SWIFT payout funded from an NGN balance is a cross-rate send: it passes through a quote (POST /v1/quotes), is debited in the source currency, and Zuba funds the USD outflow to the destination bank. A same-currency USD→USD SWIFT payout needs no quote and is sent 1:1 from your USD balance.
Route Selection Logic
The rail is determined by the beneficiary’s account type and currency:Tracking & Status
Payout Statuses
Cancelling a payout
POST /v1/payouts/{id}/cancel cancels a payout that is still created or queued. The balance debited at creation (principal, fee and any conversion legs) is returned to the account it was taken from, and a payout.cancelled webhook is delivered. Retrying a cancel is always safe: an already-cancelled payout is returned again, and a balance return that had not completed is driven to completion. If the balance return does not complete, the call responds 500 INTERNAL_ERROR while the payout is already cancelled; the payout is flagged for our operators and a later cancel call completes the return. A payout in any status other than created, queued or cancelled is refused with 409 PAYOUT_NOT_CANCELLABLE; check its status with GET /v1/payouts/{id} instead.
From the dashboard, a receipt can be downloaded for a payout in any status except cancelled. For a payout that is still in progress, the receipt reflects its current status.
Amounts above corridor limits
Every corridor has a per-transaction band, and a payout or payout quote outside it is rejected by the standard bounds checks. Amounts above corridor limits are served by the orders channel (POST /v1/orders) for order-enabled accounts: a quote-first flow where funds are held from your balance and the trade is executed by Zuba’s desk within a stated execution window. See the Trade Desk Orders guide or contact us to enable orders on your account.
Real-time Tracking
Best Practices
Beneficiary Validation
Beneficiary Validation
- Always validate beneficiary details before creating
- Use
GET /v1/payouts/requirementsto check the beneficiary fields required for a currency before submitting - Store beneficiary IDs for repeat payments
- Keep beneficiary information up to date
Error Handling
Error Handling
- Implement proper error handling for all API calls
- Use exponential backoff for retries
- Monitor webhook notifications for status updates
- Log all transactions for audit purposes
Security
Security
- Never expose API keys in client-side code
- Validate webhook signatures
- Use HTTPS for all API communications
- Implement proper access controls
Next Steps
First Payout Guide
Step-by-step guide to send your first payout
Batch Processing
Learn how to process multiple payouts efficiently
Webhooks
Set up real-time notifications for payout status
API Reference
Complete API documentation for payouts