M-Pesa Infrastructure for Kenya

Money in, money out.
One powerful platform.

  • STK Push
  • B2C Payouts
  • Till & Links
  • Webhooks
  • REST API

Collect payments with STK and payment links, pay out to any M-Pesa number with B2C — all from one dashboard with signed webhooks and clean APIs.

★★★★★ | Trusted by 500+ Kenyan businesses | KES 2B+ processed monthly

KES 2B+
Processed monthly
500+
Active businesses
< 3s
STK push response
99.9%
API uptime

How it works

Go live in 4 steps

No mountains of Safaricom paperwork. BluePay handles the complexity so you collect payments today.

Create account

Sign up free in 2 minutes. No setup fee, no credit card required to get started.

Connect your channels

Link your M-Pesa Till or bank channels. We handle the Safaricom integration.

Integrate or use dashboard

Use our API, dashboard, and webhooks. Ship in a day, not a month.

Collect & get notified

Payments confirm fast. Webhooks notify your system instantly for reconciliation.


Product

Collect and pay out, one account

Money in for sales. Money out for withdrawals, refunds, and supplier payments.

Collections

Money in

STK Push, Till, and payment links. Every payment lands in your BluePay ledger with exports and real-time webhooks.

Explore collection API →
Disbursements

Money out

B2C payouts to any Kenyan M-Pesa number. Top up your payout balance, then send from the dashboard or your backend.

How B2C works →

STK Push

Payment prompt to any M-Pesa number. PIN on the phone, confirmation in seconds.

Collections

B2C Payouts

Send KES to customers or staff. API, dashboard, and status webhooks included.

Payouts

Dashboard & Ledger

Live feeds, pay-ins, payouts, and fees in one view. Export for finance.

Reporting

Signed Webhooks

HMAC-SHA256 verified events the instant payments and payouts complete.

Webhooks

REST API

JSON endpoints, clear error messages, and docs built for shipping fast.

Developers

Payment Links

Shareable links for WhatsApp and social — no website or checkout required.

Pay Links

B2C Payouts

Send KES to any M-Pesa number

Fund your payout balance, trigger B2C from the API or dashboard, and receive a webhook when Safaricom completes the transfer.

Top up your payout wallet

STK push adds funds to your payout wallet inside BluePay — instantly available for disbursement.

Trigger the payout

Dashboard withdrawal or POST /api/v1/b2c/payouts from your backend.

Confirm via webhook

Poll status or receive a signed webhook when the transfer completes or fails.

Payout wallet

KES reserved for outgoing transfers. Topped up via STK; refunded if M-Pesa rejects.

Processing fees

Small per-payout fee tiered by amount. Same pay-as-you-go model as collections.

B2C API endpoints
POST/api/v1/b2c/payoutsSend to customer M-Pesa
GET/api/v1/b2c/payout_statusCheck payout by ID
GET/api/v1/b2c/wallet/balancePayout wallet balance
Payout flow

How it connects

Your app → BluePay → Safaricom M-Pesa

sequenceDiagram
  participant M as Merchant App
  participant BP as BluePay
  participant MP as M-Pesa
  M->>BP: POST /api/v1/b2c/payouts
  BP->>BP: Reserve payout wallet
  BP->>MP: B2C payment request
  MP-->>M: Funds land in customer M-Pesa
  MP->>BP: Result callback
  BP->>M: Webhook (signed HMAC)

Developer API

Built for developers who ship fast

One API for STK collections, B2C payouts, payment links, and signed webhooks.

POST/api/stk_push.phpTrigger STK push
POST/api/v1/b2c/payoutsB2C payout to M-Pesa
GET/api/v1/b2c/payout_statusPayout status
GET/api/v1/b2c/wallet/balanceWallet balance
GET/api/payment_channels.phpList channels
POST/api/stk_push_paylink.phpSTK via pay link

⚡ Webhook-first design

Every payment fires a webhook within 500ms. HMAC-SHA256 signatures so you know it's genuinely from BluePay — no polling required.

stk_push — examples
# Configure channels in BluePay dashboard first
curl -X POST https://YOUR_DOMAIN/api/stk_push.php \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": "YOUR_CHANNEL_UUID",
    "phone": "254712345678",
    "amount": 1500,
    "account_reference": "ORDER-2026"
  }'

# Success response
{
  "ok": true,
  "stk_request_id": 1,
  "checkout_request_id": "ws_CO_..."
}
<?php
$body = [
  'channel_id' => 'YOUR_CHANNEL_UUID',
  'phone' => '254712345678',
  'amount' => 1500,
  'account_reference' => 'ORDER-2026',
];
$ch = curl_init('https://YOUR_DOMAIN/api/stk_push.php');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer YOUR_API_KEY',
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => json_encode($body),
  CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);
