KCB BUNI M-PESA STK Push allows you to initiate payment requests directly to a customer’s phone. KCB provides a secure API that enables businesses and developers to seamlessly collect payments from their customers. This guide will walk you through the process of authenticating, sending STK push requests, handling callbacks, and interpreting possible responses.
1. Token Generation / Authentication
Before making any API request, you need to authenticate using your Consumer Key and Consumer Secret provided during onboarding.
Endpoint:
POST https://uat.buni.kcbgroup.com/token?grant_type=client_credentials
Headers:
Authorization: Basic {Base64(consumerKey:consumerSecret)}
Sample Request:
curl --location --request POST 'https://uat.buni.kcbgroup.com/token?grant_type=client_credentials' \
--header 'Authorization: Basic XENo...ZTdh' \
--data ''
Success Response:
{
"access_token": "eyJ4NyRqV5xTbuwcwdi7...",
"token_type": "Bearer",
"expires_in": 3599
}
Token Parameters
Name | Type | Required | Description |
---|---|---|---|
access_token | String | Yes | The token you will use in all subsequent API requests as a Bearer token. |
token_type | String | Yes | Always "Bearer" . |
expires_in | Number | Yes | Time in seconds before the token expires (typically 3600 seconds = 1 hour). |
2. Initiating an STK Push Request
Once you have the token, you can initiate an STK push request.
Endpoint:
POST https://uat.buni.kcbgroup.com/mm/api/request/1.0.0/stkpush
Headers:
Authorization: Bearer {access_token}
Content-Type: application/json
Sample Request:
{
"phoneNumber": "254708920430",
"amount": "1",
"invoiceNumber": "1123412831049",
"sharedShortCode": true,
"orgShortCode": "522522",
"orgPassKey": "",
"callbackUrl": "https://example.com/callback",
"transactionDescription": "school fee payment"
}
Request Parameters
Name | Type | Required | Description |
---|---|---|---|
phoneNumber | String | Yes | Customer’s phone number in 2547XXXXXXXX format to which STK Push will be sent. |
amount | String | Yes | Transaction amount in Kenyan Shillings (KES) to be transferred. |
invoiceNumber | String | Optional | Unique invoice reference for your system. For the case of KCB Till, Unique code assigned to each invoice. Format:KCBTILLNO-YOURACCREF |
sharedShortCode | Boolean | Yes | Indicates whether a shared short code is being used. This is KCB’S short-code (Paybill or Till) used to receive the transaction. If provided value should be set to true. |
orgShortCode | String | Optional | Your assigned Paybill or Till number used to identify an organisation and receive the transactions. |
orgPassKey | String | Optional | Unique pass key for the organization assigned by KCB. Usually leave blank |
callbackUrl | String | Yes | Your server endpoint where payment results will be sent (must be HTTPS). |
transactionDescription | String | Optional | Short description of the transaction. |
3. Success Response
If the request is valid, you will receive the following response:
{
"response": {
"MerchantRequestID": "0c43-471f-b0cb-f80bcaee5e9e242149",
"ResponseCode": "0",
"CustomerMessage": "Success. Request accepted for processing",
"CheckoutRequestID": "ws_CO_17092025150413517708920430",
"ResponseDescription": "Success. Request accepted for processing"
},
"header": {
"statusDescription": "Success. Request accepted for processing",
"statusCode": "0"
}
}
Response Parameters
Name | Type | Description |
---|---|---|
MerchantRequestID | String | Unique ID for the request generated by the API. |
CheckoutRequestID | String | Unique transaction ID for tracking the payment. |
ResponseCode | String |
0 means request accepted; non-zero means an error. |
CustomerMessage | String | Message displayed to customer confirming request acceptance. |
ResponseDescription | String | Additional details on response status. |
statusCode | String | API status code (0 = success). |
statusDescription | String | Human-readable status description. |
💻 Step 5: Real-World Code Examples
Below are working code snippets showing how to integrate KCB Buni STK Push in Node.js, PHP, and Python.
✅ Node.js Example
const axios = require('axios');
const consumerKey = "YOUR_CONSUMER_KEY";
const consumerSecret = "YOUR_CONSUMER_SECRET";
// Base64 encode key:secret
const basicAuth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString('base64');
async function getAccessToken() {
const url = 'https://uat.buni.kcbgroup.com/token?grant_type=client_credentials';
const resp = await axios.post(url, null, {
headers: {
'Authorization': `Basic ${basicAuth}`,
'Content-Type': 'application/x-www-form-urlencoded'
}
});
return resp.data.access_token;
}
async function initiateStkPush() {
const token = await getAccessToken();
const stkUrl = 'https://uat.buni.kcbgroup.com/mm/api/request/1.0.0/stkpush';
const payload = {
phoneNumber: "254708920430",
amount: "1",
invoiceNumber: "1312831049",
sharedShortCode: true,
orgShortCode: "522522",
orgPassKey: "YOUR_ORG_PASSKEY",
callbackUrl: "https://yourdomain.com/webhook",
transactionDescription: "school fee payment"
};
const resp = await axios.post(stkUrl, payload, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
console.log("STK Push Response:", resp.data);
}
initiateStkPush().catch(err => {
console.error("Error:", err.response ? err.response.data : err.message);
});
✅ PHP Example
<?php
$consumerKey = "YOUR_CONSUMER_KEY";
$consumerSecret = "YOUR_CONSUMER_SECRET";
$basicAuth = base64_encode($consumerKey . ":" . $consumerSecret);
function getAccessToken($basicAuth) {
$tokenUrl = "https://uat.buni.kcbgroup.com/token?grant_type=client_credentials";
$ch = curl_init($tokenUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Basic $basicAuth",
"Content-Type: application/x-www-form-urlencoded"
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
return $data['access_token'];
}
function initiateStkPush($token) {
$stkUrl = "https://uat.buni.kcbgroup.com/mm/api/request/1.0.0/stkpush";
$payload = [
"phoneNumber" => "254708920430",
"amount" => "1",
"invoiceNumber" => "1312831049",
"sharedShortCode" => true,
"orgShortCode" => "522522",
"orgPassKey" => "YOUR_ORG_PASSKEY",
"callbackUrl" => "https://yourdomain.com/webhook",
"transactionDescription" => "school fee payment"
];
$ch = curl_init($stkUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $token",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Usage
$token = getAccessToken($basicAuth);
$response = initiateStkPush($token);
print_r($response);
?>
✅ Python Example
import requests
import json
consumer_key = "YOUR_CONSUMER_KEY"
consumer_secret = "YOUR_CONSUMER_SECRET"
def get_access_token():
url = "https://uat.buni.kcbgroup.com/token?grant_type=client_credentials"
resp = requests.post(url, auth=(consumer_key, consumer_secret))
resp.raise_for_status()
return resp.json()["access_token"]
def initiate_stk_push(token):
stk_url = "https://uat.buni.kcbgroup.com/mm/api/request/1.0.0/stkpush"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
payload = {
"phoneNumber": "254708920430",
"amount": "1",
"invoiceNumber": "1312831049",
"sharedShortCode": True,
"orgShortCode": "522522",
"orgPassKey": "YOUR_ORG_PASSKEY",
"callbackUrl": "https://yourdomain.com/webhook",
"transactionDescription": "school fee payment"
}
resp = requests.post(stk_url, headers=headers, json=payload)
resp.raise_for_status()
return resp.json()
if __name__ == "__main__":
token = get_access_token()
response = initiate_stk_push(token)
print(json.dumps(response, indent=2))
4. Callback Response
Once the customer enters their PIN and completes the transaction, the API will send a callback to your specified callbackUrl
.
Sample Callback Payload:
{
"Body": {
"stkCallback": {
"MerchantRequestID": "0c43-471f-b0cb-f80bcaee5e9e242149",
"CheckoutRequestID": "ws_CO_17092025150413517708920430",
"ResultCode": 0,
"ResultDesc": "The service request is processed successfully.",
"CallbackMetadata": {
"Item": [
{"Name": "Amount", "Value": 1},
{"Name": "MpesaReceiptNumber", "Value": "TIH8BED1K4"},
{"Name": "Balance"},
{"Name": "TransactionDate", "Value": 20250917150425},
{"Name": "PhoneNumber", "Value": 254708920430}
]
}
}
}
}
Callback Parameters
Name | Type | Description |
---|---|---|
ResultCode | Number |
0 = Success, other codes indicate failure reasons. |
ResultDesc | String | Description of transaction outcome. |
Amount | Number | Amount paid by customer. |
MpesaReceiptNumber | String | M-PESA transaction ID. |
TransactionDate | Number | Date and time of transaction in YYYYMMDDHHMMSS format. |
PhoneNumber | Number | Customer’s phone number that made the payment. |
5. Possible Error Responses
Error Example | Description |
---|---|
{ "fault": { "code": 900901, "message": "Invalid Credentials", "description": "Invalid token" }} |
Token provided is invalid. |
{ "fault": { "code": 900902, "message": "Missing Credentials", "description": "Provide credentials" }} |
No authentication credentials provided. |
{ "response": {}, "header": { "statusDescription": "Bad Request - Invalid Remarks", "statusCode": "1" }} |
Some parameters missing or invalid. |
Successful: ResponseCode: "0"
|
Request accepted; wait for callback. |
6. Possible Callback Scenarios
-
Transaction successful →
ResultCode: 0
-
User unreachable →
ResultCode: 1037
("DS timeout user cannot be reached") -
Invalid PIN →
ResultCode: 2001
("Initiator information invalid") -
User cancels prompt →
ResultCode: 1032
("Request cancelled by user")
7. HTTP Response Codes
Code | Description |
---|---|
200 | Request processed successfully |
400 | Bad request (invalid parameters) |
401 | Unauthorized (invalid credentials) |
403 | Forbidden request |
404 | Resource not found |
500 | Internal server error |
503 | Service unavailable |
8. Going Live
To go live:
A template of the formal request letter required for go-live
Fill and Submit a signed request letter to buni@kcbgroup.com
For any other queries and support, contact buni@kcbgroup.com
For any assistance with Payment Integrations, Reach out!
Happy Coding!
Top comments (0)