https://lipaharakaapis.co.ke/api.php?action=
Method
POST
Content-Type
application/x-www-form-urlencoded
Auth
api_key (POST param)
Initiate a customer M‑PESA STK Push payment. The customer receives a pop‑up on their phone to enter PIN.
https://lipaharakaapis.co.ke/api.php?action=api_stk
| Parameter | Type | Description |
|---|---|---|
api_key | string | Your unique merchant API key (provided in dashboard). required |
phone | string | Customer phone: 2547XXXXXXXX or 07XXXXXXXX. required |
amount | integer | Amount in KES (min 1). required |
channel_id | integer | Payment route ID (e.g., 16). required |
{
"success": true,
"message": "STK Push initiated successfully",
"data": {
"payment_id": 1234,
"checkout_request_id": "ws_CO_123456789",
"merchant_request_id": "1234-5678-9012"
}
}
{
"success": false,
"message": "Invalid phone number format",
"code": "INVALID_PHONE"
}
<?php
$apiUrl = 'https://lipaharakaapis.co.ke/api.php?action=api_stk';
$data = [
'api_key' => 'YOUR_API_KEY',
'phone' => '254712345678',
'amount' => '100',
'channel_id' => '16'
];
$ch = curl_init($apiUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$resp = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http === 200) {
print_r(json_decode($resp, true));
} else {
echo "HTTP error $http: $resp";
}
?>
curl -X POST https://lipaharakaapis.co.ke/api.php?action=api_stk \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "api_key=YOUR_API_KEY" \
-d "phone=254712345678" \
-d "amount=100" \
-d "channel_id=16"
fetch('https://lipaharakaapis.co.ke/api.php?action=api_stk', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
api_key: 'YOUR_API_KEY',
phone: '254712345678',
amount: '100',
channel_id: '16'
})
})
.then(r => r.json())
.then(data => console.log(data))
.catch(err => console.error('Error:', err));
Send money from your merchant wallet to a customer's phone (B2C payout). Wallet balance is deducted automatically.
https://lipaharakaapis.co.ke/api.php?action=api_withdraw
| Parameter | Type | Description |
|---|---|---|
api_key | string | Your merchant API key (linked to wallet). required |
phone | string | Recipient phone (2547XXXXXXXX or 07XXXXXXXX). required |
amount | integer | Amount to withdraw (KES). Must not exceed wallet balance. required |
{
"success": true,
"message": "Withdrawal initiated successfully",
"data": {
"withdrawal_id": "WD_987654321",
"amount": 10,
"balance_after": 4520.50,
"transaction_id": "B2C_123456789"
}
}
{
"success": false,
"message": "Insufficient wallet balance",
"code": "LOW_BALANCE"
}
<?php
$apiUrl = 'https://lipaharakaapis.co.ke/api.php?action=api_withdraw';
$data = [
'api_key' => 'YOUR_API_KEY',
'phone' => '254712345678',
'amount' => '10'
];
$ch = curl_init($apiUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"User-Agent: Mozilla/5.0",
"Referer: https://lipaharakaapis.co.ke/",
]);
$resp = curl_exec($ch);
$error = curl_error($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($error) die("cURL Error: $error");
if ($http !== 200) die("HTTP Error $http – Response: $resp");
$result = json_decode($resp, true);
if ($result['success'] ?? false) {
echo "OK: " . $result['message'];
} else {
echo "API error: " . ($result['message'] ?? 'unknown');
}
?>
curl -X POST https://lipaharakaapis.co.ke/api.php?action=api_withdraw \
-d "api_key=YOUR_API_KEY" \
-d "phone=254712345678" \
-d "amount=10"
Programmatically fund your merchant wallet by sending an STK push to any phone number. Funds are credited to your wallet upon successful payment. Perfect for automating wallet top-ups from your own system.
https://lipaharakaapis.co.ke/api.php?action=api_wallet_deposit
| Parameter | Type | Description |
|---|---|---|
api_key | string | Your merchant API key (must be approved for API deposit). required |
phone | string | Phone number to charge via STK push: 2547XXXXXXXX or 07XXXXXXXX. required |
amount | integer | Amount in KES (minimum 10). required |
callback_url | string | Your HTTPS endpoint to receive the deposit result in real time. Must be HTTPS. optional |
reference | string | Your internal reference for tracking. optional |
402.
{
"ok": true,
"message": "STK Push sent. Awaiting customer confirmation.",
"transaction_id": 386,
"checkout_request_id": "ws_CO_08102026001915165794465480",
"callback_registered": true,
"callback_url": "https://your-domain.com/lipa-callback",
"service_fee": 0,
"provider_response": {
"MerchantRequestID": "7f71-4d52-85a0-09de397d193f445022",
"CheckoutRequestID": "ws_CO_08102026001915165794465480",
"ResponseCode": "0",
"ResponseDescription": "Success. Request accepted for processing",
"CustomerMessage": "Success. Request accepted for processing"
}
}
// Access not enabled (403)
{
"ok": false,
"message": "API deposit access not enabled. Please apply via the dashboard and get approval from admin."
}
// Insufficient service wallet (402)
{
"ok": false,
"message": "Merchant inactive. Wallet deposit not allowed. Please top up your service wallet."
}
// Invalid callback URL (400)
{
"ok": false,
"message": "callback_url must use HTTPS for security."
}
<?php
$apiUrl = 'https://lipaharakaapis.co.ke/api.php?action=api_wallet_deposit';
$data = [
'api_key' => 'YOUR_API_KEY',
'phone' => '254794465480',
'amount' => 100,
'reference' => 'DEP-' . time(),
'callback_url' => 'https://your-domain.com/lipa-callback.php' // optional
];
$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data),
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded']
]);
$resp = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$result = json_decode($resp, true);
if (!empty($result['ok'])) {
echo "✅ STK sent. TX#" . $result['transaction_id'] . "\n";
echo "Checkout ID: " . $result['checkout_request_id'] . "\n";
} else {
echo "❌ Failed: " . ($result['message'] ?? 'Unknown error') . "\n";
}
?>
curl -X POST "https://lipaharakaapis.co.ke/api.php?action=api_wallet_deposit" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "api_key=YOUR_API_KEY" \
-d "phone=254794465480" \
-d "amount=100" \
-d "reference=DEP-001" \
-d "callback_url=https://your-domain.com/lipa-callback.php"
const fetch = require('node-fetch');
async function depositToWallet() {
const params = new URLSearchParams();
params.append('api_key', 'YOUR_API_KEY');
params.append('phone', '254794465480');
params.append('amount', '100');
params.append('reference', 'DEP-' + Date.now());
params.append('callback_url', 'https://your-domain.com/lipa-callback');
const res = await fetch(
'https://lipaharakaapis.co.ke/api.php?action=api_wallet_deposit',
{ method: 'POST', body: params }
);
const data = await res.json();
if (data.ok) {
console.log('✅ STK sent. TX#', data.transaction_id);
console.log('Checkout ID:', data.checkout_request_id);
} else {
console.error('❌', data.message);
}
}
depositToWallet();
When a transaction status changes (payment completed, failed, or withdrawal processed), we send an HTTP POST request to the webhook URL you configured in your dashboard.
→ Your configured URL
X-Signature header
using your webhook secret. Always verify the signature before processing.
When a customer completes or fails an STK push payment, we send you this payload:
Fields: status amount phone receipt result_desc checkout_request_id
{
"status": "completed",
"amount": 100,
"phone": "254712345678",
"receipt": "UBOA97JX7Z",
"result_desc": "The service request is processed successfully.",
"checkout_request_id": "ws_CO_24022026140458394794465480"
}
status – "completed" or "failed"amount – The payment amount in KESphone – Customer phone numberreceipt – M‑Pesa receipt number (only on success)result_desc – Description of the resultcheckout_request_id – The unique checkout ID from the STK requestWhen a withdrawal (B2C) is processed, we send you this payload:
Fields: phone amount transaction_id status
{
"phone": "254712345678",
"amount": 10,
"transaction_id": "TXN_123456789",
"status": "completed"
}
phone – Recipient phone numberamount – The withdrawal amount in KEStransaction_id – The unique transaction ID from M‑Pesastatus – "completed" or "failed"{
"status": "failed",
"amount": 100,
"phone": "254712345678",
"receipt": null,
"result_desc": "The user cancelled the transaction",
"checkout_request_id": "ws_CO_24022026140458394794465480"
}
{
"phone": "254712345678",
"amount": 10,
"transaction_id": null,
"status": "failed"
}
If you provided a callback_url when calling api_wallet_deposit,
we will POST a JSON payload to your endpoint when the deposit completes or fails.
Event: wallet_deposit.completed
{
"event": "wallet_deposit.completed",
"transaction_id": 387,
"checkout_request_id": "ws_CO_08102026002205639794465480",
"user_id": 1,
"amount": 15,
"phone": "254794465480",
"mpesa_receipt": "UJ8A99E0DK",
"service_fee": 1,
"status": "success",
"message": "The service request is processed successfully.",
"timestamp": "2026-10-07T23:22:24+02:00"
}
Event: wallet_deposit.failed
{
"event": "wallet_deposit.failed",
"transaction_id": 381,
"checkout_request_id": "ws_CO_07102026221838916794465480",
"user_id": 1,
"amount": 10,
"phone": "254794465480",
"status": "failed",
"message": "Request Cancelled by user.",
"timestamp": "2026-10-07T21:18:45+02:00"
}
event – "wallet_deposit.completed" or "wallet_deposit.failed"transaction_id – Our internal wallet transaction IDcheckout_request_id – M‑Pesa CheckoutRequestID for trackinguser_id – Your merchant user IDamount – The deposit amount in KESphone – Customer phone that was chargedmpesa_receipt – M‑Pesa receipt number (only on success)service_fee – Fee deducted from your service wallet (only on success)status – "success" or "failed"message – Description of the resulttimestamp – ISO 8601 timestamp of the eventWe provide separate handlers for STK Payment, B2C Withdrawal, and Wallet Deposit callbacks. Each handler validates the incoming data and updates your database accordingly.
This handler processes incoming STK push payment callbacks.
Fields you'll receive: status, amount, phone, receipt, result_desc, checkout_request_id
<?php
// stk_webhook_handler.php
$body = file_get_contents("php://input");
$data = json_decode($body, true);
$secret = "YOUR_WEBHOOK_SECRET";
$signature = $_SERVER["HTTP_X_SIGNATURE"] ?? "";
$expected = hash_hmac("sha256", $body, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
echo json_encode(["ok" => false, "error" => "Invalid signature"]);
exit;
}
$status = $data["status"] ?? null;
$amount = $data["amount"] ?? null;
$phone = $data["phone"] ?? null;
$receipt = $data["receipt"] ?? null;
$resultDesc = $data["result_desc"] ?? null;
$checkoutId = $data["checkout_request_id"] ?? null;
// TODO: Update your database here
http_response_code(200);
echo json_encode(["ok" => true]);
?>
This handler processes incoming B2C withdrawal callbacks.
Fields you'll receive: phone, amount, transaction_id, status
<?php
// b2c_webhook_handler.php
$rawBody = file_get_contents('php://input');
$data = json_decode($rawBody, true);
$required = ['phone', 'amount', 'transaction_id', 'status'];
foreach ($required as $field) {
if (!isset($data[$field])) {
http_response_code(400);
echo json_encode(['ok' => false, 'error' => "Missing field: $field"]);
exit;
}
}
$phone = $data['phone'];
$amount = $data['amount'];
$transactionId = $data['transaction_id'];
$status = strtolower($data['status']);
// TODO: Update your database here
http_response_code(200);
echo json_encode(["ok" => true]);
?>
This handler receives and verifies the wallet deposit callback on your server.
Always verify the X-LipaHaraka-Signature header before processing.
<?php
// wallet_deposit_handler.php — Client receives deposit notifications
$rawBody = file_get_contents('php://input');
$data = json_decode($rawBody, true);
// Basic validation
if (!$data || empty($data['event'])) {
http_response_code(400);
echo json_encode(['error' => 'Invalid payload']);
exit;
}
// Extract fields
$event = $data['event']; // "wallet_deposit.completed" or "wallet_deposit.failed"
$txId = $data['transaction_id']; // Our internal wallet transaction ID
$checkout = $data['checkout_request_id'];
$amount = $data['amount'];
$phone = $data['phone'];
$receipt = $data['mpesa_receipt'] ?? null; // null if failed
$fee = $data['service_fee'] ?? 0;
$status = $data['status']; // "success" or "failed"
$message = $data['message'];
// Handle based on event type
if ($event === 'wallet_deposit.completed') {
// Payment was successful
error_log("✅ Deposit TX#$txId KES $amount | Receipt: $receipt | Fee: $fee");
// Example: Update your order
// $stmt = $pdo->prepare("UPDATE orders SET status = 'paid' WHERE transaction_id = ?");
// $stmt->execute([$txId]);
// Example: Send confirmation SMS
// sendSMS($phone, "Payment of KES $amount received. Receipt: $receipt");
} elseif ($event === 'wallet_deposit.failed') {
// Payment failed or was cancelled
error_log("❌ Deposit TX#$txId failed: $message");
// Example: Mark order as failed
// $pdo->prepare("UPDATE orders SET status = 'failed' WHERE transaction_id = ?")->execute([$txId]);
}
// Always respond with 200 OK to acknowledge receipt
http_response_code(200);
echo json_encode([
'received' => true,
'timestamp' => date('c')
]);
?>
200 OK (any 2xx) to acknowledge receipt.2547XXXXXXXX (international) or 07XXXXXXXX. The API normalises both.POST. Unexpected methods receive 405.success or ok boolean to determine status.transaction_id or checkout_request_id to avoid duplicate processing.429 Too Many Requests response with JSON error.