// json_decode($response, true)
import requests

r = requests.post(
    "https://YOUR_DOMAIN/api/stk_push.php",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "channel_id": "YOUR_CHANNEL_UUID",
        "phone": "254712345678",
        "amount": 1500,
        "account_reference": "ORDER-2026",
    },
    timeout=60,
)
print(r.status_code, r.json())
// Node 18+ (global fetch)
const res = await fetch("https://YOUR_DOMAIN/api/stk_push.php", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    channel_id: "YOUR_CHANNEL_UUID",
    phone: "254712345678",
    amount: 1500,
    account_reference: "ORDER-2026",
  }),
});
console.log(res.status, await res.json());
import java.net.URI;
import java.net.http.*;

String json = "{\"channel_id\":\"UUID\",\"phone\":\"254712345678\",\"amount\":1500}";
HttpClient client = HttpClient.newHttpClient();
HttpRequest req = HttpRequest.newBuilder(URI.create(
    "https://YOUR_DOMAIN/api/stk_push.php"))
  .header("Authorization", "Bearer YOUR_API_KEY")
  .header("Content-Type", "application/json")
  .POST(HttpRequest.BodyPublishers.ofString(json))
  .build();
var res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.statusCode() + " " + res.body());
POST /api/v1/b2c/payouts
# Fund B2C wallet in dashboard first
curl -X POST https://bluepay.co.ke/api/v1/b2c/payouts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 500,
    "phone": "254712345678",
    "reference": "WD-2026-001",
    "description": "Withdrawal",
    "callbackUrl": "https://yoursite.com/webhooks/b2c"
  }'

# Poll status
curl "https://bluepay.co.ke/api/v1/b2c/payout_status?payout_id=PAYOUT_UUID" \
  -H "Authorization: Bearer YOUR_API_KEY"
<?php
$body = [
  'amount' => 500,
  'phone' => '254712345678',
  'reference' => 'WD-2026-001',
  'description' => 'Withdrawal',
  'callbackUrl' => 'https://yoursite.com/webhooks/b2c',
];
$ch = curl_init('https://bluepay.co.ke/api/v1/b2c/payouts');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer YOUR_API_KEY',
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => json_encode($body),
  CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);
// json_decode($response, true)
import requests

r = requests.post(
    "https://bluepay.co.ke/api/v1/b2c/payouts",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "amount": 500,
        "phone": "254712345678",
        "reference": "WD-2026-001",
        "description": "Withdrawal",
        "callbackUrl": "https://yoursite.com/webhooks/b2c",
    },
    timeout=60,
)
print(r.status_code, r.json())
// Node 18+ (global fetch)
const res = await fetch("https://bluepay.co.ke/api/v1/b2c/payouts", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    amount: 500,
    phone: "254712345678",
    reference: "WD-2026-001",
    description: "Withdrawal",
    callbackUrl: "https://yoursite.com/webhooks/b2c",
  }),
});
console.log(res.status, await res.json());
import java.net.URI;
import java.net.http.*;

String json = "{\"amount\":500,\"phone\":\"254712345678\",\"reference\":\"WD-2026-001\"}";
HttpClient client = HttpClient.newHttpClient();
HttpRequest req = HttpRequest.newBuilder(URI.create(
    "https://bluepay.co.ke/api/v1/b2c/payouts"))
  .header("Authorization", "Bearer YOUR_API_KEY")
  .header("Content-Type", "application/json")
  .POST(HttpRequest.BodyPublishers.ofString(json))
  .build();
var res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.statusCode() + " " + res.body());
webhook — verify HMAC-SHA256
// Read raw body BEFORE json_decode — sig is over bytes
$payload = file_get_contents('php://input');
$sigHeader = $_SERVER['HTTP_X_BLUEPAY_SIGNATURE'] ?? '';
$secret = 'YOUR_SECRET_KEY';

if (!preg_match('/^v1=([a-f0-9]{64})$/', $sigHeader, $m)) {
  http_response_code(400); exit;
}
$expected = hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expected, $m[1])) {
  http_response_code(401); exit;
}
$event = json_decode($payload, true);
// $event['event'] === 'mpesa.payment.received'
import hashlib, hmac, json, re

raw = request.data  # raw bytes — read BEFORE json.loads
sig = request.headers.get("X-BluePay-Signature", "")
secret = b"YOUR_SECRET_KEY"

m = re.fullmatch(r"v1=([a-f0-9]{64})", sig)
if not m:
    abort(400)
expected = hmac.new(secret, raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, m.group(1)):
    abort(401)
event = json.loads(raw.decode("utf-8"))
import crypto from "crypto";

