C2B vs B2C vs B2B on Daraja: Which API for Which Flow
C2B (Customer to Business) receives payments from customers into your shortcode. B2C (Business to Customer) sends money from your shortcode to a customer's M-Pesa. B2B (Business to Business) transfers between M-Pesa shortcodes. Most developers start with C2B (via STK Push) for collecting payments, then add B2C when they need to send refunds, salaries, or promotional payouts.
The three Daraja API families
Daraja organizes its payment APIs into three categories based on the direction money flows:
- C2B (Customer to Business): money moves from a person's M-Pesa wallet to your business shortcode. This is what happens when a customer pays for something.
- B2C (Business to Customer): money moves from your business shortcode to a person's M-Pesa wallet. This is for refunds, salary disbursements, cashback, and contest payouts.
- B2B (Business to Business): money moves between two business shortcodes. This is for supplier payments, inter-company transfers, and settlement.
Each category uses different endpoints, different request structures, and different approval processes. Understanding which one you need before writing code saves you from building the wrong integration.
C2B: Collecting payments from customers
C2B is the most common integration. There are two ways customers can pay your shortcode:
1. STK Push (Lipa na M-Pesa Online): your application triggers a payment prompt on the customer's phone. This is the smoothest experience because the customer does not need to navigate the M-Pesa menu themselves.
2. Manual paybill/till payment: the customer opens M-Pesa, selects "Pay Bill" or "Buy Goods," enters your shortcode and amount manually. You receive a notification via the C2B Register URL API.
For STK Push, use the /mpesa/stkpush/v1/processrequest endpoint. For receiving notifications of manual payments, register your validation and confirmation URLs:
import axios from 'axios';
async function registerC2BUrls() {
const token = await getAccessToken();
const response = await axios.post(
'https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl',
{
ShortCode: process.env.DARAJA_SHORTCODE,
ResponseType: 'Completed',
ConfirmationURL: 'https://yourdomain.com/api/mpesa/c2b/confirm',
ValidationURL: 'https://yourdomain.com/api/mpesa/c2b/validate',
},
{
headers: { Authorization: `Bearer ${token}` },
}
);
return response.data;
}The ValidationURL is called before the transaction completes. You can accept or reject the payment by returning the appropriate response. Use this to check if the account number is valid in your system.
The ConfirmationURL is called after the transaction completes. This is where you update your database.
// Validation handler: accept or reject the payment
app.post('/api/mpesa/c2b/validate', (req, res) => {
const { BillRefNumber, TransAmount } = req.body;
// Check if the account reference exists
const orderExists = checkOrderExists(BillRefNumber);
if (orderExists) {
res.json({ ResultCode: 0, ResultDesc: 'Accepted' });
} else {
res.json({ ResultCode: 1, ResultDesc: 'Rejected: invalid account' });
}
});
// Confirmation handler: payment completed
app.post('/api/mpesa/c2b/confirm', (req, res) => {
const {
TransID,
TransAmount,
BillRefNumber,
MSISDN,
} = req.body;
console.log(
`C2B payment: KES ${TransAmount} from ${MSISDN}, ` +
`Ref: ${BillRefNumber}, Receipt: ${TransID}`
);
// TODO: update order in database
res.json({ ResultCode: 0, ResultDesc: 'Accepted' });
});Use STK Push when you want to control the payment experience (e-commerce checkout, app subscription). Use C2B URL registration when you want to capture payments customers initiate themselves (walk-in payments to your paybill).
B2C: Sending money to customers
B2C lets your business send money to individual M-Pesa users. Common use cases:
- Refunds for cancelled orders
- Salary or wage disbursements
- Cashback or promotional rewards
- Contest or competition payouts
- Agent float disbursement
B2C requires additional approval from Safaricom because you are sending money out of your account. You need an Initiator Name and Security Credential (an encrypted password) that are issued during go-live.
Node.js example:
async function sendB2CPayment(
phone: string,
amount: number,
occasion: string
) {
const token = await getAccessToken();
const response = await axios.post(
'https://sandbox.safaricom.co.ke/mpesa/b2c/v3/paymentrequest',
{
OriginatorConversationID: generateUniqueId(),
InitiatorName: process.env.B2C_INITIATOR_NAME,
SecurityCredential: process.env.B2C_SECURITY_CREDENTIAL,
CommandID: 'BusinessPayment',
Amount: amount,
PartyA: process.env.DARAJA_SHORTCODE,
PartyB: phone,
Remarks: occasion,
QueueTimeOutURL: process.env.B2C_TIMEOUT_URL,
ResultURL: process.env.B2C_RESULT_URL,
Occasion: occasion,
},
{
headers: { Authorization: `Bearer ${token}` },
}
);
return response.data;
}Python example:
def send_b2c_payment(
phone: str, amount: int, occasion: str
) -> dict:
token = get_access_token()
payload = {
"OriginatorConversationID": generate_unique_id(),
"InitiatorName": os.environ["B2C_INITIATOR_NAME"],
"SecurityCredential": os.environ["B2C_SECURITY_CREDENTIAL"],
"CommandID": "BusinessPayment",
"Amount": amount,
"PartyA": os.environ["DARAJA_SHORTCODE"],
"PartyB": phone,
"Remarks": occasion,
"QueueTimeOutURL": os.environ["B2C_TIMEOUT_URL"],
"ResultURL": os.environ["B2C_RESULT_URL"],
"Occasion": occasion,
}
response = requests.post(
"https://sandbox.safaricom.co.ke/mpesa/b2c/v3/paymentrequest",
json=payload,
headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()
return response.json()The CommandID determines the transaction type:
BusinessPayment: normal payment to a customer (salary, refund)SalaryPayment: specifically for salary, with different tax reportingPromotionPayment: promotional or reward payment
B2C results come asynchronously to your ResultURL, just like STK Push callbacks.
B2B: Transfers between businesses
B2B transfers money from one M-Pesa shortcode to another. This is less common for startups but essential for:
- Paying suppliers who have M-Pesa shortcodes
- Moving funds between your own paybill and till number
- Marketplace platforms that split payments to multiple vendors
- Inter-company transfers within a group of companies
The B2B API requires both sender and receiver shortcodes, and both must be registered on the Daraja platform. The request structure is similar to B2C but with a receiving shortcode instead of a phone number:
async function sendB2BPayment(
receiverShortcode: string,
amount: number,
accountRef: string
) {
const token = await getAccessToken();
const response = await axios.post(
'https://sandbox.safaricom.co.ke/mpesa/b2b/v1/paymentrequest',
{
Initiator: process.env.B2B_INITIATOR_NAME,
SecurityCredential: process.env.B2B_SECURITY_CREDENTIAL,
CommandID: 'BusinessPayBill',
SenderIdentifierType: 4,
RecieverIdentifierType: 4,
Amount: amount,
PartyA: process.env.DARAJA_SHORTCODE,
PartyB: receiverShortcode,
AccountReference: accountRef,
Remarks: 'Supplier payment',
QueueTimeOutURL: process.env.B2B_TIMEOUT_URL,
ResultURL: process.env.B2B_RESULT_URL,
},
{
headers: { Authorization: `Bearer ${token}` },
}
);
return response.data;
}B2B is the most restricted Daraja product. Safaricom requires more documentation and may take longer to approve your application.
Decision table: which API for your use case
Use this table to pick the right Daraja product for your business flow:
- E-commerce checkout: C2B via STK Push. Customer pays your shortcode through a payment prompt.
- Subscription billing: C2B via STK Push, triggered monthly by your billing system.
- Walk-in payments (restaurant, shop): C2B via URL registration. Customers pay your till directly from their M-Pesa app.
- Refunds: B2C. Send money back from your shortcode to the customer's M-Pesa.
- Salary payments: B2C with
SalaryPaymentcommand. Bulk disbursement to employee phone numbers. - Cashback / rewards: B2C with
PromotionPaymentcommand. - Supplier payments: B2B if the supplier has a shortcode. If the supplier only has a personal M-Pesa, use B2C.
- Marketplace vendor splits: B2B to split to vendor shortcodes, or B2C to split to vendor phone numbers.
- Float top-up for agents: B2C to agent phone numbers.
Most applications start with C2B (STK Push) only. Add B2C when you need to send money out, and B2B only when you need to move funds between shortcodes. There is no need to implement all three on day one.
Frequently Asked Questions
- Can I use C2B and B2C on the same shortcode?
- Yes. A single paybill shortcode can receive payments (C2B) and send payments (B2C). You need to apply for both API products on the Daraja portal, and the B2C capability requires additional security credentials, but they share the same shortcode.
- What are the fees for B2C and B2B transactions?
- B2C and B2B transactions have different fee structures from C2B. The fees depend on the amount, the transaction type, and your agreement with Safaricom. [TODO: verify on provider website] for the current fee schedule. Generally, B2C fees are higher per transaction than C2B because you are moving money out.
- Is there a bulk payment API for sending to many people at once?
- Daraja does not have a dedicated bulk payment endpoint. To pay multiple people, you send individual B2C requests for each recipient. Use a job queue to manage the requests and respect rate limits. Some third-party providers like Kopokopo and IntaSend offer bulk disbursement features built on top of Daraja.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon. Ship 8 production apps with M-Pesa, USSD, and WhatsApp integrations, and get career support until placement.
See Programs