The Easiest Ways to Integrate M-Pesa Automatic Payouts (B2C)
Compare 3 ways to automate M-Pesa B2C payouts in Kenya — Daraja, aggregators and PalPluss — with working code, fees, idempotency and webhook handling.
- By
- PalPluss Engineering
- Published
- Reading time
- 5 min
The short answer
- The easiest way to automate M-Pesa B2C payouts is a payments API that already holds the Safaricom B2C setup — PalPluss lets you send a payout with one authenticated POST request.
- Going direct to Safaricom Daraja B2C means a B2C shortcode, initiator credentials, certificate-encrypted security credentials and your own callback handling.
- Whatever you choose, use idempotency keys, confirm the result by webhook, and keep a pre-funded payout balance.
B2C (Business to Customer) is the M-Pesa product that moves money from a business to a person's phone: salaries, loan disbursements, refunds, rider and agent commissions, marketplace seller payouts, betting and gaming winnings. Doing it by hand from the M-Pesa portal works for ten payments a month. It breaks the moment payouts depend on events in your own product.
This guide compares the three realistic ways to automate M-Pesa payouts in Kenya, then walks through a production-ready integration with PalPluss — the option we built to make B2C a single API call.
Option 1: PalPluss B2C API (easiest)
PalPluss holds the Safaricom-side B2C configuration for you. You fund a B2C wallet, generate an API key in the console, and call POST /v1/b2c/payouts. There is no shortcode application, no initiator password and no certificate encryption on your side.
- One request per payout — amount, phone number, your own reference and an optional callback URL.
- Automatic reversals — if M-Pesa rejects a payout, the amount is credited back to your B2C wallet without manual action.
- Idempotency built in — send an
Idempotency-Keyheader and retries never pay twice. - Transparent fees — a tiered fee from KES 6 per payout, deducted from your service wallet. See pricing.
- Duplicate-payout protection — the API rejects a second payout to the same number within 60 seconds (
429 B2C_PAYOUT_COOLDOWN), stopping double-clicks and runaway loops from emptying your float.
Option 2: Safaricom Daraja B2C directly
Daraja is Safaricom's own developer platform. Integrating B2C directly gives you full control and no platform fee, but expect real setup work before the first shilling moves:
- Apply for a B2C-enabled shortcode and go-live approval for your organisation.
- Create an initiator user on the M-Pesa organisation portal.
- Encrypt the initiator password with Safaricom's public certificate to produce the
SecurityCredential. - Implement OAuth token generation and refresh.
- Call the B2C payment request endpoint, then correlate asynchronous results by
ConversationIDon a publicResultURLandQueueTimeOutURL. - Build your own retry, reversal and reconciliation logic.
Direct Daraja makes sense for large organisations with a dedicated payments team. For most startups the engineering and approval time costs more than a platform fee.
Option 3: Multi-country payment aggregators
Pan-African gateways also offer mobile-money payouts alongside cards. They are a good fit when you need many countries in one contract. The trade-off for a Kenya-first product is usually percentage-based pricing, longer onboarding, and M-Pesa treated as one channel among dozens rather than the core product.
Side-by-side comparison
| PalPluss | Daraja direct | Multi-country aggregator | |
|---|---|---|---|
| Time to first payout | Minutes after KYC | Weeks (shortcode + go-live) | Days to weeks |
| Credentials you manage | One API key | Shortcode, initiator, certificate, OAuth | Account + API keys |
| Failed payout reversal | Automatic | You build it | Varies |
| Idempotency | Built in | You build it | Varies |
| Pricing model | Flat tiered fee per payout | Safaricom tariffs only | Often percentage-based |
| Focus | M-Pesa, Kenya | M-Pesa, Kenya | Many countries & methods |
Send your first automatic payout with PalPluss
1. Create an account and pass KYC
Register at console.palpluss.com. B2C payouts are unlocked once your KYC is approved — requests before approval return 403 KYC_NOT_VERIFIED.
2. Fund your wallets
Top up your B2C wallet (the float payouts are sent from) and your service wallet (which pays per-transaction fees). Both can be topped up by STK Push from the console or through the API.
3. Send the payout
curl -X POST https://api.palpluss.com/v1/b2c/payouts \
-u "$PALPLUSS_API_KEY:" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: payout-order-1042" \
-d '{
"amount": 1500,
"phone": "0712345678",
"reference": "ORDER-1042",
"callbackUrl": "https://yourapp.com/webhooks/palpluss"
}'Prefer a typed client? The official Node.js SDK generates the idempotency key for you:
import { PalPluss } from "@palpluss/sdk";
const palpluss = new PalPluss({ apiKey: process.env.PALPLUSS_API_KEY });
const payout = await palpluss.b2cPayout(
{ amount: 1500, phone: "254712345678", reference: "ORDER-1042" },
{ idempotencyKey: "payout-order-1042" },
);
console.log(payout.status); // PROCESSING — final result arrives by webhook4. Handle the webhook
Payouts settle asynchronously, usually within 30 seconds to 5 minutes. PalPluss sends a POST to your callbackUrl when the payout reaches SUCCESS or FAILED. Treat that callback as the source of truth, and make your handler idempotent because a callback can be delivered more than once.
app.post("/webhooks/palpluss", async (req, res) => {
const { transaction } = req.body;
// Ignore repeats — the same event may be delivered more than once.
if (await alreadyProcessed(transaction.id)) return res.sendStatus(200);
if (transaction.status === "SUCCESS") {
await markPaid(transaction.external_reference, transaction.mpesa_receipt);
} else if (transaction.status === "FAILED") {
// The amount is already back in your B2C wallet.
await markPayoutFailed(transaction.external_reference, transaction.result_desc);
}
res.sendStatus(200);
});Best practices for automated payouts
- Use one idempotency key per business event (for example the withdrawal or order ID). A retried request then returns the original payout instead of paying twice.
- Never mark a payout complete from the API response alone —
PROCESSINGmeans accepted, not delivered. Wait for the webhook. - Keep a float buffer in your B2C wallet sized to at least a day of payouts, and monitor it with the balance endpoint.
- Respect the per-number cooldown — if you genuinely need two payouts to one person, combine them or wait for
details.retryAfterSeconds. - Validate amounts server-side. The minimum payout is KES 10.
Frequently asked questions
What is the easiest way to integrate M-Pesa B2C payouts?
Use a payments API that already holds the Safaricom B2C configuration. With PalPluss you fund a B2C wallet, create an API key and send one POST request to /v1/b2c/payouts — no shortcode, initiator or certificate setup on your side.
How long does an M-Pesa B2C payout take?
Most payouts settle within 30 seconds to 5 minutes. PalPluss notifies your callback URL when the payout reaches SUCCESS or FAILED.
What happens if a B2C payout fails?
On PalPluss the payout amount is automatically reversed to your B2C wallet. The per-transaction service fee is not refunded.
How do I stop duplicate M-Pesa payouts?
Send an Idempotency-Key header with each payout so retries return the original result. PalPluss also blocks a second payout to the same phone number within 60 seconds with a 429 B2C_PAYOUT_COOLDOWN error.
What is the minimum M-Pesa B2C payout on PalPluss?
The minimum payout is KES 10. Fees start at KES 6 per payout and are tiered by amount.
Build on PalPluss
M-Pesa collections and payouts in one API. KES 15 in free tokens to start.