Payouts
This guide explains how to implement payouts, verify transaction status, and handle webhook notifications. Payouts allow you to send funds to recipients through mobile money, bank accounts, and other local payment methods.
When To Use This
Use this guide when you want to send funds from your Honeycoin balance to a recipient through mobile money, bank transfer, M-Pesa Paybill, M-Pesa Till, or a saved external account.
All payout destinations use the same payout endpoint. The main difference between payout types is the destination value and the shape of payoutMethod.
Happy Path
-
Confirm the recipient's country, currency, destination type, and account details.
-
Retrieve any required destination code, such as a mobile money operator ID or bank code.
-
Submit a payout request with a unique
externalReference. -
Store the returned
transactionIdand initialstatus. -
Listen for webhook updates and reconcile the final status with your
externalReference.
Destination Requirements
| Destination | Use Case | Required payoutMethod Fields | Notes |
|---|---|---|---|
MoMo | Mobile money wallet payout | accountName, accountNumber, sometimes code | code is the mobile money operator ID when operator selection is required |
Bank Account | Bank transfer payout | accountName, accountNumber, code | code is the bank code from the Get Banks API |
Paybill | Kenya M-Pesa Paybill payout | accountName, accountNumber, businessNumber | Kenya only |
Till | Kenya M-Pesa Till payout | accountName, accountNumber | Kenya only |
| External account | Repeat payout to a saved recipient | externalAccountId | Omit currency, country, destination, and payoutMethod when using externalAccountId |
1 - Initiate Mobile Money Payout
To send funds to a mobile money wallet, collect the required recipient information and send it to the payouts endpoint.
| Field | Type | Required | Description |
|---|---|---|---|
| amount | Number | ✅ | Amount to payout (e.g. 1000) |
| currency | String | ✅ | Currency the recipient receives (e.g. KES, UGX) |
| country | String | ✅ | ISO 3166-1 alpha-2 country code (e.g. KE, UG) |
| externalReference | String | ✅ | Your own unique ID for reconciliation (e.g. payout_12345) |
| payoutMethod | Object | ✅ | Recipient details (see below) |
| destination | String | ✅ | Set to MoMo for mobile money |
| debitCurrency | String | ❌ | Wallet currency to debit from (if different from payout currency) |
Payout Method Object (Mobile Money):
| Field | Type | Required | Description |
|---|---|---|---|
| accountName | String | ✅ | Recipient name |
| accountNumber | String | ✅ | Phone number with country code, no plus sign (e.g. 254719624552) |
| code | String | ❌ | Mobile money operator ID when operator selection is required |
Example Request:
{
"amount": 1000,
"currency": "KES",
"country": "KE",
"externalReference": "payout_seller_001",
"payoutMethod": {
"accountName": "John Doe",
"accountNumber": "254719624552",
"code": "MPESA"
},
"destination": "MoMo"
}Below is an example of the response:
Success
{
"success": true,
"message": "Payout initiated successfully.",
"data": {
"transactionId": "zxcvbnmfdffdffdf",
"status": "pending",
"amount": 1000,
"currency": "KES",
"externalReference": "payout_seller_001"
}
}Note: Use the returned transactionId (or your externalReference) to track the payout status. Mobile money payouts are processed asynchronously.
2 - Initiate Bank Account Payout
To send funds to a bank account, use the same endpoint with bank-specific details.
| Field | Type | Required | Description |
|---|---|---|---|
| amount | Number | ✅ | Amount to payout |
| currency | String | ✅ | Currency code |
| country | String | ✅ | ISO 3166-1 alpha-2 country code |
| externalReference | String | ✅ | Your own unique ID for reconciliation |
| payoutMethod | Object | ✅ | Recipient bank details (see below) |
| destination | String | ✅ | Set to Bank Account |
| debitCurrency | String | ❌ | Wallet currency to debit from |
Payout Method Object (Bank Account):
| Field | Type | Required | Description |
|---|---|---|---|
| accountName | String | ✅ | Account holder name |
| accountNumber | String | ✅ | Bank account number |
| code | String | ✅ | Bank code (retrieve from Get Banks API) |
To retrieve the correct bank code, use the Get Banks API:
GET /api/b2b/utilities/banks?country=KEExample Request:
{
"amount": 50000,
"currency": "KES",
"country": "KE",
"externalReference": "payout_vendor_bank_001",
"payoutMethod": {
"accountName": "ABC Company Ltd",
"accountNumber": "1234567890",
"code": "1243"
},
"destination": "Bank Account"
}Example Response:
{
"success": true,
"message": "Payout initiated successfully.",
"data": {
"transactionId": "zxcvbnmfdffdffdf",
"status": "pending",
"amount": 50000,
"currency": "KES",
"externalReference": "payout_vendor_bank_001"
}
}Note: Bank payouts may take longer to process than mobile money payouts. Processing times vary by bank and country.
3 - Initiate M-Pesa Paybill Payout (Kenya Only)
To send funds to an M-Pesa Paybill account, use the same top-level payout fields and set destination to Paybill.
Payout Method Object (Paybill):
| Field | Type | Required | Description |
|---|---|---|---|
| accountName | String | ✅ | Recipient name |
| accountNumber | String | ✅ | Paybill account number |
| businessNumber | String | ✅ | Paybill business number |
Example Request:
{
"amount": 5000,
"currency": "KES",
"country": "KE",
"externalReference": "payout_paybill_001",
"payoutMethod": {
"accountName": "John Doe",
"accountNumber": "1234567890",
"businessNumber": "123456"
},
"destination": "Paybill"
}Example Response:
{
"success": true,
"message": "Payout initiated successfully.",
"data": {
"transactionId": "zxcvbnmfdffdffdf",
"status": "pending",
"amount": 5000,
"currency": "KES",
"externalReference": "payout_paybill_001"
}
}4 - Initiate M-Pesa Till Payout (Kenya Only)
To send funds to an M-Pesa Till account, use the same top-level payout fields and set destination to Till.
Payout Method Object (Till):
| Field | Type | Required | Description |
|---|---|---|---|
| accountName | String | ✅ | Recipient name |
| accountNumber | String | ✅ | Till number |
Example Request:
{
"amount": 2500,
"currency": "KES",
"country": "KE",
"externalReference": "payout_till_001",
"payoutMethod": {
"accountName": "John Doe",
"accountNumber": "123456"
},
"destination": "Till"
}Example Response:
{
"success": true,
"message": "Payout initiated successfully.",
"data": {
"transactionId": "zxcvbnmfdffdffdf",
"status": "pending",
"amount": 2500,
"currency": "KES",
"externalReference": "payout_till_001"
}
}5 - Payout Using External Account
If you have previously created an external account for a recipient, you can initiate payouts using just the externalAccountId without re-entering recipient details.
When using externalAccountId, omit currency, country, destination, and payoutMethod. Honeycoin uses the saved destination details on the external account.
| Field | Type | Required | Description |
|---|---|---|---|
| amount | Number | ✅ | Amount to payout |
| externalReference | String | ✅ | Your own unique ID for reconciliation |
| externalAccountId | String | ✅ | ID of the saved external account |
Example Request:
{
"amount": 1000,
"externalReference": "payout_recurring_001",
"externalAccountId": "zxEFheZW6DlnCgXJIJDz"
}Example Response:
{
"success": true,
"message": "Payout initiated successfully.",
"data": {
"transactionId": "zxcvbnmfdffdffdf",
"status": "pending",
"amount": 1000,
"externalReference": "payout_recurring_001"
}
}For more information on creating and managing external accounts, see the Create an External Account documentation.
6 - Multi-Currency Payouts
You can initiate payouts in a recipient's local currency while debiting from a different wallet currency. Use the debitCurrency parameter to specify the wallet to debit from.
| Field | Type | Required | Description |
|---|---|---|---|
| amount | Number | ✅ | Amount in the payout currency |
| currency | String | ✅ | Currency the recipient receives (e.g. KES) |
| debitCurrency | String | ✅ | Currency to debit from your wallet (e.g. USD) |
| country | String | ✅ | Country code |
| externalReference | String | ✅ | Your own unique ID |
| payoutMethod | Object | ✅ | Recipient details |
| destination | String | ✅ | Payout destination |
Example Request:
{
"amount": 1000,
"currency": "KES",
"debitCurrency": "USD",
"country": "KE",
"externalReference": "payout_multicurrency_001",
"payoutMethod": {
"accountName": "John Doe",
"accountNumber": "254719624552"
},
"destination": "MoMo"
}Honeycoin automatically converts USD from your wallet to KES at the current exchange rate and sends the equivalent amount to the recipient.
7 - Get Payout Status
Always verify the payout status to confirm it has been processed. Use the get transaction endpoint with either:
- The transactionId from the payout response, or
- Your externalReference
Example status check:
GET /api/b2b/transactions/zxcvbnmfdffdffdfHere are examples of payout status responses:
{
"success": true,
"data": {
"transactionId": "zxcvbnmfdffdffdf",
"amount": 1000,
"type": "withdrew",
"currency": "KES",
"chargeStatus": "pending",
"status": "PENDING",
"method": "momo",
"fullTimestamp": "2025-06-17T02:16:44+03:00",
"externalReference": "payout_seller_001"
}
}{
"success": true,
"data": {
"transactionId": "zxcvbnmfdffdffdf",
"amount": 1000,
"type": "withdrew",
"currency": "KES",
"chargeStatus": "successful",
"status": "SUCCESSFUL",
"method": "momo",
"fullTimestamp": "2025-06-17T02:25:44+03:00",
"externalReference": "payout_seller_001",
"thirdPartyReference": "MPESA_REF_12345"
}
}{
"success": true,
"data": {
"transactionId": "zxcvbnmfdffdffdf",
"amount": 1000,
"type": "withdrew",
"currency": "KES",
"chargeStatus": "failed",
"status": "FAILED",
"method": "momo",
"fullTimestamp": "2025-06-17T02:30:44+03:00",
"externalReference": "payout_seller_001",
"failureReason": "Recipient account not found"
}
}8 - Handle Webhooks
Configure webhooks to receive real-time payout updates instead of polling the status endpoint.
Setup
-
Configure your webhook URL in your dashboard account
-
Implement webhook endpoint security and validation
-
Handle the webhook notifications in your application
Here are examples of payout webhook responses:
{
"event": "payout_created",
"data": {
"transactionId": "zxcvbnmfdffdffdf",
"status": "pending",
"type": "withdrew",
"externalReference": "payout_seller_001",
"amount": 1000,
"currency": "KES"
},
"timestamp": "2025-06-17T02:16:44.600Z"
}{
"event": "payout_updated",
"data": {
"transactionId": "zxcvbnmfdffdffdf",
"status": "successful",
"type": "withdrew",
"externalReference": "payout_seller_001",
"amount": 1000,
"currency": "KES",
"method": "momo",
"completedAt": "2025-06-17T02:25:44+03:00"
},
"timestamp": "2025-06-17T02:25:44.600Z"
}{
"event": "payout_updated",
"data": {
"transactionId": "zxcvbnmfdffdffdf",
"status": "failed",
"type": "withdrew",
"externalReference": "payout_seller_001",
"amount": 1000,
"currency": "KES",
"failureReason": "Recipient account not found"
},
"timestamp": "2025-06-17T02:30:44.600Z"
}9 - Handle Failed Payouts
When a payout fails, you will receive a webhook notification or see the failure status when querying the transaction. Common failure reasons include:
| Failure Reason | Cause | Action |
|---|---|---|
| Recipient account not found | Invalid account number or phone number | Verify recipient details and retry |
| Insufficient wallet balance | Your account doesn't have enough funds | Top up your account and retry |
| Invalid bank code | Bank code doesn't exist or is incorrect | Verify bank code using Get Banks API |
| Recipient limit exceeded | Amount exceeds recipient's daily limit | Reduce amount or split into multiple payouts |
| Service unavailable | Payment network is temporarily down | Retry after some time |
| Invalid currency | Currency not supported for destination | Check Supported Countries & Limits |
Best Practices
- Verify recipient information before initiating payouts to reduce failed transactions
- Use external accounts for recurring payouts to the same recipients
- Implement proper error handling for failed payouts and retry logic
- Use webhooks for real-time payout updates rather than continuously polling
- Keep detailed records of all payouts for reconciliation and auditing
- Check transaction limits before initiating payouts to ensure amounts are within allowed ranges
- Test thoroughly in sandbox before deploying to production
- Monitor payout success rates to identify potential issues with recipients or payment networks
- Implement idempotency by using unique externalReference values to prevent duplicate payouts

