C2B — Customer to Business
C2B handles payments the customer starts — paying your shortcode (Pay Bill / Buy Goods). You register two URLs we call during the flow: one to validate, one to confirm.
The flow
Validation — called first, before money moves. Inspect the bill reference and amount; accept or reject.
Money moves — if accepted, the customer's payment is applied to your collection account.
Confirmation — called once it completes. This is your signal to fulfil the order.
POST /partner/c2b/register
Field | Type | Required | Description |
|---|---|---|---|
validation_url | string | yes | We POST proposed payments here; you accept or reject. |
confirmation_url | string | yes | We POST completed payments here; you fulfil. |
response_type | string | no |
|
curl -X POST https://<your-operator>/api/bridgepay/v1/partner/c2b/register \
-H "Authorization: Bearer at_..." -H "Content-Type: application/json" \
-d '{
"validation_url": "https://your-app.com/c2b/validate",
"confirmation_url": "https://your-app.com/c2b/confirm",
"response_type": "completed"
}'Allowed mode (default response)
response_type is the fallback decision if your validation endpoint is unreachable or times out:
Value | Behaviour when validation is down |
|---|---|
completed | Accept the payment by default (don't block paying customers). |
cancelled | Reject by default (strict; only accept when you explicitly validate). |
Validation request & expected response
We POST the proposed payment to your validation URL. Reply 200 with an accept or reject:
// we POST to your validation_url
{ "TransAmount": 500, "BillRefNumber": "ORDER-77",
"MSISDN": "2547xxxxxxxx", "BusinessShortCode": "555550" }
// you reply 200 with:
{ "ResultCode": 0, "ResultDesc": "Accepted" } // accept
{ "ResultCode": 1, "ResultDesc": "Rejected" } // rejectConfirmation payload
When the payment completes we POST this to your confirmation URL (signed — verify it, see Webhooks):
{
"TransactionType": "Pay Bill",
"TransID": "BP2600000122",
"TransAmount": 500,
"BusinessShortCode": "555550",
"BillRefNumber": "ORDER-77",
"MSISDN": "2547xxxxxxxx",
"OrgAccountBalance": 1234.00
}Handle it
Node.js
app.post("/c2b/confirm", (req, res) => {
if (!verify(req.rawBody, req.header("X-PesaBridge-Signature"), SECRET))
return res.sendStatus(401);
const { TransID, TransAmount, BillRefNumber } = req.body;
fulfilOnce(TransID, BillRefNumber, TransAmount); // idempotent on TransID
res.json({ ResultCode: 0, ResultDesc: "Received" });
});Python (Flask)
@app.post("/c2b/confirm")
def confirm():
if not verify(request.get_data(), request.headers.get("X-PesaBridge-Signature"), SECRET):
return "", 401
b = request.get_json()
fulfil_once(b["TransID"], b["BillRefNumber"], b["TransAmount"])
return {"ResultCode": 0, "ResultDesc": "Received"}Test it (sandbox)
curl -X POST https://<your-operator>/api/bridgepay/v1/partner/c2b/simulate \
-H "Authorization: Bearer at_..." -H "Content-Type: application/json" \
-d '{ "msisdn":"2547xxxxxxxx", "amount":500, "bill_ref":"ORDER-77" }'Be idempotent
Key fulfilment on TransID so a redelivered confirmation never ships an order twice.

