How STK push works
- Get a token. GET
/oauth/v1/generate?grant_type=client_credentialswith HTTP Basic auth (Consumer Key and Secret). Tokens last about an hour; cache them. - Send the STK push. POST
/mpesa/stkpush/v1/processrequest. The customer sees an M-Pesa PIN prompt. A response withResponseCode "0"only means the prompt was sent. - Receive the callback. Safaricom posts
Body.stkCallbackto your CallBackURL.ResultCode 0means paid, and only then does it includeCallbackMetadatawith the receipt number. - Query when unsure. If no callback arrives in about 90 seconds, or you get
1037, POST/mpesa/stkpushquery/v1/querywith the CheckoutRequestID.
Sandbox: https://sandbox.safaricom.co.ke. Production: https://api.safaricom.co.ke.
The Password, step by step
- Take the current time in Kenya and format it as
YYYYMMDDHHmmss, e.g.20261028074100. - Join, with nothing in between:
BusinessShortCode + Passkey + Timestamp. - Base64-encode the result. That's the Password.
- Send the same Timestamp in the request body. Generating the time twice, once for the Password and once for the field, is a classic bug.
Request fields
| Field | Value |
|---|---|
BusinessShortCode | Your PayBill, or for Buy Goods your store (head office) number. |
TransactionType | CustomerPayBillOnline or CustomerBuyGoodsOnline. |
Amount | Whole shillings. Decimals are rejected. |
PartyA, PhoneNumber | The customer's number as 2547XXXXXXXX or 2541XXXXXXXX. |
PartyB | The PayBill number, or the till number for Buy Goods. |
CallBackURL | Public HTTPS URL. Avoid "mpesa" or "safaricom" in it, which developers report gets callbacks dropped. |
AccountReference | Up to 12 characters, shown on the customer's prompt. |
TransactionDesc | Up to 13 characters. |
Handling the callback
- Reply with
{"ResultCode": 0, "ResultDesc": "Accepted"}quickly, and do slow work afterwards. CallbackMetadatais only present on success. Code that reads it unconditionally crashes on every failed payment.- Treat
1037as "not known yet", not "failed": query before letting the customer pay again. - Callbacks can arrive twice. Update the order idempotently on CheckoutRequestID.
Every ResultCode and Daraja error is explained in the M-Pesa error code reference.
Frequently asked questions
How is the M-Pesa Daraja STK push Password generated?
Password is base64(BusinessShortCode + Passkey + Timestamp), with the three values joined with no separators. Timestamp is the current time in YYYYMMDDHHmmss format, and you must send exactly the same Timestamp in the request body. Generate a fresh one for every request.
Why do I get 500.001.1001 on STK push?
Most often the Password doesn't match: the Timestamp in the Password differs from the Timestamp field, the passkey has a stray space or belongs to another shortcode, or BusinessShortCode is wrong. Paste your Password into the decoder on this page to see exactly which part is off. On the STK query endpoint, 500.001.1001 can also mean the payment is still being processed.
What is the difference between PayBill and Till (Buy Goods) STK push?
PayBill uses TransactionType CustomerPayBillOnline, with PartyB set to the PayBill number. Till uses CustomerBuyGoodsOnline: BusinessShortCode is your store (head office) number and PartyB is the till number. Use the passkey Safaricom issued for the BusinessShortCode you send.
What timezone should the Daraja Timestamp use?
Use Kenyan time (Africa/Nairobi, UTC+3). Servers in other timezones are a common source of confusing failures. The generated code always formats the time in Africa/Nairobi.
Where do I find the sandbox shortcode and passkey?
In the Daraja portal, open your sandbox app's test credentials (simulator) page. The sandbox PayBill is normally 174379, with a matching test passkey shown there. Production passkeys are sent by Safaricom when your shortcode goes live.
Related
- M-Pesa STK push in Node.js: a full integration with callbacks, 1037 handling and tests.
- M-Pesa STK push in Laravel: a service class, safe callbacks confirmed by STK query, a queued job and tests.
- M-Pesa STK push in Django: a requests-based client, callbacks confirmed by STK query, a reconcile command and tests.
- M-Pesa callbacks and ResultCode 1037: why callbacks go missing, which codes are final, and how to settle every push once.
- M-Pesa B2C payouts in Node.js: SecurityCredential, results matched to payouts, and payouts that are never sent twice.
- M-Pesa Daraja error codes: 1032, 1037, 2001, 404.001.03 and more.
- African Phone Number Formatter: check a number is Safaricom before sending a push.
Checked October 2026 against published Daraja integration guides. Safaricom's own documentation is on the Daraja portal at developer.safaricom.co.ke.