Daraja API · Technical Tutorial
M-Pesa STK Push Tutorial 2026: Complete Daraja API Implementation Guide
Step-by-step guide to implementing M-Pesa STK Push on your Kenyan website or app — from sandbox setup to production. With Node.js code examples.
M-Pesa STK Push is the Safaricom Daraja API method that lets you send a payment prompt directly to a customer's phone — they just enter their PIN to pay. No Pay Bill numbers to type, no account references to remember. It's the right way to handle M-Pesa on any modern Kenyan e-commerce site or app.
This guide walks you through the entire implementation: getting Daraja credentials, calling the STK Push API, handling the callback, and deploying to production. Code examples are in Node.js but the same patterns translate to PHP, Python, or any other backend language.
Prerequisites Before You Start
- A Safaricom Daraja account at developer.safaricom.co.ke
- For production: a Pay Bill number or Till number from Safaricom
- A backend server with HTTPS (callback URLs require HTTPS)
- Basic understanding of REST APIs and webhooks
Step 1: Get Daraja Sandbox Credentials
Log into Daraja portal, click "My Apps", create a new app. Select the "M-Pesa Sandbox" subscription. You'll get:
- Consumer Key — public client ID
- Consumer Secret — keep this private, server-side only
For sandbox testing you also need: PassKey (default sandbox: bfb279f9aa9bdbcf158e97dd71a467cd2e0c893059b10f78e6b72ada1ed2c919), Business ShortCode (sandbox: 174379), and Test phone (254708374149).
Step 2: Get an Access Token
Daraja uses OAuth 2.0. Every STK Push request needs a fresh access token (valid for 1 hour). In Node.js:
async function getAccessToken() {
const auth = Buffer.from(`${CONSUMER_KEY}:${CONSUMER_SECRET}`).toString('base64');
const res = await fetch(
'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
{ headers: { Authorization: `Basic ${auth}` } }
);
const data = await res.json();
return data.access_token;
}Step 3: Initiate STK Push
Send the STK Push request with the customer's phone number and amount:
async function stkPush({ phone, amount, accountRef, description }) {
const token = await getAccessToken();
const timestamp = new Date()
.toISOString().replace(/[^0-9]/g, '').slice(0, 14);
const password = Buffer.from(
`${SHORTCODE}${PASSKEY}${timestamp}`
).toString('base64');
const payload = {
BusinessShortCode: SHORTCODE,
Password: password,
Timestamp: timestamp,
TransactionType: 'CustomerPayBillOnline',
Amount: amount,
PartyA: phone, // 254XXXXXXXXX format
PartyB: SHORTCODE,
PhoneNumber: phone,
CallBackURL: 'https://yourdomain.com/api/mpesa/callback',
AccountReference: accountRef,
TransactionDesc: description,
};
const res = await fetch(
'https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest',
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
}
);
return res.json();
}The response includes a CheckoutRequestID — store this so you can match the callback when it arrives.
Step 4: Handle the Callback
Safaricom sends a POST to your CallBackURL with the transaction result. The body looks like this on success:
{
"Body": {
"stkCallback": {
"MerchantRequestID": "...",
"CheckoutRequestID": "...",
"ResultCode": 0,
"ResultDesc": "The service request is processed successfully.",
"CallbackMetadata": {
"Item": [
{ "Name": "Amount", "Value": 100 },
{ "Name": "MpesaReceiptNumber", "Value": "QHJ7..." },
{ "Name": "PhoneNumber", "Value": 254712345678 }
]
}
}
}
}On failure, ResultCode will be non-zero (e.g., 1032 = user cancelled, 1037 = no response from user). See our Daraja API errors guide for the full list.
Your callback handler should: parse the body, look up the order by CheckoutRequestID, confirm the result with an STK Query (Safaricom does not sign callbacks, so anyone could post a fake one), mark the order paid (or failed), and respond with HTTP 200. Always respond 200 even on errors — Safaricom retries failed callbacks aggressively.
Step 5: Test in Sandbox Thoroughly
Use the test phone number 254708374149 with any 4-digit PIN. Test these scenarios:
- Successful payment
- User cancellation (cancel the prompt)
- Timeout (don't respond to the prompt)
- Invalid amount (KES 0 or negative)
- Invalid phone format
- Network failure during callback (use ngrok or similar to test offline)
Step 6: Move to Production
When ready:
- Apply for "Go Live" on Daraja portal — you need the username of a Business Administrator or Business Manager on the M-Pesa Org Portal, and Safaricom approves within 24 working hours (Monday to Friday, 8am to 5pm)
- Replace credentials with production keys
- Change all URLs from
sandbox.safaricom.co.ketoapi.safaricom.co.ke - Update PassKey to your production passkey (Safaricom emails it to you after go-live)
- Update ShortCode to your real Pay Bill or Till
- Whitelist your callback URL in Daraja settings
Common Production Pitfalls
- Callback URL not HTTPS: Safaricom rejects HTTP callbacks. Use Let's Encrypt or your hosting's SSL.
- Phone format wrong: Must be 254XXXXXXXXX (12 digits, no +, no leading 0).
- Amount format: Integer only. KES 100.50 fails — use 100 or 101.
- Callback timeouts: Your server must respond quickly. If processing takes longer, ack first then process async.
- Idempotency: Safaricom may retry callbacks. Check if the order is already paid before marking it again.
Frequently Asked Questions
What is M-Pesa STK Push?+
STK Push (SIM Toolkit Push) is a Daraja API method that sends a payment prompt directly to a customer's phone. The customer enters their PIN on their device to authorize payment — no need to manually enter Pay Bill numbers or account references. It's the gold standard for online M-Pesa checkout in Kenya.
Do I need a Pay Bill or Till Number for STK Push?+
Yes. You need either a Pay Bill (Lipa Na M-Pesa Pay Bill) or a Till Number (Buy Goods). For e-commerce websites, Pay Bill with an account reference is more common because it lets you track which order each payment relates to.
How long does M-Pesa STK Push take to process?+
The prompt usually reaches the customer within seconds. It then waits for the PIN for roughly 1–3 minutes, depending on the phone, before timing out; Safaricom does not publish an exact number. After that, Daraja sends the result to your callback URL. Confirm it with an STK Query before marking the order paid.
What's the difference between sandbox and production Daraja API?+
Sandbox uses test credentials and the test phone number 254708374149. No real money moves. Production uses your real Pay Bill, real customer phones, and real M-Pesa transactions. Test thoroughly in sandbox before switching environment URLs to production.
How much does Safaricom charge for STK Push transactions?+
As far as Safaricom's published tariffs show, there is no separate STK Push charge: STK Push payments are charged as ordinary Till or Paybill payments, and Safaricom lists no fee for using the Daraja API itself. For a Till, at the time of writing (October 2026), payments up to KES 500 cost the merchant nothing, KES 501 to 36,363 cost 0.55%, and above that a flat KES 200. Paybill tariffs are agreed with Safaricom (the business pays, the customer pays, or the cost is split), so ask your Safaricom account manager.
Need Help with M-Pesa Integration?
If you'd rather not deal with Daraja yourself, we handle M-Pesa STK Push integration as part of every e-commerce build. We've processed 12,000+ M-Pesa transactions across client stores. Get in touch.