Collect M-Pesa payments via STK Push using your own paybill or till number. Bring your Daraja credentials, plug them in once, and Sozuri handles authentication, callbacks, SMS confirmation and webhook forwarding automatically.

POST https://sozuri.net/api/v1/zuka/push

Setup — Bring Your Own Shortcode

Register your M-Pesa credentials in your project’s Payments settings. You’ll need a Daraja API application from developer.safaricom.co.ke.

FieldRequired for STKDescription
Business Short CodeYesYour M-Pesa paybill or till number.
Consumer KeyYesFrom your Daraja API application.
Consumer SecretYesFrom your Daraja API application.
PasskeyYesFrom your Daraja STK Push setup.
Webhook URLOptionalYour endpoint to receive confirmed payment notifications.
Notification NumbersOptionalComma-separated numbers (cashier, manager) that receive SMS alerts on every payment.

Open your project’s Payments menu and click Configure in the top right to fill in the credentials:

Payments settings dialog

1. Initiate STK Push

This sends an M-Pesa payment prompt directly to your customer’s phone. They enter their M-Pesa PIN and Sozuri handles the rest — recording the transaction, sending SMS notifications, and forwarding the confirmation to your webhook.

Request parameters
FieldRequiredTypeDescription
phoneYesStringThe customer’s Kenyan phone number in any format (07xx, 254xx, +254xx). Sozuri normalises it.
amountYesIntegerAmount in KES. Minimum 1.
account_referenceNoStringMax 12 characters. Invoice number, student ID, anything you want to see on the M-Pesa statement. Defaults to your project code.
Sample request
POST /api/v1/zuka/push HTTP/1.1
Host: sozuri.net
Authorization: Bearer YOUR_PROJECT_API_KEY
Content-Type: application/json
Accept: application/json

{
    "phone": "0712345678",
    "amount": 500,
    "account_reference": "INV-001"
}
<?php

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_URL            => 'https://sozuri.net/api/v1/zuka/push',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => json_encode([
        'phone'             => '0712345678',
        'amount'            => 500,
        'account_reference' => 'INV-001',
    ]),
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer YOUR_PROJECT_API_KEY',
        'Content-Type: application/json',
        'Accept: application/json',
    ],
]);

$response = curl_exec($curl);
curl_close($curl);

$result = json_decode($response, true);
echo $result['checkout_request_id'];
const response = await fetch('https://sozuri.net/api/v1/zuka/push', {
    method: 'POST',
    headers: {
        'Authorization': 'Bearer YOUR_PROJECT_API_KEY',
        'Content-Type': 'application/json',
        'Accept': 'application/json',
    },
    body: JSON.stringify({
        phone: '0712345678',
        amount: 500,
        account_reference: 'INV-001',
    }),
});

const data = await response.json();
console.log(data.checkout_request_id);
import requests

response = requests.post(
    "https://sozuri.net/api/v1/zuka/push",
    headers={
        "Authorization": "Bearer YOUR_PROJECT_API_KEY",
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
    json={
        "phone": "0712345678",
        "amount": 500,
        "account_reference": "INV-001",
    },
)
print(response.json())
curl --request POST 'https://sozuri.net/api/v1/zuka/push' \
    --header 'Authorization: Bearer YOUR_PROJECT_API_KEY' \
    --header 'Content-Type: application/json' \
    --header 'Accept: application/json' \
    --data '{
        "phone": "0712345678",
        "amount": 500,
        "account_reference": "INV-001"
    }'
Success response (200)
{
    "message": "STK push sent. Prompt your customer to check their phone.",
    "checkout_request_id": "ws_CO_22042026115226183725164293",
    "merchant_request_id": "7ddc-4cfc-bb79-a1658fc680ae20423067"
}
Error response (422)
{
    "error": "No active paybill with STK credentials found for this project."
}

Payment webhook callback

When a payment is confirmed, Sozuri POSTs to the webhook URL you set in your payment settings. The payload mirrors the M-Pesa C2B structure for easy parsing.

Sozuri also sends an SMS to the customer and every number in numbers_to_notify. Each SMS includes a shared OTP — the cashier can ask for it to verify the customer really paid, preventing fake-screenshot fraud.
Webhook body
{
    "TransactionType": "Pay Bill",
    "TransID": "QGH7JKLM2P",
    "TransTime": "20260422115226",
    "TransAmount": "500.00",
    "BusinessShortCode": "123456",
    "BillRefNumber": "INV-001",
    "InvoiceNumber": "",
    "OrgAccountBalance": "",
    "ThirdPartyTransID": "",
    "MSISDN": "254712345678",
    "FirstName": "John",
    "MiddleName": "",
    "LastName": "Doe"
}

Poll payment status

After initiating an STK Push, poll this endpoint to check whether the customer has completed the payment. Useful when your application can’t accept inbound webhooks.

Poll every 5 seconds. Stop after 24 attempts (2 minutes) or when the status is confirmed or fail.
StatusMeaning
pendingWaiting for the customer to enter their PIN, or for M-Pesa’s callback.
confirmedPayment successful — trans_id and amount are populated.
failCustomer cancelled, the request timed out, or the wallet had insufficient funds.
Request
GET/zuka/pay/{code}/status/{checkout_request_id}
curl https://sozuri.net/zuka/pay/YOUR_PAYBILL_CODE/status/ws_CO_22042026115226183725164293
Sample responses
// Pending
{ "status": "pending", "trans_id": null, "amount": null }

// Confirmed
{ "status": "confirmed", "trans_id": "QGH7JKLM2P", "amount": "500.00" }

// Failed
{ "status": "fail", "trans_id": null, "amount": null }

Every configured paybill automatically gets a hosted payment page anyone can open and pay from — no code required.

Payment link and QR code
https://sozuri.net/zuka/pay/{your-paybill-code}

The customer opens the link, enters their phone number and amount, and receives an STK Push. The page polls for confirmation and shows live status — perfect when you want a payment URL you can share over WhatsApp, print on a receipt, or embed in a portal.

Hosted payment page Confirmed transaction

Find your payment link in your project’s Payments dashboard. Copy it, share it, or click to test.

Use cases

Where teams plug Sozuri Zuka into their day-to-day collections.

E-commerce checkout

Trigger an STK on the customer’s phone at checkout, listen for the webhook, and ship as soon as it’s confirmed.

M
MPESA
Payment confirmation
RJ12X9PQRS Confirmed. Ksh 1,500.00 sent to GALAXION STORES on 18/5/26 at 4:21 PM. New M-PESA balance is Ksh 8,420.00. 16:21

WhatsApp / SMS payment links

Share a hosted Zuka link in a conversation — the customer pays in their browser without any technical effort on your side.

G
Galaxion Stores
business account
Hi Wanjiru, your invoice #4821 is ready. Pay KES 2,400 securely:
https://zuka.sozuri.net/inv/4821 11:02
Paid 👍 11:05 ✓✓
Payment received. Asante! Your order is now being prepared. 11:05

QR codes on receipts & tables

Print the Zuka link as a QR code on receipts, restaurant tables, donation boxes — customers scan, pay, leave.

School fees & event tickets

Use account_reference for the student ID or ticket number — reconciliation becomes a one-line database join.

Collect M-Pesa in under an hour.

Bring your Daraja credentials, plug them into a Sozuri project, and send your first STK Push from the dashboard or API today.