The KCB Funds Transfer API enables businesses and applications to securely initiate funds transfers programmatically without requiring manual intervention through the KCB banking platform (Mobile app and Web). This guide walks you through the API, request parameters, authentication, transaction flow, and how to successfully integrate and test a funds transfer.
What Is the KCB Funds Transfer API?
Banks have always let you move money through their apps or teller counters. The KCB Funds Transfer (FT) API lets you do that same thing from your own application - by writing code.
Instead of a human clicking "Send Money" in the KCB app, your system sends an HTTP request to KCB's servers, and the money moves. That's it at the core.
This is useful when you're building:
- A business payment system (e.g. auto-paying suppliers)
- A SaaS platform that needs to disburse funds to users
- A financial dashboard that triggers bank transfers
- Any system where money movement needs to be automated
What Can It Actually Do?
The KCB FT API supports four types of money movement:
| Transfer Type | What It Means | Code |
|---|---|---|
| Internal Transfer | Between two KCB accounts | IF |
| RTGS | Large-value transfers to other banks | RT |
| EFT | Standard transfers to other banks | EF |
| Pesalink | Bank-to-bank transfers within Kenya's IPS | PL |
| Mobile Money | From KCB account to M-PESA wallet | MO |
You pick the type that fits your use case and pass the right code in your request.
Before You Write Any Code
There are two environments: Sandbox (for testing) and Production (real money). Always start with Sandbox.
To Get Started on Sandbox
- Register on the KCB Buni developer portal: sandbox.buni.kcbgroup.com
- Create an application on the portal
- Subscribe to the FT API - you'll get a Consumer Key and a Consumer Secret
- Have a Callback URL ready (more on this below)
- KCB will whitelist your Buni username and provide test account numbers
GOING LIVE (For Production (Real Money))
You'll additionally need:
- A real KCB Bank account number
- A signed Indemnity Form (KCB provides the template)
- An FT Request Letter for integration approval
How the API Works - The Big Picture
The KCB FT API is asynchronous. This is the most important thing to understand as a beginner.
When you send a transfer request, you don't get back "success" or "failed" immediately. Instead:
- You send the transfer request → KCB immediately replies: "Got it, we're processing"
- KCB processes the transfer in their core banking system
- KCB sends the result to your Callback URL via a POST request
This means your system needs to be able to receive incoming requests (a webhook endpoint), not just make outgoing ones. If you don't have a callback URL, you won't know whether transfers succeeded or failed.
Your App ──POST──▶ KCB FT API ──▶ Core Banking
│
└──POST──▶ Your Callback URL
(result arrives here)
Step 1: Get an Auth Token
Every API call to KCB requires a Bearer Token. You get this by calling the token endpoint using your Consumer Key and Consumer Secret from the Buni portal.
POST https://uat.buni.kcbgroup.com/token?grant_type=client_credentials
Authorization: Basic <base64(consumerKey:consumerSecret)>
The response gives you an access token. Include it as a header in all subsequent requests:
Authorization: Bearer <your_token_here>
Tokens expire, so your application should handle token refresh - request a new one when the old one stops working.
Step 2: Send a Transfer Request
Once you have a token, you can initiate a funds transfer. Here's what the request body looks like:
{
"beneficiaryDetails": "John Doe",
"companyCode": "KE0010001",
"creditAccountNumber": "1234567890",
"currency": "KES",
"debitAccountNumber": "9876543210",
"debitAmount": 5000,
"paymentDetails": "Invoice #1042 settlement",
"transactionReference": "MYAPP20240831001",
"transactionType": "IF",
"beneficiaryBankCode": "01"
}
Let's break down each field:
| Field | What It Is | Example |
|---|---|---|
beneficiaryDetails |
Full name of who receives the money | "John Doe" |
companyCode |
KCB's internal code for your bank branch | "KE0010001" |
creditAccountNumber |
Account that receives the money | "1234567890" |
currency |
Always KES for Kenyan Shillings |
"KES" |
debitAccountNumber |
Your KCB account (the one being charged) | "9876543210" |
debitAmount |
Amount to transfer (in KES) | 5000 |
paymentDetails |
Short note about the transfer reason | "Invoice settlement" |
transactionReference |
Your unique ID for this transaction | "MYAPP20240831001" |
transactionType |
Type of transfer (see table above) | "IF" |
beneficiaryBankCode |
Bank code of the recipient's bank |
"01" for KCB |
The
transactionReferencefield is critical. It must be unique per transaction - never reuse it. This is how you and KCB track the same transaction. A good pattern: prefix + date + sequential number, e.g.PAY20240831001.
Step 3: Handle the Response
When your request reaches KCB, they send back an immediate acknowledgement. Here's what success looks like:
{
"statusCode": "0",
"statusMessage": "Success",
"statusDescription": "Request received for processing",
"merchantID": "263eb626-3fe7-4662-813e-f6f2962219e1",
"retrievalRefNumber": "PCI663RSS"
}
statusCode: "0" means KCB accepted your request. Save the merchantID - you can use it to reference this transaction later.
But this is not the final result. The transfer is still being processed.
Common Error Responses
| Error | What It Means | Fix |
|---|---|---|
Missing Credentials |
Token is missing or wrong | Re-check your Bearer token |
Validation failed: Invalid crucial param |
A field is missing or wrong value | Review your request body |
Retrieval RefNumber already exist |
You reused a transactionReference
|
Generate a new unique reference |
Transaction failed due to limit rule |
Amount exceeds your daily/transaction limit | Adjust amount or check limits on FT portal |
Step 4: Handle the Callback (The Real Result)
When KCB finishes processing, they POST to your Callback URL. This is what a successful notification looks like:
{
"ftReference": "FT22060GXZGY",
"transactionDate": "2022-07-06T11:08:40.019Z",
"amount": "5000",
"transactionStatus": "SUCCESS",
"transactionMessage": "Processed Successfully",
"beneficiaryAccountNumber": "1234567890",
"beneficiaryName": "JOHN DOE",
"transactionReference": "MYAPP20240831001",
"merchantId": "1d2b13f8-ea62-465c-9fda-e18353579880",
"debitAccountNumber": "9876543210"
}
Your callback endpoint should:
- Check
transactionStatus- it'll beSUCCESSorFAILED - Match
transactionReferenceto the transfer you initiated - Update your database accordingly
- Return an HTTP
200 OKso KCB knows you received the notification
Tip: Always match incoming callbacks to your own records using
transactionReference. Never trust a callback blindly - verify the amount and accounts match what you sent.
Sending Money to M-PESA
To transfer from a KCB account to an M-PESA wallet, use transaction type MO and set beneficiaryBankCode to MPESA:
{
"transactionType": "MO",
"beneficiaryBankCode": "MPESA",
"creditAccountNumber": "2547XXXXXXXX",
"beneficiaryDetails": "Jane Wanjiku",
...
}
The creditAccountNumber in this case should be the recipient's phone number in the format 2547XXXXXXXX.
Sending to Other Banks
For inter-bank transfers (EFT, RTGS, Pesalink), you need the correct bank code for the recipient's bank. Here are the most common ones:
| Bank | Code |
|---|---|
| KCB | 01 |
| Equity Bank | 68 |
| Co-op Bank | 11 |
| NCBA | 07 |
| Stanbic Bank | 31 |
| I&M Bank | 57 |
| DTB | 63 |
| Family Bank | 70 |
| Absa | 03 |
| M-PESA | MPESA |
The full list is available in the API documentation appendix.
A Minimal Node.js Example
Here's a simple end-to-end example to get you oriented:
const axios = require('axios');
// Step 1: Get a token
async function getToken(consumerKey, consumerSecret) {
const credentials = Buffer.from(`${consumerKey}:${consumerSecret}`).toString('base64');
const response = await axios.post(
'https://uat.buni.kcbgroup.com/token?grant_type=client_credentials',
{},
{ headers: { Authorization: `Basic ${credentials}` } }
);
return response.data.access_token;
}
// Step 2: Initiate a transfer
async function sendTransfer(token) {
const payload = {
beneficiaryDetails: "John Doe",
companyCode: "KE0010001",
creditAccountNumber: "1234567890",
currency: "KES",
debitAccountNumber: "9876543210",
debitAmount: 1000,
paymentDetails: "Test payment",
transactionReference: `PAY${Date.now()}`, // unique reference
transactionType: "IF",
beneficiaryBankCode: "01"
};
const response = await axios.post(
'https://uat.buni.kcbgroup.com/ft/transfer', // confirm exact endpoint from KCB
payload,
{ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' } }
);
return response.data;
}
// Step 3: Callback handler (Express example)
app.post('/kcb/callback', (req, res) => {
const { transactionReference, transactionStatus, amount } = req.body;
if (transactionStatus === 'SUCCESS') {
console.log(`Transfer ${transactionReference} succeeded - KES ${amount}`);
// update your DB here
} else {
console.log(`Transfer ${transactionReference} FAILED`);
// handle failure - notify user, reverse any internal state
}
res.status(200).send('OK'); // always acknowledge
});
Common Beginner Mistakes to Avoid
1. Not saving the transactionReference
If you don't store it, you can't match the callback to your original request.
2. Confusing "Success" response with a completed transfer
A statusCode: "0" means accepted, not completed. Wait for the callback.
3. Not handling duplicate callbacks
KCB may retry sending the callback if your server returns a non-200 response. Make your callback handler idempotent - processing the same transactionReference twice should not double-credit or double-update.
4. Using production credentials in development
Always use Sandbox for testing. Real money moves in Production.
5. Hardcoding the transactionReference
It must be unique per request. Generate it dynamically.
Quick Reference
| Item | Detail |
|---|---|
| Sandbox Portal | sandbox.buni.kcbgroup.com |
| Token Endpoint | POST /token?grant_type=client_credentials |
| Auth Method | OAuth 2.0 (Basic Auth with Consumer Key + Secret) |
| Request Type | Async (result comes via callback) |
| Result Delivery | POST to your Callback URL |
| Support Email | buni@kcbgroup.com |
Next Steps
- Register on the KCB Buni portal
- Create an application and subscribe to the FT API
- Set up a test callback URL (tools like Webhook.site or ngrok are useful here)
- Run a test internal transfer between the sandbox accounts KCB provides
- Inspect the callback payload your server receives
- Once confident, apply for Production access
For questions or integration support, reach out to the KCB Buni team at buni@kcbgroup.com or Whatsapp Me here
Top comments (0)