⚡ Lipa Haraka APIs

v2.0.0
Simple, secure, fast APIs for STK Push, B2C withdrawals, Wallet Deposit & real‑time webhooks. JSON responses api_key auth
Base URL https://lipaharakaapis.co.ke/api.php?action= Method POST Content-Type application/x-www-form-urlencoded Auth api_key (POST param)

📲 STK PUSH API #

Initiate a customer M‑PESA STK Push payment. The customer receives a pop‑up on their phone to enter PIN.

POST https://lipaharakaapis.co.ke/api.php?action=api_stk

🔐 Required Parameters

ParameterTypeDescription
api_keystringYour unique merchant API key (provided in dashboard). required
phonestringCustomer phone: 2547XXXXXXXX or 07XXXXXXXX. required
amountintegerAmount in KES (min 1). required
channel_idintegerPayment route ID (e.g., 16). required

✅ Example Success Response

JSON
{
    "success": true,
    "message": "STK Push initiated successfully",
    "data": {
        "payment_id": 1234,
        "checkout_request_id": "ws_CO_123456789",
        "merchant_request_id": "1234-5678-9012"
    }
}

❌ Example Error Response

JSON
{
    "success": false,
    "message": "Invalid phone number format",
    "code": "INVALID_PHONE"
}

💻 PHP Example

PHP
<?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 Example

cURL
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"

🟦 JavaScript (fetch) Example

JavaScript
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));

🏧 Wallet Withdrawal API (B2C) #

Send money from your merchant wallet to a customer's phone (B2C payout). Wallet balance is deducted automatically.

POST https://lipaharakaapis.co.ke/api.php?action=api_withdraw

🔐 Required Parameters

ParameterTypeDescription
api_keystringYour merchant API key (linked to wallet). required
phonestringRecipient phone (2547XXXXXXXX or 07XXXXXXXX). required
amountintegerAmount to withdraw (KES). Must not exceed wallet balance. required

✅ Example Success Response

JSON
{
    "success": true,
    "message": "Withdrawal initiated successfully",
    "data": {
        "withdrawal_id": "WD_987654321",
        "amount": 10,
        "balance_after": 4520.50,
        "transaction_id": "B2C_123456789"
    }
}

❌ Example Error Response

JSON
{
    "success": false,
    "message": "Insufficient wallet balance",
    "code": "LOW_BALANCE"
}

💻 PHP Example with Error Handling

PHP
<?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 Example

cURL
curl -X POST https://lipaharakaapis.co.ke/api.php?action=api_withdraw \
  -d "api_key=YOUR_API_KEY" \
  -d "phone=254712345678" \
  -d "amount=10"

💰 API Wallet Deposit #

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.

🔐 Access Required: This API is only available to approved merchants. Apply via your dashboard → API Deposit, upload your business documents (business registration, KRA PIN, ID), and wait for admin approval (usually 24–48 hours).
POST https://lipaharakaapis.co.ke/api.php?action=api_wallet_deposit

🔐 Required Parameters

ParameterTypeDescription
api_keystringYour merchant API key (must be approved for API deposit). required
phonestringPhone number to charge via STK push: 2547XXXXXXXX or 07XXXXXXXX. required
amountintegerAmount in KES (minimum 10). required
callback_urlstringYour HTTPS endpoint to receive the deposit result in real time. Must be HTTPS. optional
referencestringYour internal reference for tracking. optional
📌 Service Fee: A service fee is deducted from your Service Wallet when the deposit succeeds (same fee schedule as STK push). If your Service Wallet has insufficient balance, the request will be rejected with 402.

✅ Example Success Response

JSON
{
    "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"
    }
}

❌ Example Error Responses

JSON
// 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 Example

PHP
<?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 Example

cURL
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"

🟦 JavaScript (Node.js) Example

JavaScript
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();

📤 Webhooks & Callbacks #

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.

POST → Your configured URL
🔐 Security: We sign each webhook request with a X-Signature header using your webhook secret. Always verify the signature before processing.

📋 STK Payment Callback

When a customer completes or fails an STK push payment, we send you this payload:

🔔 STK Callback Payload STK

Fields: status amount phone receipt result_desc checkout_request_id

JSON
{
    "status": "completed",
    "amount": 100,
    "phone": "254712345678",
    "receipt": "UBOA97JX7Z",
    "result_desc": "The service request is processed successfully.",
    "checkout_request_id": "ws_CO_24022026140458394794465480"
}
Success status: "completed"
📌 STK Fields Explained:

📋 B2C Withdrawal Callback

When a withdrawal (B2C) is processed, we send you this payload:

💰 B2C Callback Payload B2C

Fields: phone amount transaction_id status

JSON
{
    "phone": "254712345678",
    "amount": 10,
    "transaction_id": "TXN_123456789",
    "status": "completed"
}
Success status: "completed"
📌 B2C Fields Explained:

❌ Failed Callback Examples

STK Payment Failed

JSON
{
    "status": "failed",
    "amount": 100,
    "phone": "254712345678",
    "receipt": null,
    "result_desc": "The user cancelled the transaction",
    "checkout_request_id": "ws_CO_24022026140458394794465480"
}
Failed status: "failed"

B2C Withdrawal Failed

JSON
{
    "phone": "254712345678",
    "amount": 10,
    "transaction_id": null,
    "status": "failed"
}
Failed status: "failed"

💰 API Wallet Deposit Callback

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.

✅ Deposit Success Callback Deposit

Event: wallet_deposit.completed

JSON
{
    "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"
}
Success status: "success"

❌ Deposit Failure Callback Failed

Event: wallet_deposit.failed

JSON
{
    "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"
}
Failed status: "failed"
📌 Wallet Deposit Callback Fields:

💻 Webhook Handler Examples

We provide separate handlers for STK Payment, B2C Withdrawal, and Wallet Deposit callbacks. Each handler validates the incoming data and updates your database accordingly.

🔔 STK Payment Webhook Handler

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
<?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]);
?>

💰 B2C Withdrawal Webhook Handler

This handler processes incoming B2C withdrawal callbacks.

Fields you'll receive: phone, amount, transaction_id, status

PHP — b2c_webhook_handler.php
<?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]);
?>

💰 Wallet Deposit Webhook Handler

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
<?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')
]);
?>
✅ Webhook Verification:

📌 Important Notes & Best Practices #

📡 Rate Limiting: Currently, the API allows up to 30 requests per minute per API key. If exceeded, you'll receive a 429 Too Many Requests response with JSON error.

📋 Common Error Codes

INVALID_API_KEY API key missing, expired, or incorrect. 401
INVALID_PHONE Phone number does not meet required format. 400
INSUFFICIENT_BALANCE Wallet balance too low for withdrawal. 402
SERVICE_WALLET_LOW Service Wallet balance insufficient to cover fee. 402
CHANNEL_ERROR M‑PESA service temporarily down (STK). 503
DUPLICATE_REQUEST Identical request detected within short time. 409
RATE_LIMIT Too many requests. Wait and retry. 429
INVALID_AMOUNT Amount is zero, negative, or exceeds limit. 400
SUB_INACTIVE Merchant subscription is inactive or expired. 403
DEPOSIT_NOT_ENABLED API deposit access not approved for this merchant. 403

🛠️ Need help? Email: Lipaonlineapis@gmail.com Phone: +254 719 809 743
Dashboard Login