Daraja SDK Logomark
Daraja SDK
Payouts & Disbursements

B2C

Send money from a business short code to a registered M-PESA customer (bulk disbursements).

Business to Customer, also referred to by Safaricom as Bulk Disbursements.

Endpoint

https://sandbox.safaricom.co.ke/mpesa/b2c/v3/paymentrequest

Overview

Moves money from a business account to a customer's M-PESA wallet.

Going live requires either a dedicated Bulk Disbursement Account with a B2C short code, or converting an existing Pay Bill/Till into a "one account" capable of both receiving and disbursing. Applications go through Safaricom's business channel.

How it works

  1. The merchant sends a payment request.
  2. Daraja validates and authenticates it, forwards it to M-PESA, and acknowledges the merchant.
  3. M-PESA validates the initiator details and processes the transaction.
  4. M-PESA posts the result to the merchant's callback URL.
  5. The customer receives an SMS confirming the payment.

Prerequisites

  • Daraja account and sandbox app.
  • InitiatorName: the API operator's username, available on the Test Credentials page for sandbox.
  • InitiatorPassword, encrypted into a SecurityCredential using M-PESA's public key certificate (see Introduction and Organization Portal & Go Live).
  • A live B2C shortcode, or Pay Bill/Till converted to "one account", with Business Admin/Manager operators created.

Password character limits

Safaricom's documentation restricts the initiator password to #, &, %, and $ as special characters; parentheses are rejected. @ is documented as not being treated as special on M-PESA.

Request

{
  "OriginatorConversationID": "600997_Test_32et3241ed8yu",
  "InitiatorName": "testapi",
  "SecurityCredential": "RC6E9WDxXR4b9X2c6z3gp0oC5Th ==",
  "CommandID": "BusinessPayment",
  "Amount": "10",
  "PartyA": "600992",
  "PartyB": "254705912645",
  "Remarks": "remarked",
  "QueueTimeOutURL": "https://mydomain.com/path",
  "ResultURL": "https://mydomain.com/path",
  "Occassion": "ChristmasPay"
}
FieldDescriptionRequiredSample value
OriginatorConversationIDYour own unique ID, used to avoid double-disbursement and to check status before retrying.Yes600997_Test_32et3241ed8yu
InitiatorNameAPI operator's username.Yesjohndoe
SecurityCredentialEncrypted initiator password.Yes(encrypted string)
CommandIDSalaryPayment, BusinessPayment, or PromotionPayment.YesBusinessPayment
AmountTransaction amount.Yes11
PartyAYour B2C short code.Yes600997
PartyBRecipient's MSISDN, 12 digits, no plus sign.Yes254705912645
Remarks2 to 100 characters.YesFree text
QueueTimeOutURLTimeout notification URL.Yeshttps://mydomain.com/b2c/timedout
ResultURLResult notification URL.Yeshttps://mydomain.com/b2c/result
Occassion (sic)1 to 100 characters, optional.NoFree text

Defect in the source material

The optional field is spelled Occassion, with a double "s", in Safaricom's own request schema. It's kept exactly as documented, since that's the literal field name the API expects.

CommandID options

SalaryPayment is documented as supporting both registered and unregistered M-PESA customers, but Safaricom's own text immediately adds that sending to unregistered numbers is no longer supported, which leaves the practical difference between SalaryPayment and BusinessPayment unclear from the documentation alone. PromotionPayment behaves like BusinessPayment but the SMS sent to the customer is phrased as a congratulatory message.

Response

{
  "ConversationID": "AG_20240706_20106e9209f64bebd05b",
  "OriginatorConversationID": "600997_Test_32et3241ed8yu",
  "ResponseCode": "0",
  "ResponseDescription": "Accept the service request successfully."
}

Callback payload

{
  "Result": {
    "ResultType": 0,
    "ResultCode": 0,
    "ResultDesc": "The service request is processed successfully.",
    "OriginatorConversationID": "53e3-4aa8-9fe0-8fb5e4092cdd3533373",
    "ConversationID": "AG_20240706_2010364430d9bbdaf872",
    "TransactionID": "SG632NMUAB",
    "ResultParameters": {
      "ResultParameter": [
        { "Key": "TransactionAmount", "Value": 10 },
        { "Key": "TransactionReceipt", "Value": "SG632NMUAB" },
        {
          "Key": "ReceiverPartyPublicName",
          "Value": "254705912645 - NICHOLAS JOHN SONGOK"
        },
        {
          "Key": "TransactionCompletedDateTime",
          "Value": "06.07.2024 22:48:52"
        },
        { "Key": "B2CUtilityAccountAvailableFunds", "Value": 8959269.6 },
        { "Key": "B2CWorkingAccountAvailableFunds", "Value": 1199371.0 },
        { "Key": "B2CRecipientIsRegisteredCustomer", "Value": "Y" },
        { "Key": "B2CChargesPaidAccountAvailableFunds", "Value": -1980.0 }
      ]
    },
    "ReferenceData": {
      "ReferenceItem": {
        "Key": "QueueTimeoutURL",
        "Value": "https://internalsandbox.safaricom.co.ke/mpesa/b2cresults/v1/submit"
      }
    }
  }
}
FieldDescription
ResultTypeUsually 0.
ResultCode0 for success; see the result code table below for others.
TransactionIDM-PESA transaction ID, also sent to the customer by SMS.
TransactionAmount / TransactionReceiptAmount transacted and the receipt number.
ReceiverPartyPublicNameRecipient's number and registered name.
B2CUtilityAccountAvailableFunds, B2CWorkingAccountAvailableFunds, B2CChargesPaidAccountAvailableFundsPost-transaction balances on the relevant B2C accounts.
B2CRecipientIsRegisteredCustomerY or N.

Result codes

CodeMeaningExplanation
0SuccessProcessed on M-PESA.
1Insufficient balanceNot enough funds in the Utility account.
2Declined, below minimum limitAmount too small.
3Declined, above maximum limitAmount too large.
4Declined, would exceed daily limitCustomer daily transfer limit is KES 500,000.
8Declined, would exceed maximum balanceCustomer balance cap is KES 500,000.
11DebitParty in invalid stateB2C account isn't active.
21Initiator not allowedMissing ORG B2C API Initiator role.
2001Initiator information invalidWrong username, wrong encrypted password, or wrong certificate/algorithm used to encrypt it.
2006Account rule declinedAccount isn't active.
2028Not permitted by product assignmentPartyA isn't provisioned for B2C.
2040Credit party customer type unsupportedCustomer isn't registered.
8006Security credential lockedBusiness Administrator needs to unlock it.
SFC_IC0003Operator does not existInvalid or non-existent recipient MSISDN.

Testing

Daraja Simulator: create a test app with the Business to Customer product attached.

Postman: token from https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials, then call the endpoint above.

Go Live

See Organization Portal & Go Live for the admin/manager and API initiator setup this API depends on.

FAQs

On this page