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, 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:

  1. Apply for "Go Live" on Daraja portal — Safaricom reviews and approves within 1–3 business days
  2. Replace credentials with production keys
  3. Change all URLs from sandbox.safaricom.co.ke to api.safaricom.co.ke
  4. Update PassKey to your production passkey
  5. Update ShortCode to your real Pay Bill or Till
  6. 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 within 30 seconds. 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?+

Customer prompt arrives within 5–10 seconds. Customer has 60 seconds to enter PIN. Daraja sends the result callback to your server within another 10–30 seconds typically. Total: 30–90 seconds end to end.

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?+

Safaricom charges Pay Bill merchants based on a tiered structure — typically 0.5–1% of transaction value with monthly minimums. Apply for the merchant rate through your Safaricom relationship manager. There are no per-API-call fees from Daraja itself.

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.