This guide covers all M-Pesa driver types supported by Salami Gateway: Express (STK Push), C2B, B2C, and B2B. It includes configuration, API usage, field references, and sample payloads for each.
| Driver | Type Identifier | Use Case |
|---|---|---|
| M-Pesa Express | MpesaKeExpress |
STK Push -- prompt customer to pay from their phone |
| M-Pesa C2B | MpesaKeC2B |
Customer-to-Business -- receive paybill/till payments |
| M-Pesa B2C | MpesaKeB2C |
Business-to-Customer -- send money to phone numbers |
| M-Pesa B2B | MpesaKeB2B |
Business-to-Business -- send money between businesses |
| M-Pesa Personal | MpesaKePersonal |
Extract transactions from personal M-Pesa SMS messages |
| Environment | Base URL |
|---|---|
| Sandbox | https://sandbox.safaricom.co.ke |
| Production | https://api.safaricom.co.ke |
The is_live flag on your Payment App determines which environment is used.
STK Push sends a payment prompt directly to the customer's phone via the Lipa Na M-Pesa Online API.
| Field | Label | Description |
|---|---|---|
consumer_key |
Consumer Key | Safaricom app consumer key from the Daraja portal |
consumer_secret |
Consumer Secret | Safaricom app consumer secret |
short_code |
Short Code | Business short code (paybill or till number) |
passkey |
Passkey | Lipa Na M-Pesa Online passkey |
initiator_name |
Initiator Name | API operator username |
security_credential |
Security Credential | Base64-encoded encrypted initiator password |
POST /api/pay/{payment_app}/requestPayment
Request:
{
"phone_number": "254712345678",
"amount": 1,
"reference": "ORDER-001",
"description": "Test payment"
}
| Field | Type | Required | Description |
|---|---|---|---|
phone_number |
string | Yes | Customer phone in 2547XXXXXXXX format |
amount |
integer | Yes | Amount in KES (minimum 1) |
reference |
string | Yes | Account reference (max 12 characters) |
description |
string | No | Transaction description (max 13 characters) |
Response:
{
"data": {
"MerchantRequestID": "29115-34620561-1",
"CheckoutRequestID": "ws_CO_191220191020363925",
"ResponseCode": "0",
"ResponseDescription": "Success. Request accepted for processing",
"CustomerMessage": "Success. Request accepted for processing"
}
}
GET /api/pay/{payment_app}/checkPaymentRequestStatus/{transaction}
Response:
{
"data": {
"ResponseCode": "0",
"ResponseDescription": "The service request has been accepted successfully",
"MerchantRequestID": "29115-34620561-1",
"CheckoutRequestID": "ws_CO_191220191020363925",
"ResultCode": "0",
"ResultDesc": "The service request is processed successfully."
}
}
Result Codes:
| ResultCode | Description |
|---|---|
0 |
Success |
1 |
Insufficient balance |
1032 |
Request cancelled by user |
1037 |
DS timeout -- user did not respond |
2001 |
Wrong PIN entered |
Received at: POST /api/pay/{payment_app}/callback
Success:
{
"Body": {
"stkCallback": {
"MerchantRequestID": "29115-34620561-1",
"CheckoutRequestID": "ws_CO_191220191020363925",
"ResultCode": 0,
"ResultDesc": "The service request is processed successfully.",
"CallbackMetadata": {
"Item": [
{ "Name": "Amount", "Value": 1.00 },
{ "Name": "MpesaReceiptNumber", "Value": "QJI3R7GXWV" },
{ "Name": "Balance" },
{ "Name": "TransactionDate", "Value": 20260315143000 },
{ "Name": "PhoneNumber", "Value": 254712345678 }
]
}
}
}
}
Failure (user cancelled):
{
"Body": {
"stkCallback": {
"MerchantRequestID": "29115-34620561-1",
"CheckoutRequestID": "ws_CO_191220191020363925",
"ResultCode": 1032,
"ResultDesc": "Request cancelled by user."
}
}
}
Customer-to-Business handles payments from customers to your paybill or till number. Requires URL registration to receive confirmation and validation callbacks.
| Field | Label | Description |
|---|---|---|
consumer_key |
Consumer Key | Safaricom app consumer key |
consumer_secret |
Consumer Secret | Safaricom app consumer secret |
short_code |
Short Code | Business short code |
initiator_name |
Initiator Name | API operator username |
security_credential |
Security Credential | Encrypted initiator password |
Register your confirmation and validation URLs with Safaricom.
POST /api/pay/{payment_app}/registerUrls
Response:
{
"data": {
"ConversationID": "AG_20191219_00004492b1b6f0af4f53",
"OriginatorConversationID": "16740-34861180-1",
"ResponseDescription": "Success"
}
}
Salami automatically registers these URLs:
https://your-domain.com/api/pay/{app_id}/validateTransactionhttps://your-domain.com/api/pay/{app_id}/callback/confirmationPOST /api/pay/{payment_app}/simulateTransaction
Request:
{
"phone_number": "254708374149",
"amount": 100,
"reference": "TEST001"
}
| Field | Type | Required | Description |
|---|---|---|---|
phone_number |
string | Yes | Customer phone number (use sandbox test number) |
amount |
integer | Yes | Transaction amount |
reference |
string | Yes | Bill reference number |
Response:
{
"data": {
"ConversationID": "AG_20191219_00004492b1b6f0af4f53",
"OriginatorConversationID": "16740-34861180-1",
"ResponseDescription": "Accept the service request successfully."
}
}
Received at: ANY /api/pay/{payment_app}/validateTransaction
{
"TransactionType": "Pay Bill",
"TransID": "QJI3R7GXWV",
"TransTime": "20260315143000",
"TransAmount": "100.00",
"BusinessShortCode": "174379",
"BillRefNumber": "INV-2026-001",
"InvoiceNumber": "",
"OrgAccountBalance": "",
"ThirdPartyTransID": "",
"MSISDN": "254712345678",
"FirstName": "JOHN",
"MiddleName": "",
"LastName": "DOE"
}
Received at: ANY /api/pay/{payment_app}/callback/confirmation
{
"TransactionType": "Pay Bill",
"TransID": "QJI3R7GXWV",
"TransTime": "20260315143000",
"TransAmount": "100.00",
"BusinessShortCode": "174379",
"BillRefNumber": "INV-2026-001",
"InvoiceNumber": "",
"OrgAccountBalance": "1500.00",
"ThirdPartyTransID": "",
"MSISDN": "254712345678",
"FirstName": "JOHN",
"MiddleName": "",
"LastName": "DOE"
}
Business-to-Customer sends money from your business account to a customer's M-Pesa account.
| Field | Label | Description |
|---|---|---|
consumer_key |
Consumer Key | Safaricom app consumer key |
consumer_secret |
Consumer Secret | Safaricom app consumer secret |
short_code |
Short Code | Business short code |
initiator_name |
Initiator Name | API operator username |
security_credential |
Security Credential | Encrypted initiator password |
test_phone_number |
Test Phone Number | Phone number for sandbox testing |
POST /api/pay/{payment_app}/sendMoney
Request:
{
"phone_number": "254712345678",
"amount": 500,
"command_id": "BusinessPayment",
"remarks": "Salary payment for March",
"occasion": "March 2026 Salary"
}
| Field | Type | Required | Description |
|---|---|---|---|
phone_number |
string | Yes | Recipient in 2547XXXXXXXX format |
amount |
integer | Yes | Amount in KES |
command_id |
string | Yes | One of: BusinessPayment, SalaryPayment, PromotionPayment |
remarks |
string | No | Comments (max 100 characters) |
occasion |
string | No | Occasion description (max 100 characters) |
Command IDs:
| Command ID | Description |
|---|---|
BusinessPayment |
Normal business-to-customer payment |
SalaryPayment |
Salary disbursement |
PromotionPayment |
Promotional payment |
Response:
{
"data": {
"ConversationID": "AG_20191219_00004492b1b6f0af4f53",
"OriginatorConversationID": "16740-34861180-1",
"ResponseCode": "0",
"ResponseDescription": "Accept the service request successfully."
}
}
Received at: ANY /api/pay/{payment_app}/callback/result
{
"Result": {
"ResultType": 0,
"ResultCode": 0,
"ResultDesc": "The service request is processed successfully.",
"OriginatorConversationID": "16740-34861180-1",
"ConversationID": "AG_20191219_00004492b1b6f0af4f53",
"TransactionID": "QJI3R7GXWV",
"ResultParameters": {
"ResultParameter": [
{ "Key": "TransactionAmount", "Value": 500 },
{ "Key": "TransactionReceipt", "Value": "QJI3R7GXWV" },
{ "Key": "ReceiverPartyPublicName", "Value": "254712345678 - JOHN DOE" },
{ "Key": "TransactionCompletedDateTime", "Value": "15.03.2026 14:30:00" },
{ "Key": "B2CUtilityAccountAvailableFunds", "Value": 50000.00 },
{ "Key": "B2CWorkingAccountAvailableFunds", "Value": 100000.00 },
{ "Key": "B2CRecipientIsRegisteredCustomer", "Value": "Y" },
{ "Key": "B2CChargesPaidAccountAvailableFunds", "Value": 0.00 }
]
}
}
}
Received at: ANY /api/pay/{payment_app}/callback/timeout
{
"Result": {
"ResultType": 0,
"ResultCode": 2001,
"ResultDesc": "The initiator information is invalid.",
"OriginatorConversationID": "16740-34861180-1",
"ConversationID": "AG_20191219_00004492b1b6f0af4f53",
"TransactionID": "QJI3R7GXWV"
}
}
Business-to-Business transfers money between M-Pesa business accounts.
| Field | Label | Description |
|---|---|---|
consumer_key |
Consumer Key | Safaricom app consumer key |
consumer_secret |
Consumer Secret | Safaricom app consumer secret |
short_code |
Short Code | Sender business short code |
initiator_name |
Initiator Name | API operator username |
security_credential |
Security Credential | Encrypted initiator password |
test_phone_number |
Test Phone Number | Receiver short code for sandbox testing |
POST /api/pay/{payment_app}/sendMoney
Request:
{
"receiver_short_code": "600000",
"amount": 10000,
"command_id": "BusinessPayBill",
"reference": "PO-2026-001",
"remarks": "Supplier payment"
}
| Field | Type | Required | Description |
|---|---|---|---|
receiver_short_code |
string | Yes | Receiver business short code |
amount |
integer | Yes | Amount in KES |
command_id |
string | Yes | One of: BusinessPayBill, MerchantToMerchantTransfer, BusinessBuyGoods |
reference |
string | No | Account reference |
remarks |
string | No | Comments |
Available for: MpesaKeExpress, MpesaKeC2B, MpesaKeB2C, MpesaKeB2B
POST /api/pay/{payment_app}/reverseTransaction/{transaction}
Response:
{
"data": {
"OriginatorConversationID": "16740-34861180-1",
"ConversationID": "AG_20191219_00004492b1b6f0af4f53",
"ResponseCode": "0",
"ResponseDescription": "Accept the service request successfully."
}
}
The reversal result arrives asynchronously via the callback URL.
Available for: All M-Pesa drivers except MpesaKePersonal
GET /api/pay/{payment_app}/getTransactionStatus/{transaction}
Response:
{
"data": {
"ResponseCode": "0",
"ResponseDescription": "Accept the service request successfully.",
"OriginatorConversationID": "16740-34861180-1",
"ConversationID": "AG_20191219_00004492b1b6f0af4f53"
}
}
The full result arrives via the callback URL as an asynchronous response.
Available for: MpesaKeExpress, MpesaKeC2B, MpesaKeB2C, MpesaKeB2B
GET /api/pay/{payment_app}/checkBalance
Response:
{
"data": {
"OriginatorConversationID": "16740-34861180-1",
"ConversationID": "AG_20191219_00004492b1b6f0af4f53",
"ResponseCode": "0",
"ResponseDescription": "Accept the service request successfully."
}
}
The actual balance arrives via the callback URL.
Available for: MpesaKePersonal, MpesaKeC2B, MpesaTz, MpesaCongo
Parses M-Pesa confirmation SMS messages and extracts transaction data.
POST /api/pay/{payment_app}/extractTransaction
Request:
{
"message": "QJI3R7GXWV Confirmed. Ksh100.00 received from JOHN DOE 0712345678 on 15/3/26 at 2:30 PM. New M-PESA balance is Ksh1,500.00."
}
Extracted fields:
| Extracted Field | Source |
|---|---|
transaction_id |
Receipt number (e.g., QJI3R7GXWV) |
amount |
Amount value |
payer_name |
Sender name |
payer_account |
Sender phone number |
transaction_time |
Date and time from the SMS |
status |
STATUS_COMPLETED |
| Error Code | Description |
|---|---|
0 |
Success |
1 |
Insufficient funds |
1032 |
Request cancelled by user |
1037 |
DS timeout (user did not enter PIN) |
2001 |
Invalid initiator information |
17 |
System rules limit -- duplicate request |
26 |
System busy -- try again later |
1001 |
Unable to lock subscriber |
1025 |
Invalid phone number |
1036 |
Insufficient B2C balance |