// Use express.raw({ type: "application/json" }) so req.body is a Buffer
app.post("/webhook", (req, res) => {
  const raw = req.body;
  const sig = req.get("X-BluePay-Signature") || "";
  const m = /^v1=([a-f0-9]{64})$/.exec(sig);
  if (!m) return res.sendStatus(400);
  const expected = crypto.createHmac("sha256", "YOUR_SECRET_KEY")
    .update(raw).digest("hex");
  if (!crypto.timingSafeEqual(
    Buffer.from(expected, "hex"), Buffer.from(m[1], "hex")
  )) return res.sendStatus(401);
  const event = JSON.parse(raw.toString("utf8"));
  res.sendStatus(200);
});
require "openssl"
require "json"

raw = request.body.read
sig = request.get_header("HTTP_X_BLUEPAY_SIGNATURE").to_s
secret = "YOUR_SECRET_KEY"
m = /\Av1=([a-f0-9]{64})\z/.match(sig)
halt 400 unless m
expected = OpenSSL::HMAC.hexdigest("SHA256", secret, raw)
halt 401 unless Rack::Utils.secure_compare(expected, m[1])
event = JSON.parse(raw)

Security & Compliance

Bank-grade security, Kenyan trust

Security-first by design — signed webhooks, encrypted connections, and clean API patterns throughout.

🔒

End-to-end encryption

All API communication is over HTTPS. Keep your API keys server-side and rotate them anytime without downtime.

🛡️

Webhook signatures

Every webhook is signed with HMAC-SHA256. Your server verifies before processing — protection from replay attacks.

🔑

Flexible API access

Bearer API keys or named Basic-auth credentials from the dashboard. Rotate anytime, zero downtime.

🔍

Fraud detection

Real-time anomaly detection, velocity checks, and duplicate payment guards protect your collections automatically.

📋

Full audit trail

Every transaction, webhook, API call, and user action is logged with timestamps. Export for compliance.

Secure by default

Strict input validation, signed webhooks, idempotent endpoints, and safe defaults across the entire platform.

🔒 HTTPS everywhere
🛡️ Signed webhooks
⚡ Fast confirmations
📋 Audit logs
🇰🇪 Kenya-compliant

Who uses BluePay

Built for every Kenyan business

From corner shops to enterprise SaaS. If you collect money in Kenya, BluePay was built for you.

🏫

Schools & Colleges

Fee collection via STK with payment links and webhook confirmations.

🏠

Property & Rentals

Monthly rent collection, automated receipts, and SACCO contributions.

🛒

E-commerce & Retail

Checkout payment links, order webhooks, and instant settlement.

🍕

Restaurants & Bars

Table-side QR links, daily reconciliation, and POS integration.

💻

SaaS & Developers

Full API for building payment products on top of BluePay.

🤝

SACCOs & Chamas

Member contribution tracking with payment links and webhooks.

🏥

Healthcare & Clinics

Patient links, insurance reconciliation, multi-branch reporting.

🚚

Logistics & Delivery

Cash-on-delivery via M-Pesa with instant webhook confirmations.


What people say

Trusted across Kenya

From finance teams to developers — here's how BluePay changes how businesses handle payments.

★★★★★

We went from spreadsheet chaos to one dashboard. STK prompts and webhooks mean our front desk and accounts finally agree on what was paid. Reconciliation that took 3 days now takes 10 minutes.

Amina K.
Operations Lead, Nairobi Retail Chain
★★★★★

The API fit our checkout in a single day. Webhooks fire instantly and the error codes are actually useful — saved us from maintaining another brittle M-Pesa integration.

David M.
Product Engineer, SaaS Startup
★★★★★

Members pay on Till; we see it instantly and export for audit. Less chasing, fewer "I paid" disputes — the auto-reconciliation paid for itself in the first week.

Ruth N.
Treasurer, Community SACCO

Blog

Latest insights

Payment trends, developer guides, and business growth tips for the Kenyan market.

Developer Guide

Top SaaS & developer solutions you can build using BluePay in Kenya

Read article →
Growth Strategy

The ultimate guide to payment automation for growing Kenyan businesses

Read article →
Industry Trends

Digital payment trends for Kenyan SMEs in 2026 — automation, transparency & controls

Read article →

Ready to simplify your payments?

Join 500+ Kenyan businesses collecting faster, reconciling automatically, and spending less time chasing payments.

🍕 Restaurants 🏫 Schools 🛒 Retailers 💻 Developers 🏠 Property 🤝 SACCOs 🏥 Healthcare 🚚 Logistics

Get in touch

Schedule a demo

Tell us about your business and payment volumes. We'll show you exactly how BluePay fits — live, no slides.

Phone & WhatsApp

+254 742 783 614

Location

Nairobi, Kenya — serving businesses nationwide

Response time

Usually within 2 hours on weekdays. WhatsApp for urgent queries.

Send us a message