M-Pesa Daraja API: The Complete Integration Guide (2026)
Daraja is Safaricom's developer platform for M-Pesa. It is how a website, app or system asks a customer to pay, finds out whether they paid, and sends money back out. This guide covers what we actually do on client projects and in our own products, including the mistakes that only show up once real money flows.
The APIs you will actually use
- STK Push (M-Pesa Express) — your system asks Safaricom to show a payment prompt on the customer's phone; they enter their PIN. This is the right choice for almost every online checkout, booking or subscription.
- STK Push Query — asks Safaricom for the final status of a prompt you sent. You need it, because callbacks sometimes never arrive.
- C2B — the customer pays from their own M-Pesa menu to your paybill or till, and Safaricom notifies your system. Useful when people pay without going through your checkout.
- B2C — your business sends money to a phone number: refunds, payouts, commissions.
- Transaction Status — looks up a transaction by its M-Pesa receipt code, for reconciling anything unclear.
Till or paybill?
Both can receive STK Push payments. With a paybill, the customer's account reference (for example an order number) comes through with the payment. With a till (Buy Goods), set the transaction type to CustomerBuyGoodsOnline; the BusinessShortCode is the store or head-office number your till sits under and PartyB is the till number itself. Mixing these two up is the most common reason a till integration fails on day one.
The bigger difference is paying out. Refunds and payouts use B2C, which needs a separate B2C shortcode. A till on its own cannot send money to customers, so if your business model involves refunds or paying vendors, plan for that from the start.
STK Push, step by step
- Get an access token. Call the OAuth endpoint with your Consumer Key and Secret (Basic auth). The token lasts about an hour, so cache it rather than requesting a new one for every payment.
- Build the password. Base64 of
Shortcode + Passkey + Timestamp, where the timestamp isYYYYMMDDHHmmssand must match the timestamp you send. - Send the request to
/mpesa/stkpush/v1/processrequestwith the amount, the customer's number in2547XXXXXXXXformat, your callback URL and an account reference. - Save the
CheckoutRequestIDagainst the order straight away. It is how you match the result later. - Receive the callback. Safaricom posts the result to your HTTPS callback URL. A
ResultCodeof 0 means paid, and the metadata includes the M-Pesa receipt number. - Confirm before you deliver. See the next section.
Do not trust the callback on its own
Your callback URL is a public web address. Anyone who discovers it can post a message that looks like a successful payment. Before an order is marked paid, confirm it: query the status with STK Push Query (or Transaction Status with the receipt code), and check that the amount and the CheckoutRequestID match what you stored. Make the handler idempotent too, so a callback that arrives twice can never credit an order twice.
Callbacks can also simply not arrive. Run a scheduled job, every few minutes, that queries every payment still pending after a short wait and settles it one way or the other. Without it you will eventually have a customer who paid and never got their order.
Result codes you will see most
0— success.1032— the customer cancelled the prompt.1037— no response from the phone (switched off, no network, or they ignored it).1— insufficient balance.2001— wrong PIN.
Show the customer a plain message for each one ("You cancelled the payment — try again?") instead of a generic failure.
The "who is this?" problem
The prompt shows the business name registered on your shortcode, not your brand. If a customer is buying from "Tixa" and the prompt says "BYKABIRO LABS", some will cancel, thinking it is a scam. We saw this ourselves. The fix is simple: tell the customer the exact name they will see, on the checkout page and in any chat, before the prompt arrives.
Security basics
- Keep the Consumer Key, Secret, Passkey and B2C credentials on your server only. Never put them in a mobile app or in front-end JavaScript; an app should ask your server to start the payment.
- Keep credentials out of the web root and out of source-code zips and backups that could end up public.
- Log every request and callback, without logging the secrets themselves.
Sandbox, then go-live
Build everything against the sandbox first: it has its own test shortcode and passkey, and the simulated prompts let you test success, cancellation and timeouts. When it works, apply to go live from the Daraja portal for your real shortcode. Safaricom will issue production credentials and the passkey for that shortcode. Then test with a few real payments of small amounts before opening it to customers.
Checklist before launch: token cached, CheckoutRequestID stored, callback confirmed with a status query, idempotent handler, reconciliation job running, payment name shown to the customer, secrets off the web root.
Rather not deal with Daraja yourself? We build and run M-Pesa integrations with server-side confirmation and daily reconciliation.
M-Pesa integration →