SayaPay provides a clean REST API to integrate M-Pesa STK Push, B2C, and C2B payments into your application. Base URLs:
All requests must use HTTPS. All request/response bodies are JSON. Include Content-Type: application/json in all POST requests.
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.
curl -X GET \ https://sandbox.sayapay.co.ke/api/v1/oauth/token \ -H "Authorization: Basic $(echo -n 'YOUR_KEY:YOUR_SECRET' | base64)"
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3599
}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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| phone | string | Required | Customer's phone in format 254XXXXXXXXX |
| amount | integer | Required | Amount in KES. Minimum 1. |
| reference | string | Required | Account reference, e.g. invoice or order number |
| description | string | Required | Transaction description (max 13 chars) |
| callbackUrl | string | Optional | Override default callback URL for this request |
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
{
"ResponseCode": "0",
"ResponseDescription": "Success. Request accepted for processing",
"MerchantRequestID": "29115-34620561-1",
"CheckoutRequestID": "ws_CO_191220191020363925",
"CustomerMessage": "Success. Request accepted for processing"
}SayaPay sends POST requests to your callback URL when payments complete. Configure your default callback in the dashboard, or override per request.
{
"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 }
]
}
}
}
}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' }); } });
npm install sayapay-nodeconst 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' });