Payouts API
Send payments to till/paybill numbers and mobile money wallets across Africa.
Mobile Money Transfer
Initiate a payout to a mobile money wallet.
POST /business/payout/transferMobile Money Request Body
| Field | Type | Required | Description |
|---|---|---|---|
senderCurrency | string | Yes | Sender’s currency code (USD, EUR, etc.) |
receiverCurrency | string | Yes | Receiver’s currency code (KES, TZS, UGX, ZAR) |
amount | number | Yes | Amount to send in receiver’s currency |
senderFirstName | string | Yes | Sender’s first name |
senderLastName | string | Yes | Sender’s last name |
senderDob | string | Yes | Sender’s date of birth (YYYY-MM-DD) |
senderIdType | string | Yes | ID type: passport, national_id, driving_license |
senderIdNumber | string | Yes | Sender’s ID number |
senderNationality | string | Yes | Sender’s nationality (2-letter country code) |
senderTelephoneNo | string | Yes | Sender’s phone number with country code |
receiverFirstName | string | Yes | Receiver’s first name |
receiverLastName | string | Yes | Receiver’s last name |
receiverPhone | string | Yes | Receiver’s phone number with country code |
relationship | string | Yes | Relationship to receiver (e.g., “Family”, “Business”) |
payoutCountry | string | Yes | Destination country code (KE, TZ, UG, ZA) |
purposeOfTransfer | string | Yes | Purpose (see allowed values below) |
sourceOfFunds | string | Yes | Source of funds (see allowed values below) |
businessReference | string | Yes | Your unique transaction reference |
Example Request - Kenya Mobile Money
curl -X POST https://api.test.wakapay.io/business/payout/transfer \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"senderCurrency": "USD",
"receiverCurrency": "KES",
"amount": 100.0,
"senderFirstName": "Alice",
"senderLastName": "Smith",
"senderDob": "1985-03-15",
"senderIdType": "passport",
"senderIdNumber": "AB1234567",
"senderNationality": "TZ",
"senderTelephoneNo": "+255712345678",
"receiverFirstName": "John",
"receiverLastName": "Doe",
"receiverPhone": "+254700000001",
"relationship": "Family",
"payoutCountry": "KE",
"purposeOfTransfer": "family_support",
"sourceOfFunds": "salary",
"businessReference": "TEST-TRANSFER-KE-001"
}'const response = await fetch(
"https://api.test.wakapay.io/business/payout/transfer",
{
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
senderCurrency: "USD",
receiverCurrency: "KES",
amount: 100.0,
senderFirstName: "Alice",
senderLastName: "Smith",
senderDob: "1985-03-15",
senderIdType: "passport",
senderIdNumber: "AB1234567",
senderNationality: "TZ",
senderTelephoneNo: "+255712345678",
receiverFirstName: "John",
receiverLastName: "Doe",
receiverPhone: "+254700000001",
relationship: "Family",
payoutCountry: "KE",
purposeOfTransfer: "family_support",
sourceOfFunds: "salary",
businessReference: "TEST-TRANSFER-KE-001",
}),
},
);
const data = await response.json();
console.log(data);Mobile Money Response (201 Created)
{
"businessReference": "TEST-TRANSFER-KE-001",
"receiverAmount": 100,
"receiverCurrency": "KES",
"senderAmount": 0.7751937984496124,
"senderCurrency": "USD",
"status": "termination_success",
"totalDebited": 0.7906976744186047,
"wakapayReference": "0ad0a4a6-364b-11f1-8c14-0242ac120008"
}Response Fields
| Field | Type | Description |
|---|---|---|
wakapayReference | string | Wakapay transaction ID |
businessReference | string | Your reference for tracking |
status | string | Transaction status (termination_success, termination_pending, termination_failure) |
senderAmount | number | Amount debited from sender wallet |
senderCurrency | string | Sender’s currency |
receiverAmount | number | Amount received by recipient |
receiverCurrency | string | Receiver’s currency |
totalDebited | number | Total amount debited (includes fees) |
Payment to Paybill / Till
Initiate a payment to a Paybill or Till number.
POST /business/payout/paymentDifference between Till and Paybill:
- Till (Lipa Number) - No account reference needed. Payment goes directly to the till number.
- Paybill - Requires an account reference (
receiverAccount). Payment goes to the paybill number with a specific account reference for tracking.
Till/Paybill Request Body
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Payment type: paybill or till |
senderCurrency | string | Yes | Sender’s currency code (USD, EUR, etc.) |
receiverCurrency | string | Yes | Receiver’s currency code (KES, TZS, UGX, ZAR) |
amount | number | Yes | Amount to send in receiver’s currency |
senderFirstName | string | Yes | Sender’s first name |
senderLastName | string | Yes | Sender’s last name |
senderDob | string | Yes | Sender’s date of birth (YYYY-MM-DD) |
senderIdType | string | Yes | ID type: passport, national_id, driving_license |
senderIdNumber | string | Yes | Sender’s ID number |
senderNationality | string | Yes | Sender’s nationality (2-letter country code) |
senderTelephoneNo | string | Yes | Sender’s phone number with country code |
receiverFirstName | string | Yes | Receiver’s first name |
receiverLastName | string | Yes | Receiver’s last name |
receiverLipaNumber | string | Yes | Paybill or Till number |
receiverAccount | string | Conditional | Paybill account/reference (required for paybill, omit for till) |
relationship | string | Yes | Relationship to receiver (e.g., “Family”, “Business”) |
payoutCountry | string | Yes | Destination country code (KE, TZ, UG, ZA) |
purposeOfTransfer | string | Yes | Purpose (see allowed values below) |
sourceOfFunds | string | Yes | Source of funds (see allowed values below) |
businessReference | string | Yes | Your unique transaction reference |
Example Request - Till Payment
curl -X POST https://api.test.wakapay.io/business/payout/payment \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "till",
"senderCurrency": "USD",
"receiverCurrency": "KES",
"amount": 100.0,
"senderFirstName": "Alice",
"senderLastName": "Smith",
"senderDob": "1985-03-15",
"senderIdType": "passport",
"senderIdNumber": "AB1234567",
"senderNationality": "TZ",
"senderTelephoneNo": "+255712345678",
"receiverFirstName": "John",
"receiverLastName": "Doe",
"receiverLipaNumber": "888880",
"relationship": "Family",
"payoutCountry": "KE",
"purposeOfTransfer": "family_support",
"sourceOfFunds": "salary",
"businessReference": "TEST-PAYMENT-KE-TILL-001"
}'const response = await fetch(
"https://api.test.wakapay.io/business/payout/payment",
{
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
type: "till",
senderCurrency: "USD",
receiverCurrency: "KES",
amount: 100.0,
senderFirstName: "Alice",
senderLastName: "Smith",
senderDob: "1985-03-15",
senderIdType: "passport",
senderIdNumber: "AB1234567",
senderNationality: "TZ",
senderTelephoneNo: "+255712345678",
receiverFirstName: "John",
receiverLastName: "Doe",
receiverLipaNumber: "888880",
relationship: "Family",
payoutCountry: "KE",
purposeOfTransfer: "family_support",
sourceOfFunds: "salary",
businessReference: "TEST-PAYMENT-KE-TILL-001",
}),
},
);
const data = await response.json();
console.log(data);Till Response (201 Created)
{
"businessReference": "TEST-PAYMENT-KE-TILL-001",
"receiverAmount": 100,
"receiverCurrency": "KES",
"senderAmount": 0.7751937984496124,
"senderCurrency": "USD",
"status": "termination_success",
"totalDebited": 0.7906976744186047,
"wakapayReference": "3454311f-364b-11f1-8c14-0242ac120008"
}Example Request - Paybill Payment
curl -X POST https://api.test.wakapay.io/business/payout/payment \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "paybill",
"senderCurrency": "USD",
"receiverCurrency": "KES",
"amount": 100.0,
"senderFirstName": "Alice",
"senderLastName": "Smith",
"senderDob": "1985-03-15",
"senderIdType": "passport",
"senderIdNumber": "AB1234567",
"senderNationality": "TZ",
"senderTelephoneNo": "+255712345678",
"receiverFirstName": "John",
"receiverLastName": "Doe",
"receiverLipaNumber": "123456",
"receiverAccount": "ABC123",
"relationship": "Family",
"payoutCountry": "KE",
"purposeOfTransfer": "family_support",
"sourceOfFunds": "salary",
"businessReference": "TEST-PAYMENT-KE-PAYBILL-001"
}'Response - Paybill (201 Created)
{
"businessReference": "TEST-PAYMENT-KE-PAYBILL-001",
"receiverAmount": 100,
"receiverCurrency": "KES",
"senderAmount": 0.7751937984496124,
"senderCurrency": "USD",
"status": "termination_success",
"totalDebited": 0.7906976744186047,
"wakapayReference": "3487eae9-364b-11f1-8c14-0242ac120008"
}Transaction Status Values
| Status | Description |
|---|---|
termination_pending | Transaction initiated, awaiting completion |
termination_success | Transaction completed successfully |
termination_failure | Transaction failed |
Purpose of Transfer - Allowed Values
All valid values for the purposeOfTransfer field:
family_support- Family support and remittanceseducation- Educational expensesbusiness_payment- Business-related paymentsmedical- Medical expensesinvestment- Investment purposesconstruction- Construction expensesrent- Rent and housinggeneral- General purposesfuel- Fuel and energyrepairs- Repairs and maintenancegift- Giftspersonal_care- Personal care expensesfood_and_groceries- Food and groceriestransport- Transportationtravel- Travel expensesshopping- Shoppingentertainment- Entertainmentdonations- Charitable donationsother- Other purposes
Source of Funds - Allowed Values
All valid values for the sourceOfFunds field:
salary- Employment salarysavings- Personal savingsbusiness_income- Business incomesale_of_assets- Sale of assetsinvestment_income- Investment returnsother- Other sources
TESTENV Test Credentials
Test Phone Numbers (Mobile Money)
| Country | Phone Number | Status | Display Name |
|---|---|---|---|
| Kenya (KE) | +254700000001 | Verified, payout success | Amina Mjema |
| Tanzania (TZ) | +255700000001 | Verified, no FX rate configured | Amina Mjema |
Test Lipa Numbers (Till/Paybill)
| Country | Type | Number | Account Ref | Status |
|---|---|---|---|---|
| Kenya (KE) | Till | 888880 | - | Verified, payout success |
| Kenya (KE) | Paybill | 123456 | ABC123 | Verified, payout success |
| Tanzania (TZ) | Till | 600000 | - | Verified, no FX rate |
| Tanzania (TZ) | Paybill | 700000 | ABC123 | Verified, no FX rate |
Note: Only USD→KES FX rate is configured in TESTENV. TZS payouts will fail with “rate USD_TZS not configured” error.
Error Responses
400 - Missing Required Field
{
"code": 0,
"error": "receiverPhone is required"
}{
"code": 0,
"error": "receiverLipaNumber is required"
}400 - Invalid purposeOfTransfer
{
"code": 0,
"error": "purposeOfTransfer must be one of: [family_support, education, business_payment, medical, investment, construction, rent, general, fuel, repairs, gift, personal_care, food_and_groceries, transport, travel, shopping, entertainment, donations, other]"
}400 - Invalid sourceOfFunds
{
"code": 0,
"error": "sourceOfFunds must be one of: [salary, savings, business_income, sale_of_assets, investment_income, other]"
}404 - FX Rate Not Configured
{
"code": 0,
"error": "rate USD_TZS not configured"
}409 - Duplicate Reference
{
"code": 0,
"error": "duplicate reference"
}412 - Insufficient Balance
{
"code": 0,
"error": "insufficient available balance"
}422 - Account Not Active
{
"code": 0,
"error": "account not active"
}Note: Non-test phone numbers in TESTENV will return this error.
Use Cases
Send Mobile Money Transfer
async function sendMobileMoneyTransfer(phone, amount, currency, countryCode) {
// Step 1: Verify recipient
const verifyResponse = await fetch(
"https://api.test.wakapay.io/business/verify-transfer",
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
countryCode: countryCode,
phoneNumber: phone,
}),
},
);
const verifyData = await verifyResponse.json();
if (!verifyData.verified) {
throw new Error("Recipient not verified");
}
console.log(`Sending to: ${verifyData.displayName}`);
// Step 2: Check FX rate
const rateResponse = await fetch(
`https://api.test.wakapay.io/business/rate?from=USD&to=${currency}`,
{
headers: { Authorization: `Bearer ${token}` },
},
);
const rateData = await rateResponse.json();
console.log(`FX Rate: ${rateData.rate}`);
// Step 3: Send transfer
const transferResponse = await fetch(
"https://api.test.wakapay.io/business/payout/transfer",
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
senderCurrency: "USD",
receiverCurrency: currency,
amount: amount,
senderFirstName: "Alice",
senderLastName: "Smith",
senderDob: "1985-03-15",
senderIdType: "passport",
senderIdNumber: "AB1234567",
senderNationality: "TZ",
senderTelephoneNo: "+255712345678",
receiverFirstName: verifyData.displayName.split(" ")[0],
receiverLastName: verifyData.displayName.split(" ")[1] || "User",
receiverPhone: phone,
relationship: "Family",
payoutCountry: countryCode,
purposeOfTransfer: "family_support",
sourceOfFunds: "salary",
businessReference: `TRF-${Date.now()}`,
}),
},
);
return await transferResponse.json();
}
// Usage
await sendMobileMoneyTransfer("+254700000001", 100, "KES", "KE");Send Till Payment
async function sendTillPayment(tillNumber, amount, currency, countryCode) {
// Step 1: Verify till number
const verifyResponse = await fetch(
"https://api.test.wakapay.io/business/verify-payment",
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
countryCode: countryCode,
type: "till",
lipaNumber: tillNumber,
}),
},
);
const verifyData = await verifyResponse.json();
if (!verifyData.verified) {
throw new Error("Till number not verified");
}
console.log(`Paying to: ${verifyData.displayName}`);
// Step 2: Send payment
const paymentResponse = await fetch(
"https://api.test.wakapay.io/business/payout/payment",
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
type: "till",
senderCurrency: "USD",
receiverCurrency: currency,
amount: amount,
senderFirstName: "Alice",
senderLastName: "Smith",
senderDob: "1985-03-15",
senderIdType: "passport",
senderIdNumber: "AB1234567",
senderNationality: "TZ",
senderTelephoneNo: "+255712345678",
receiverFirstName: "Merchant",
receiverLastName: "Name",
receiverLipaNumber: tillNumber,
relationship: "Business",
payoutCountry: countryCode,
purposeOfTransfer: "business_payment",
sourceOfFunds: "business_income",
businessReference: `TILL-${Date.now()}`,
}),
},
);
return await paymentResponse.json();
}
// Usage
await sendTillPayment("888880", 100, "KES", "KE");Best Practices
- Always use unique references - Prevents duplicate payments (409 error)
- Verify recipients first - Use verify-transfer or verify-payment endpoints
- Check FX rates - Ensure currency pair is configured before cross-currency payouts
- Display verification results - Show displayName to user for confirmation
- Handle errors gracefully - Implement proper error handling for all error codes
- Use TESTENV numbers for testing - Use provided test credentials in test environment
- Include receiverAccount for paybill - Required field for paybill payments
Supported Countries
- KE - Kenya
- TZ - Tanzania
- UG - Uganda
- ZA - South Africa
Related
- Recipient Verification - Verify recipients before sending
- Transactions - Check payment status
- Error Handling - Handle payout errors
Last updated on