Setup your environment
Learn how to update your docs locally and deploy them to the public.
Prerequisites
Before you begin, ensure you have:
A Zuba account with API access
Your API credentials (API key and secret)
Basic knowledge of REST APIs
Get your API credentials
Log in to your Zuba Test Dashboard
Navigate to API Settings
Click Generate API Credentials
Save your Client ID, Client Secret, and Token URL securely
See the Authentication Guide for detailed instructions on obtaining access tokens.
Never expose your Client Secret in client-side code or public repositories. Always keep it secure on your server.
Rate Limits
The API enforces rate limits to ensure fair usage and platform stability:
Limit Requests Window Burst 10 per second Sustained 100 per minute Hourly 1,000 per hour
If you exceed these limits, you’ll receive a 429 Too Many Requests response. Implement exponential backoff in your integration to handle rate limiting gracefully.
Create your first beneficiary
Before sending payouts, you need to create a beneficiary with their banking details:
curl -X POST "https://api.sandbox.zuba.com/v1/beneficiaries" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john.doe@example.com",
"country": "US",
"countrySubdivision": "NY",
"address": "123 Main St",
"city": "New York",
"postcode": "10001",
"accounts": [{
"type": "bank_account",
"currency": "USD",
"data": {
"accountNumber": "1234567890",
"routingNumber": "021000021",
"accountHolderName": "John Doe",
"bankName": "Chase Bank"
}
}]
}'
const beneficiary = await fetch ( 'https://api.sandbox.zuba.com/v1/beneficiaries' , {
method: 'POST' ,
headers: {
'Authorization' : 'Bearer YOUR_ACCESS_TOKEN' ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
name: 'John Doe' ,
email: 'john.doe@example.com' ,
country: 'US' ,
countrySubdivision: 'NY' ,
address: '123 Main St' ,
city: 'New York' ,
postcode: '10001' ,
accounts: [{
type: 'bank_account' ,
currency: 'USD' ,
data: {
accountNumber: '1234567890' ,
routingNumber: '021000021' ,
accountHolderName: 'John Doe' ,
bankName: 'Chase Bank'
}
}]
})
});
const beneficiaryData = await beneficiary . json ();
console . log ( beneficiaryData );
import requests
beneficiary = requests.post(
'https://api.sandbox.zuba.com/v1/beneficiaries' ,
headers ={
'Authorization' : 'Bearer YOUR_ACCESS_TOKEN' ,
'Content-Type' : 'application/json'
},
json ={
'name' : 'John Doe' ,
'email' : 'john.doe@example.com' ,
'country' : 'US' ,
'countrySubdivision' : 'NY' ,
'address' : '123 Main St' ,
'city' : 'New York' ,
'postcode' : '10001' ,
'accounts' : [{
'type' : 'bank_account' ,
'currency' : 'USD' ,
'data' : {
'accountNumber' : '1234567890' ,
'routingNumber' : '021000021' ,
'accountHolderName' : 'John Doe' ,
'bankName' : 'Chase Bank'
}
}]
}
)
beneficiary_data = beneficiary.json()
print (beneficiary_data)
import java.net.http.*;
import java.net.URI;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;
HttpClient client = HttpClient . newHttpClient ();
JsonObject accountData = new JsonObject ();
accountData . addProperty ( "accountNumber" , "1234567890" );
accountData . addProperty ( "routingNumber" , "021000021" );
accountData . addProperty ( "accountHolderName" , "John Doe" );
accountData . addProperty ( "bankName" , "Chase Bank" );
JsonObject account = new JsonObject ();
account . addProperty ( "type" , "bank_account" );
account . addProperty ( "currency" , "USD" );
account . add ( "data" , accountData);
JsonArray accounts = new JsonArray ();
accounts . add (account);
JsonObject requestBody = new JsonObject ();
requestBody . addProperty ( "name" , "John Doe" );
requestBody . addProperty ( "email" , "john.doe@example.com" );
requestBody . addProperty ( "country" , "US" );
requestBody . addProperty ( "countrySubdivision" , "NY" );
requestBody . addProperty ( "address" , "123 Main St" );
requestBody . addProperty ( "city" , "New York" );
requestBody . addProperty ( "postcode" , "10001" );
requestBody . add ( "accounts" , accounts);
HttpRequest request = HttpRequest . newBuilder ()
. uri ( URI . create ( "https://api.sandbox.zuba.com/v1/beneficiaries" ))
. header ( "Authorization" , "Bearer YOUR_ACCESS_TOKEN" )
. header ( "Content-Type" , "application/json" )
. POST ( HttpRequest . BodyPublishers . ofString ( new Gson (). toJson (requestBody)))
. build ();
HttpResponse < String > response = client . send (request,
HttpResponse . BodyHandlers . ofString ());
JsonObject beneficiaryData = new Gson (). fromJson ( response . body (), JsonObject . class );
System . out . println (beneficiaryData);
Check your account balance
Before sending payouts, ensure your account has sufficient funds. You can check your balance across all currencies:
curl -X GET "https://api.sandbox.zuba.com/v1/ledger/balances" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
const balances = await fetch ( 'https://api.sandbox.zuba.com/v1/ledger/balances' , {
headers: {
'Authorization' : 'Bearer YOUR_ACCESS_TOKEN'
}
});
const balanceData = await balances . json ();
console . log ( balanceData );
import requests
balances = requests.get(
'https://api.sandbox.zuba.com/v1/ledger/balances' ,
headers ={
'Authorization' : 'Bearer YOUR_ACCESS_TOKEN'
}
)
balance_data = balances.json()
print (balance_data)
HttpClient client = HttpClient . newHttpClient ();
HttpRequest request = HttpRequest . newBuilder ()
. uri ( URI . create ( "https://api.sandbox.zuba.com/v1/ledger/balances" ))
. header ( "Authorization" , "Bearer YOUR_ACCESS_TOKEN" )
. GET ()
. build ();
HttpResponse < String > response = client . send (request,
HttpResponse . BodyHandlers . ofString ());
JsonObject balanceData = new Gson (). fromJson ( response . body (), JsonObject . class );
System . out . println (balanceData);
If your account has zero balance, payouts will fail. Contact your account manager or use the dashboard to fund your account before proceeding.
Send your first payout
Now you can send a payout to your beneficiary. RECOMMENDED: Reference the beneficiary by ID (from the previous step). Note that amount is passed as a string to avoid floating-point precision issues.
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):
Sender Types: senderInfo.type is 'individual' (default when omitted) for natural-person senders, or 'business' for legal entities. The examples below use an individual sender; for a business sender, see Business sender example .
curl -X POST "https://api.sandbox.zuba.com/v1/payouts" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clientRef": "PAYOUT-INV-001",
"amount": "1000.00",
"inputCurrency": "EUR",
"currency": "NGN",
"route": "bank_transfer",
"senderInfo": {
"firstName": "Jack",
"lastName": "Jones",
"address": "456 London Road",
"city": "London",
"postalCode": "SW1A 1AA",
"country": "GB",
"dateOfBirth": "1985-06-15"
},
"beneficiary": {
"id": "BENEFICIARY_ID_FROM_PREVIOUS_STEP"
},
"reference": "Invoice #INV-001",
"description": "Payment for marketing services"
}'
const payout = await fetch ( 'https://api.sandbox.zuba.com/v1/payouts' , {
method: 'POST' ,
headers: {
'Authorization' : 'Bearer YOUR_ACCESS_TOKEN' ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
clientRef: 'PAYOUT-INV-001' ,
amount: '1000.00' ,
inputCurrency: 'EUR' ,
currency: 'NGN' ,
senderInfo: {
firstName: 'Jack' ,
lastName: 'Jones' ,
address: '456 London Road' ,
city: 'London' ,
postalCode: 'SW1A 1AA' ,
country: 'GB' ,
dateOfBirth: '1985-06-15'
},
route: 'bank_transfer' ,
beneficiary: {
id: beneficiaryData . id
},
reference: 'Invoice #INV-001' ,
description: 'Payment for marketing services'
})
});
const payoutData = await payout . json ();
console . log ( payoutData );
import requests
payout = requests.post(
'https://api.sandbox.zuba.com/v1/payouts' ,
headers ={
'Authorization' : 'Bearer YOUR_ACCESS_TOKEN' ,
'Content-Type' : 'application/json'
},
json ={
'clientRef' : 'PAYOUT-INV-001' ,
'amount' : '1000.00' ,
'inputCurrency' : 'EUR' ,
'senderInfo' : {
'firstName' : 'Jack' ,
'lastName' : 'Jones' ,
'address' : '456 London Road' ,
'city' : 'London' ,
'postalCode' : 'SW1A 1AA' ,
'country' : 'GB' ,
'dateOfBirth' : '1985-06-15'
},
'currency' : 'NGN' ,
'route' : 'bank_transfer' ,
'beneficiary' : {
'id' : beneficiary_data[ 'id' ]
},
'reference' : 'Invoice #INV-001' ,
'description' : 'Payment for marketing services'
}
)
payout_data = payout.json()
print (payout_data)
JsonObject beneficiaryRef = new JsonObject ();
beneficiaryRef . addProperty ( "id" , beneficiaryData . get ( "id" ). getAsString ());
JsonObject senderInfo = new JsonObject ();
senderInfo . addProperty ( "firstName" , "Jack" );
senderInfo . addProperty ( "lastName" , "Jones" );
senderInfo . addProperty ( "address" , "456 London Road" );
senderInfo . addProperty ( "city" , "London" );
senderInfo . addProperty ( "postalCode" , "SW1A 1AA" );
senderInfo . addProperty ( "country" , "GB" );
senderInfo . addProperty ( "dateOfBirth" , "1985-06-15" );
JsonObject payoutRequest = new JsonObject ();
payoutRequest . addProperty ( "clientRef" , "PAYOUT-INV-001" );
payoutRequest . addProperty ( "amount" , "1000.00" );
payoutRequest . addProperty ( "inputCurrency" , "EUR" );
payoutRequest . addProperty ( "currency" , "NGN" );
payoutRequest . addProperty ( "route" , "bank_transfer" );
payoutRequest . add ( "senderInfo" , senderInfo);
payoutRequest . add ( "beneficiary" , beneficiaryRef);
payoutRequest . addProperty ( "reference" , "Invoice #INV-001" );
payoutRequest . addProperty ( "description" , "Payment for marketing services" );
HttpRequest request = HttpRequest . newBuilder ()
. uri ( URI . create ( "https://api.sandbox.zuba.com/v1/payouts" ))
. header ( "Authorization" , "Bearer YOUR_ACCESS_TOKEN" )
. header ( "Content-Type" , "application/json" )
. POST ( HttpRequest . BodyPublishers . ofString ( new Gson (). toJson (payoutRequest)))
. build ();
HttpResponse < String > response = client . send (request,
HttpResponse . BodyHandlers . ofString ());
JsonObject payoutData = new Gson (). fromJson ( response . body (), JsonObject . class );
System . out . println (payoutData);
Business sender example
When the sender is a legal entity rather than a natural person, set senderInfo.type to 'business' and provide companyName, registrationNumber, and country. The registrationNumber is the company’s official registration identifier and is used as the AML pivot for sanctions and UBO screening. Business senders do not carry firstName, lastName, or dateOfBirth.
curl -X POST "https://api.sandbox.zuba.com/v1/payouts" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clientRef": "PAYOUT-INV-002",
"amount": "1000.00",
"inputCurrency": "EUR",
"currency": "NGN",
"route": "bank_transfer",
"senderInfo": {
"type": "business",
"companyName": "Acme Trading Ltd",
"registrationNumber": "12345678",
"address": "1 Finsbury Square",
"city": "London",
"postalCode": "EC2A 1AE",
"country": "GB"
},
"beneficiary": {
"id": "BENEFICIARY_ID_FROM_PREVIOUS_STEP"
},
"reference": "Invoice #INV-002",
"description": "Payment for marketing services"
}'
const payout = await fetch ( 'https://api.sandbox.zuba.com/v1/payouts' , {
method: 'POST' ,
headers: {
'Authorization' : 'Bearer YOUR_ACCESS_TOKEN' ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
clientRef: 'PAYOUT-INV-002' ,
amount: '1000.00' ,
inputCurrency: 'EUR' ,
currency: 'NGN' ,
route: 'bank_transfer' ,
senderInfo: {
type: 'business' ,
companyName: 'Acme Trading Ltd' ,
registrationNumber: '12345678' ,
address: '1 Finsbury Square' ,
city: 'London' ,
postalCode: 'EC2A 1AE' ,
country: 'GB'
},
beneficiary: {
id: beneficiaryData . id
},
reference: 'Invoice #INV-002' ,
description: 'Payment for marketing services'
})
});
import requests
payout = requests.post(
'https://api.sandbox.zuba.com/v1/payouts' ,
headers ={
'Authorization' : 'Bearer YOUR_ACCESS_TOKEN' ,
'Content-Type' : 'application/json'
},
json ={
'clientRef' : 'PAYOUT-INV-002' ,
'amount' : '1000.00' ,
'inputCurrency' : 'EUR' ,
'currency' : 'NGN' ,
'route' : 'bank_transfer' ,
'senderInfo' : {
'type' : 'business' ,
'companyName' : 'Acme Trading Ltd' ,
'registrationNumber' : '12345678' ,
'address' : '1 Finsbury Square' ,
'city' : 'London' ,
'postalCode' : 'EC2A 1AE' ,
'country' : 'GB'
},
'beneficiary' : {
'id' : beneficiary_data[ 'id' ]
},
'reference' : 'Invoice #INV-002' ,
'description' : 'Payment for marketing services'
}
)
JsonObject businessSenderInfo = new JsonObject ();
businessSenderInfo . addProperty ( "type" , "business" );
businessSenderInfo . addProperty ( "companyName" , "Acme Trading Ltd" );
businessSenderInfo . addProperty ( "registrationNumber" , "12345678" );
businessSenderInfo . addProperty ( "address" , "1 Finsbury Square" );
businessSenderInfo . addProperty ( "city" , "London" );
businessSenderInfo . addProperty ( "postalCode" , "EC2A 1AE" );
businessSenderInfo . addProperty ( "country" , "GB" );
JsonObject businessPayoutRequest = new JsonObject ();
businessPayoutRequest . addProperty ( "clientRef" , "PAYOUT-INV-002" );
businessPayoutRequest . addProperty ( "amount" , "1000.00" );
businessPayoutRequest . addProperty ( "inputCurrency" , "EUR" );
businessPayoutRequest . addProperty ( "currency" , "NGN" );
businessPayoutRequest . addProperty ( "route" , "bank_transfer" );
businessPayoutRequest . add ( "senderInfo" , businessSenderInfo);
businessPayoutRequest . add ( "beneficiary" , beneficiaryRef);
businessPayoutRequest . addProperty ( "reference" , "Invoice #INV-002" );
businessPayoutRequest . addProperty ( "description" , "Payment for marketing services" );
Track payout status
You can check the status of your payout:
curl -X GET "https://api.sandbox.zuba.com/v1/payouts/YOUR_PAYOUT_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
const status = await fetch ( `https://api.sandbox.zuba.com/v1/payouts/ ${ payoutData . id } ` , {
headers: {
'Authorization' : 'Bearer YOUR_ACCESS_TOKEN'
}
});
const statusData = await status . json ();
console . log ( 'Payout status:' , statusData . status );
import requests
status = requests.get(
f 'https://api.sandbox.zuba.com/v1/payouts/ { payout_data[ "id" ] } ' ,
headers ={
'Authorization' : 'Bearer YOUR_ACCESS_TOKEN'
}
)
status_data = status.json()
print ( f 'Payout status: { status_data[ "status" ] } ' )
String payoutId = payoutData . get ( "id" ). getAsString ();
HttpRequest request = HttpRequest . newBuilder ()
. uri ( URI . create ( "https://api.sandbox.zuba.com/v1/payouts/" + payoutId))
. header ( "Authorization" , "Bearer YOUR_ACCESS_TOKEN" )
. GET ()
. build ();
HttpResponse < String > response = client . send (request,
HttpResponse . BodyHandlers . ofString ());
JsonObject statusData = new Gson (). fromJson ( response . body (), JsonObject . class );
System . out . println ( "Payout status: " + statusData . get ( "status" ). getAsString ());
Next Steps
Authentication Learn about secure authentication methods
Webhooks Set up real-time payment notifications
Batch Payouts Process multiple payments efficiently
API Reference Explore the complete API documentation