Introduction Authentication Sandbox & Testing Error Handling STK Push STK Query Status B2C Payout C2B Register C2B Simulate Account Balance List Transactions Webhooks Node.js PHP Python

SayaPay API Reference

SayaPay provides a clean REST API to integrate M-Pesa STK Push, B2C, and C2B payments into your application. Base URLs:

SANDBOX https://sandbox.sayapay.co.ke/api/v1
LIVE https://api.sayapay.co.ke/api/v1

All requests must use HTTPS. All request/response bodies are JSON. Include Content-Type: application/json in all POST requests.

Authentication

SayaPay uses Bearer token authentication. First generate an access token using your Consumer Key and Consumer Secret from your dashboard. Tokens expire after 1 hour.

Generate Token

GET/oauth/token
Returns a short-lived access token. Send your keys as HTTP Basic Auth.
cURLShell
curl -X GET \
  https://sandbox.sayapay.co.ke/api/v1/oauth/token \
  -H "Authorization: Basic $(echo -n 'YOUR_KEY:YOUR_SECRET' | base64)"
ResponseJSON
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3599
}

STK Push

Trigger an M-Pesa Express (Lipa na M-Pesa Online) payment prompt to a customer's phone. The customer enters their PIN and funds are sent directly to your shortcode.

POST/stkpush/process
Initiate an STK Push payment request.

Request Parameters

ParameterTypeRequiredDescription
phonestringRequiredCustomer's phone in format 254XXXXXXXXX
amountintegerRequiredAmount in KES. Minimum 1.
referencestringRequiredAccount reference, e.g. invoice or order number
descriptionstringRequiredTransaction description (max 13 chars)
callbackUrlstringOptionalOverride default callback URL for this request
Node.jsJavaScript
const response = await fetch('https://sandbox.sayapay.co.ke/api/v1/stkpush/process', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    phone: '254712345678',
    amount: 1200,
    reference: 'INV-001',
    description: 'Internet bill'
  })
});

const data = await response.json();
// data.CheckoutRequestID — save this to query status later
Success ResponseJSON
{
  "ResponseCode": "0",
  "ResponseDescription": "Success. Request accepted for processing",
  "MerchantRequestID": "29115-34620561-1",
  "CheckoutRequestID": "ws_CO_191220191020363925",
  "CustomerMessage": "Success. Request accepted for processing"
}

Webhooks & Callbacks

SayaPay sends POST requests to your callback URL when payments complete. Configure your default callback in the dashboard, or override per request.

STK Callback Payload

Callback BodyJSON
{
  "Body": {
    "stkCallback": {
      "MerchantRequestID": "29115-34620561-1",
      "CheckoutRequestID": "ws_CO_191220191020363925",
      "ResultCode": 0,  // 0 = success, 1032 = cancelled
      "ResultDesc": "The service request is processed successfully.",
      "CallbackMetadata": {
        "Item": [
          { "Name": "Amount", "Value": 1200 },
          { "Name": "MpesaReceiptNumber", "Value": "NLJ7RT61SV" },
          { "Name": "TransactionDate", "Value": 20240309142233 },
          { "Name": "PhoneNumber", "Value": 254712345678 }
        ]
      }
    }
  }
}

ISP Billing Auto-Activate Example

Express.js callback handlerNode.js
app.post('/mpesa/callback', (req, res) => {
  const { stkCallback } = req.body.Body;
  
  if (stkCallback.ResultCode === 0) {
    const items = stkCallback.CallbackMetadata.Item;
    const amount = items.find(i => i.Name === 'Amount').Value;
    const receipt = items.find(i => i.Name === 'MpesaReceiptNumber').Value;
    const phone = items.find(i => i.Name === 'PhoneNumber').Value;
    
    // Update billing system, activate internet
    activateCustomer({ phone, amount, receipt });
    
    res.json({ ResultCode: 0, ResultDesc: 'Accepted' });
  } else {
    // Payment failed or cancelled by customer
    console.log('Payment failed:', stkCallback.ResultDesc);
    res.json({ ResultCode: 0, ResultDesc: 'Accepted' });
  }
});

Node.js SDK

Installnpm
npm install sayapay-node
UsageNode.js
const SayaPay = require('sayapay-node');

const client = new SayaPay({
  consumerKey: process.env.SAYAPAY_KEY,
  consumerSecret: process.env.SAYAPAY_SECRET,
  environment: 'sandbox' // or 'live'
});

// STK Push
const result = await client.stkPush({
  phone: '254712345678',
  amount: 1200,
  reference: 'INV-001',
  description: 'Internet bill'
});

// B2C Payout
const payout = await client.b2c({
  phone: '254712345678',
  amount: 5000,
  remarks: 'Salary payment'
});