M-Pesa Daraja API Integration Reference¶
Sources: Safaricom APIs Postman collection (
shared-docs/mpesa apis/Safaricom APIs.postman_collection.json), Safaricom Daraja Developer Portal. Updated: March 2026
Treasury payment workflow¶
See payment-workflow.md for the full end-to-end flow (intent creation, shared pay page, initiate_url, gateway redirect). This page covers only the M-Pesa/Daraja API specifics: STK Push triggering, manual "I paid at till/agent" confirmation, and the credential model below.
Two-Tier Configuration Model¶
| Tier | Owner | Scope | Fields |
|---|---|---|---|
| Platform (Tier 1) | Codevertex superadmin | Shared across all tenants | consumer_key, consumer_secret, passkey, environment (sandbox/production) |
| Tenant (Tier 2) | Tenant admin | Unique per tenant | shortcode (Paybill or Till), initiator_name, initiator_password, account_reference |
Storage: Tier 1 credentials stored in encrypted credentials JSON blob in GatewayConfig table (encrypted at rest). Tier 2 fields stored as plain columns (mpesa_shortcode, mpesa_initiator_name, mpesa_initiator_password encrypted, mpesa_account_ref) on the tenant-level GatewayConfig row.
Authentication¶
All M-Pesa Daraja APIs require an OAuth 2.0 Bearer token obtained from:
GET https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials
Authorization: Basic base64(consumer_key:consumer_secret)
Sandbox: https://sandbox.safaricom.co.ke
Production: https://api.safaricom.co.ke
Token expires in 3600 seconds. Cache and refresh before expiry.
1. STK Push (Lipa Na M-Pesa Online / Express)¶
Purpose: Initiate customer-to-business (C2B) payment via a push prompt to the customer's phone.
Use case: Online orders, checkout payments.
Endpoint: POST /mpesa/stkpush/v1/processrequest
Request¶
{
"BusinessShortCode": "174379",
"Password": "<base64(shortcode + passkey + timestamp)>",
"Timestamp": "20250925124519",
"TransactionType": "CustomerPayBillOnline",
"Amount": "100",
"PartyA": "254708374149",
"PartyB": "174379",
"PhoneNumber": "254708374149",
"CallBackURL": "https://booksapi.codevertexafrica.com/api/v1/webhooks/mpesa/callback",
"AccountReference": "OrderRef123",
"TransactionDesc": "Payment for Order #123"
}
Password generation:
timestamp := time.Now().Format("20060102150405")
password := base64.StdEncoding.EncodeToString([]byte(shortcode + passkey + timestamp))
TransactionType: CustomerPayBillOnline (Paybill) | CustomerBuyGoodsOnline (Till)
Response¶
{
"MerchantRequestID": "29115-34620561-1",
"CheckoutRequestID": "ws_CO_191220191020363925",
"ResponseCode": "0",
"ResponseDescription": "Success. Request accepted for processing",
"CustomerMessage": "Success. Request accepted for processing"
}
Callback (async, received at CallBackURL):
{
"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": "NLJ7RT61SV"},
{"Name": "TransactionDate", "Value": 20191219102115},
{"Name": "PhoneNumber", "Value": 254708374149}
]
}
}
}
}
ResultCode: 0 = success. Any other code = failure.
2. STK Push Query¶
Purpose: Poll the status of an STK Push transaction (use when callback wasn't received).
Endpoint: POST /mpesa/stkpushquery/v1/query
{
"BusinessShortCode": "174379",
"Password": "<same as STK push>",
"Timestamp": "20250925124519",
"CheckoutRequestID": "ws_CO_191220191020363925"
}
Response: ResultCode: 0 = paid; 1032 = user cancelled; 1037 = timeout.
3. C2B — Register URLs¶
Purpose: Register confirmation/validation URLs for over-the-counter C2B payments (paybill, till).
Endpoint: POST /mpesa/c2b/v1/registerurl
{
"ShortCode": "600000",
"ResponseType": "Completed",
"ConfirmationURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/confirmation",
"ValidationURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/validation"
}
Must be registered once per production shortcode. Sandbox uses simulate.
4. C2B — Simulate (Sandbox Only)¶
Endpoint: POST /mpesa/c2b/v1/simulate
{
"ShortCode": "600000",
"CommandID": "CustomerPayBillOnline",
"Amount": 100,
"Msisdn": "254708374149",
"BillRefNumber": "account123"
}
5. B2C — Business to Customer (Payouts to Users)¶
Purpose: Send money from business shortcode to customer M-Pesa account.
Use cases: Rider earnings payouts, refunds, cashback, loyalty redemption.
Endpoint: POST /mpesa/b2c/v1/paymentrequest
{
"InitiatorName": "bengobox_initiator",
"SecurityCredential": "<RSA encrypted initiator password>",
"CommandID": "BusinessPayment",
"Amount": "5000",
"PartyA": "600000",
"PartyB": "254708374149",
"Remarks": "Rider payout week 2026-W10",
"QueueTimeOutURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/b2c-timeout",
"ResultURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/b2c-result",
"Occasion": "WeeklyPayout"
}
CommandID options:
- BusinessPayment — (no tax) general business payment
- SalaryPayment — salary disbursement
- PromotionPayment — for cashback/promotions
SecurityCredential: RSA-encrypt initiator password using M-Pesa public key certificate.
Result callback:
{
"Result": {
"ResultCode": 0,
"TransactionID": "LGR019G3J2",
"ResultParameters": {
"ResultParameter": [
{"Key": "TransactionAmount", "Value": 5000},
{"Key": "TransactionReceipt", "Value": "LGR019G3J2"},
{"Key": "ReceiverPartyPublicName", "Value": "254708374149 - John Doe"}
]
}
}
}
6. B2B — Business to Business¶
Purpose: Shortcode-to-shortcode payments (e.g., pay supplier's paybill).
Endpoint: POST /mpesa/b2b/v1/paymentrequest
{
"Initiator": "bengobox_initiator",
"SecurityCredential": "<RSA encrypted>",
"CommandID": "BusinessPayBill",
"SenderIdentifierType": "4",
"RecieverIdentifierType": "4",
"Amount": "10000",
"PartyA": "600000",
"PartyB": "600001",
"AccountReference": "Invoice001",
"Remarks": "Supplier payment",
"QueueTimeOutURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/b2b-timeout",
"ResultURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/b2b-result"
}
CommandID: BusinessPayBill | MerchantToMerchantTransfer | MerchantTransferFromMerchantToWorking
7. B2Pochi — Business to Pochi (Individual Till)¶
Endpoint: Same as B2C (/mpesa/b2c/v1/paymentrequest)
CommandID: BusinessPayment with PartyB as the Pochi till number.
8. Transaction Status Query¶
Purpose: Query the status of any M-Pesa transaction.
Endpoint: POST /mpesa/transactionstatus/v1/query
{
"Initiator": "bengobox_initiator",
"SecurityCredential": "<RSA encrypted>",
"CommandID": "TransactionStatusQuery",
"TransactionID": "LHG31AA5TX",
"PartyA": "600000",
"IdentifierType": "4",
"ResultURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/txn-status-result",
"QueueTimeOutURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/txn-timeout",
"Remarks": "Status check",
"Occasion": ""
}
9. Transaction Reversal¶
Purpose: Reverse a completed M-Pesa transaction (within 24 hours).
Endpoint: POST /mpesa/reversal/v1/request
{
"Initiator": "bengobox_initiator",
"SecurityCredential": "<RSA encrypted>",
"CommandID": "TransactionReversal",
"TransactionID": "OEI2AK4Q16",
"Amount": "100",
"ReceiverParty": "600000",
"RecieverIdentifierType": "4",
"ResultURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/reversal-result",
"QueueTimeOutURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/timeout",
"Remarks": "Duplicate payment reversal",
"Occasion": ""
}
10. Account Balance Query¶
Purpose: Query current M-Pesa shortcode balance.
Endpoint: POST /mpesa/accountbalance/v1/query
{
"Initiator": "bengobox_initiator",
"SecurityCredential": "<RSA encrypted>",
"CommandID": "AccountBalance",
"PartyA": "600000",
"IdentifierType": "4",
"Remarks": "Balance check",
"QueueTimeOutURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/timeout",
"ResultURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/balance-result"
}
11. M-Pesa Ratiba / Standing Orders¶
Purpose: Schedule recurring customer-to-business payments.
Endpoint: POST /standingorder/v1/createStandingOrderExternal
{
"StandingOrderName": "Monthly Subscription",
"BusinessShortCode": "174379",
"TransactionType": "Standing Order Customer Pay Bill",
"Amount": "500",
"PartyA": "254708374149",
"ReceiverPartyIdentifierType": "4",
"CallBackURL": "https://booksapi.codevertexafrica.com/webhooks/mpesa/ratiba",
"AccountReference": "SubRef123",
"TransactionDesc": "Monthly subscription",
"Frequency": "3",
"StartDate": "20260310",
"EndDate": "20271231"
}
Frequency: 1=daily, 2=weekly, 3=monthly, 4=quarterly, 5=half-yearly, 6=yearly.
TransactionType: "Standing Order Customer Pay Bill" (Paybill) | "Standing Order Customer Pay Merchant" (Till/Buy Goods).
Use case: Subscription billing — when a tenant enables M-Pesa, create a standing order to auto-collect monthly subscription fees.
12. QR Code Generation¶
Purpose: Generate a static QR code for the tenant's shortcode (customers scan with M-Pesa app).
Endpoint: POST /mpesa/qrcode/v1/generate
{
"MerchantName": "Acme Retail",
"RefNo": "cafe-checkout-01",
"Amount": "500",
"TrxCode": "PB",
"CPI": "174379",
"Size": "300"
}
TrxCode: PB = Paybill | BG = Buy Goods | WA = Wallet-to-Account | SB = Subscriber-to-Bank.
Returns a base64-encoded PNG of the QR code.
Webhook Endpoints in treasury-api¶
| M-Pesa Event | Internal Route |
|---|---|
| STK Push callback | POST /webhooks/mpesa/callback |
| C2B Validation | POST /webhooks/mpesa/validation |
| C2B Confirmation | POST /webhooks/mpesa/confirmation |
| B2C Result | POST /webhooks/mpesa/b2c-result |
| B2C Timeout | POST /webhooks/mpesa/b2c-timeout |
| B2B Result | POST /webhooks/mpesa/b2b-result |
| Transaction Status Result | POST /webhooks/mpesa/txn-status-result |
| Transaction Reversal Result | POST /webhooks/mpesa/reversal-result |
| Account Balance Result | POST /webhooks/mpesa/balance-result |
| M-Pesa Ratiba | POST /webhooks/mpesa/ratiba |
RSA Security Credential Generation¶
Required for B2C, B2B, Transaction Status, Reversal, Account Balance:
// Encrypt initiator password with M-Pesa public key certificate
func generateSecurityCredential(initiatorPassword, certPath string) (string, error) {
certPEM, _ := os.ReadFile(certPath)
block, _ := pem.Decode(certPEM)
cert, _ := x509.ParseCertificate(block.Bytes)
rsaKey := cert.PublicKey.(*rsa.PublicKey)
encrypted, _ := rsa.EncryptPKCS1v15(rand.Reader, rsaKey, []byte(initiatorPassword))
return base64.StdEncoding.EncodeToString(encrypted), nil
}
Certificates: sandbox | production.
References¶
- Daraja Portal
- Postman Collection:
shared-docs/mpesa apis/Safaricom APIs.postman_collection.json - Existing M-Pesa implementation:
finance-service/treasury-api/internal/modules/gateways/mpesa.go