Skip to content
M-PESA for Business

M-PESA B2C Integration 2026 (Daraja Payouts: Salary, Business, Promotion)

Send M-PESA payouts to customers via Daraja B2C. Initiator credentials, security credential encryption, CommandID options, daily limits and callback handling.

MM
by Reviewed by David Wachira
12 min read Updated 1 September 2026Verified 01 Sept 2026

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

  1. A paybill short code with a funded B2C float (Safaricom moves funds to the float on request; typical minimum is KES 100,000).
  2. A registered API operator with the "B2C Initiator" role in the M-PESA Business Portal. Safaricom issues an InitiatorName and an initiator password.
  3. Safaricom's public certificate for the environment (sandbox or production).
  4. 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

CommandIDUse caseUnregistered recipient allowed?
SalaryPaymentPayroll to registered M-PESA customersNo, fails with 4041
BusinessPaymentGeneral payouts, refunds, gig-workYes, recipient collects at agent with ID
PromotionPaymentMarketing prizes and campaign giveawaysNo, 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

CodeMeaningFix
0SuccessBook the payout
2001Invalid initiator informationWrong SecurityCredential or InitiatorName; regenerate with correct cert
17Unresolved primary partyPartyA short code not permitted for B2C; check your paybill config
18Unresolved receiver partyRecipient phone number not valid M-PESA number
4041Recipient is not registered on M-PESASalaryPayment failed on unregistered; switch CommandID to BusinessPayment
26Traffic blockedDaily or per-transaction limit reached; wait or request limit uplift
2033Duplicate detectedSame 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.

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.