B2C is the Daraja endpoint that pays money out of your paybill float to a customer's M-PESA number. It powers refunds, salary payments, insurance claim payouts, betting withdrawals and gig-worker settlements. Unlike STK Push and C2B, B2C requires an encrypted security credential and a pre-funded float, and Safaricom applies conservative default daily limits until you build a track record.
For the endpoint catalogue see the Daraja API complete guide. For the customer-initiated flows, see the C2B integration guide and the STK Push integration guide.
Prerequisites
- A paybill short code with a funded B2C float (Safaricom moves funds to the float on request; typical minimum is KES 100,000).
- A registered API operator with the "B2C Initiator" role in the M-PESA Business Portal. Safaricom issues an InitiatorName and an initiator password.
- Safaricom's public certificate for the environment (sandbox or production).
- Consumer Key and Consumer Secret from the Daraja app.
Generating the security credential
Safaricom refuses to accept a plaintext password on the wire. You encrypt the initiator password once per environment and reuse the resulting ciphertext. Encryption is RSA/ECB/PKCS1Padding using Safaricom's public certificate; the output is base64-encoded.
Node.js example
import crypto from 'node:crypto';
import fs from 'node:fs';
const cert = fs.readFileSync('./ProductionCertificate.cer', 'utf8');
const initiatorPassword = process.env.MPESA_INITIATOR_PASSWORD;
const encrypted = crypto.publicEncrypt(
{ key: cert, padding: crypto.constants.RSA_PKCS1_PADDING },
Buffer.from(initiatorPassword, 'utf8')
);
const securityCredential = encrypted.toString('base64');
// Store securityCredential as MPESA_SECURITY_CREDENTIAL. It does not
// need to be regenerated per request; it only changes if Safaricom
// rotates the certificate or you change the initiator password.The B2C request body
POST to https://sandbox.safaricom.co.ke/mpesa/b2c/v1/paymentrequest (production: api.safaricom.co.ke) with a Bearer token and this body:
{
"InitiatorName": "TestB2CInitiator",
"SecurityCredential": "<base64 ciphertext>",
"CommandID": "BusinessPayment",
"Amount": 2500,
"PartyA": 600988,
"PartyB": 254712345678,
"Remarks": "Refund order 4042",
"QueueTimeOutURL": "https://api.yourdomain.co.ke/mpesa/b2c/timeout",
"ResultURL": "https://api.yourdomain.co.ke/mpesa/b2c/result",
"Occasion": "OrderRefund"
}CommandID options
| CommandID | Use case | Unregistered recipient allowed? |
|---|---|---|
| SalaryPayment | Payroll to registered M-PESA customers | No, fails with 4041 |
| BusinessPayment | General payouts, refunds, gig-work | Yes, recipient collects at agent with ID |
| PromotionPayment | Marketing prizes and campaign giveaways | No, blocked by policy |
The Result callback
Safaricom POSTs the final result to your ResultURL:
{
"Result": {
"ResultType": 0,
"ResultCode": 0,
"ResultDesc": "The service request is processed successfully.",
"OriginatorConversationID": "10571-7910404-1",
"ConversationID": "AG_20260901_00006c761b3ff2b6b3f8",
"TransactionID": "TA57ZBCAM3",
"ResultParameters": {
"ResultParameter": [
{ "Key": "TransactionAmount", "Value": 2500 },
{ "Key": "TransactionReceipt", "Value": "TA57ZBCAM3" },
{ "Key": "ReceiverPartyPublicName", "Value": "254712345678 - Jane Kamau" },
{ "Key": "TransactionCompletedDateTime", "Value": "01.09.2026 12:30:45" },
{ "Key": "B2CUtilityAccountAvailableFunds", "Value": 247500 },
{ "Key": "B2CWorkingAccountAvailableFunds", "Value": 750000 },
{ "Key": "B2CRecipientIsRegisteredCustomer", "Value": "Y" },
{ "Key": "B2CChargesPaidAccountAvailableFunds", "Value": 0 },
{ "Key": "B2CUtilityAccountAvailableFunds", "Value": 247500 }
]
}
}
}Common B2C ResultCodes
| Code | Meaning | Fix |
|---|---|---|
| 0 | Success | Book the payout |
| 2001 | Invalid initiator information | Wrong SecurityCredential or InitiatorName; regenerate with correct cert |
| 17 | Unresolved primary party | PartyA short code not permitted for B2C; check your paybill config |
| 18 | Unresolved receiver party | Recipient phone number not valid M-PESA number |
| 4041 | Recipient is not registered on M-PESA | SalaryPayment failed on unregistered; switch CommandID to BusinessPayment |
| 26 | Traffic blocked | Daily or per-transaction limit reached; wait or request limit uplift |
| 2033 | Duplicate detected | Same OriginatorConversationID within 60 seconds; use idempotency keys |
Daily limits and throttling
- Default new merchant: KES 500,000 per transaction, KES 2,500,000 per day.
- Recipient wallet ceiling: KES 500,000 total M-PESA balance (an individual customer whose wallet is already at the ceiling will fail the payout).
- Per-transaction ceiling: KES 500,000 (matches the M-PESA transaction limit).
- Limit uplifts: submit 3 months of clean B2C history plus a bank guarantee for the float to Safaricom Compliance for higher caps.
Related reading
- M-PESA for Business hub
- Daraja API complete guide
- STK Push integration
- C2B integration
- M-PESA payment gateways compared
- Daraja API (original integration guide)
- M-PESA charges 2026
FAQ
What is M-PESA B2C?
B2C (Business-to-Customer) is the Daraja endpoint that pays money out of your paybill or B2C float to a customer's M-PESA number. It powers refunds, salary payments, insurance claim disbursements, betting withdrawals, gig-work payouts and any flow where a business sends money to an individual.
What is the security credential and how do I generate it?
The security credential is your initiator password encrypted with Safaricom's public certificate using RSA/ECB/PKCS1Padding. You never send the plaintext password; you encrypt it once per environment and reuse the ciphertext. Safaricom provides different certificates for sandbox and production; use the correct one or you get error 2001.
What CommandID options exist for B2C?
SalaryPayment (for salaries; recipient must be a registered M-PESA customer, no unregistered payouts), BusinessPayment (general business payouts, no notification restrictions), PromotionPayment (marketing prizes; recipient sees a promotion-specific SMS template). PromotionPayment is throttled harder than the other two.
What are the B2C daily limits?
The default new-merchant B2C limit is KES 500,000 per transaction and KES 2,500,000 per day. Both are lifted by Safaricom on request once you provide 3 months of clean B2C history and a bank guarantee for the float. High-volume PSPs run at KES 10M+ per transaction. Individual recipients cannot receive more than the M-PESA balance limit of KES 500,000 in aggregate.
How is B2C different from a Safaricom bulk payment CSV?
Bulk payment via the M-PESA Business Portal is a manual CSV upload flow, good for occasional payroll of tens of people. B2C via Daraja is programmatic, individually authorised, callback-driven, and scales to thousands of payouts per hour. Use bulk CSV for monthly salaries, use B2C API for on-demand payouts and refunds.
What if the customer is not registered on M-PESA?
With BusinessPayment, Safaricom sends an unregistered payment that the recipient can withdraw at an agent with an ID. With SalaryPayment, unregistered recipients fail with error 4041. PromotionPayment blocks unregistered recipients by policy.
How do I know a B2C payout actually arrived?
Wait for the Result callback (posted to your ResultURL). ResultCode 0 with a TransactionReceipt is a confirmed payout. Anything else is a failure and the money stays in your float. Never mark a payout complete on the synchronous acknowledgement.
Verified 1 September 2026. B2C endpoint paths, SecurityCredential mechanics and CommandID options verified against the current Safaricom Daraja portal at developer.safaricom.co.ke. Volume context from Safaricom PLC Annual Report FY2026.
Related reading
Curated external sources we cite. Open in a new tab.