Money in
STK Push, Till, and payment links. Every payment lands in your BluePay ledger with exports and real-time webhooks.
Explore collection 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
No mountains of Safaricom paperwork. BluePay handles the complexity so you collect payments today.
Sign up free in 2 minutes. No setup fee, no credit card required to get started.
Link your M-Pesa Till or bank channels. We handle the Safaricom integration.
Use our API, dashboard, and webhooks. Ship in a day, not a month.
Payments confirm fast. Webhooks notify your system instantly for reconciliation.
Money in for sales. Money out for withdrawals, refunds, and supplier payments.
STK Push, Till, and payment links. Every payment lands in your BluePay ledger with exports and real-time webhooks.
Explore collection API →B2C payouts to any Kenyan M-Pesa number. Top up your payout balance, then send from the dashboard or your backend.
How B2C works →Payment prompt to any M-Pesa number. PIN on the phone, confirmation in seconds.
CollectionsSend KES to customers or staff. API, dashboard, and status webhooks included.
PayoutsLive feeds, pay-ins, payouts, and fees in one view. Export for finance.
ReportingHMAC-SHA256 verified events the instant payments and payouts complete.
WebhooksJSON endpoints, clear error messages, and docs built for shipping fast.
DevelopersShareable links for WhatsApp and social — no website or checkout required.
Pay LinksFund your payout balance, trigger B2C from the API or dashboard, and receive a webhook when Safaricom completes the transfer.
STK push adds funds to your payout wallet inside BluePay — instantly available for disbursement.
Dashboard withdrawal or POST /api/v1/b2c/payouts from your backend.
Poll status or receive a signed webhook when the transfer completes or fails.
KES reserved for outgoing transfers. Topped up via STK; refunded if M-Pesa rejects.
Small per-payout fee tiered by amount. Same pay-as-you-go model as collections.
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)
One API for STK collections, B2C payouts, payment links, and signed webhooks.
Every payment fires a webhook within 500ms. HMAC-SHA256 signatures so you know it's genuinely from BluePay — no polling required.
# 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());
# 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());
// 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-first by design — signed webhooks, encrypted connections, and clean API patterns throughout.
All API communication is over HTTPS. Keep your API keys server-side and rotate them anytime without downtime.
Every webhook is signed with HMAC-SHA256. Your server verifies before processing — protection from replay attacks.
Bearer API keys or named Basic-auth credentials from the dashboard. Rotate anytime, zero downtime.
Real-time anomaly detection, velocity checks, and duplicate payment guards protect your collections automatically.
Every transaction, webhook, API call, and user action is logged with timestamps. Export for compliance.
Strict input validation, signed webhooks, idempotent endpoints, and safe defaults across the entire platform.
From corner shops to enterprise SaaS. If you collect money in Kenya, BluePay was built for you.
Fee collection via STK with payment links and webhook confirmations.
Monthly rent collection, automated receipts, and SACCO contributions.
Checkout payment links, order webhooks, and instant settlement.
Table-side QR links, daily reconciliation, and POS integration.
Full API for building payment products on top of BluePay.
Member contribution tracking with payment links and webhooks.
Patient links, insurance reconciliation, multi-branch reporting.
Cash-on-delivery via M-Pesa with instant webhook confirmations.
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.
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.
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.
Payment trends, developer guides, and business growth tips for the Kenyan market.
Join 500+ Kenyan businesses collecting faster, reconciling automatically, and spending less time chasing payments.
Tell us about your business and payment volumes. We'll show you exactly how BluePay fits — live, no slides.
Nairobi, Kenya — serving businesses nationwide
Usually within 2 hours on weekdays. WhatsApp for urgent queries.
Or chat on WhatsApp for a faster response