Direct payments & payouts
Beyond the hosted checkout, the API exposes direct endpoints for collecting from and paying out to M-Pesa and banks. All of them require a company Bearer token, and all of them return a transaction you can poll at GET /v1/transactions/{id}.
Collections (money in)
M-Pesa STK push
POST /v1/transactions/m-pesa/c2b/initiate
Sends the payment prompt straight to the customer's phone:
curl -X POST https://sandbox.merchants.taifapay.africa/v1/transactions/m-pesa/c2b/initiate \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"phoneNumber": "0712345678",
"amount": 100,
"accountReference": "Account123",
"transactionDesc": "Top-up",
"externalId": "your-transaction-id"
}'
The customer enters their M-Pesa PIN on the prompt; the transaction completes (or fails/times out) shortly after. Use webhooks or poll the returned transaction id.
Pesalink bank transfer (C2B)
POST /v1/transactions/pesalink/c2b/initiate
Creates a pending bank transaction; it completes when the customer finishes the transfer from their own bank:
curl -X POST https://sandbox.merchants.taifapay.africa/v1/transactions/pesalink/c2b/initiate \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"accountReference": "Account123",
"transactionDesc": "Invoice settlement",
"externalId": "your-transaction-id"
}'
Payouts (money out)
:::caution Float required
Payouts are funded from your company float. Each initiate call checks and reserves amount + fee from your float balance first; if it is insufficient you get a 400 with the available and required amounts. Fees follow the standard M-Pesa tariff for the amount.
:::
M-Pesa B2C (send to phone)
POST /v1/transactions/m-pesa/b2c/initiate
{
"amount": 100,
"phoneNumber": "0712345678",
"accountReference": "Account123",
"transactionDesc": "Withdrawal",
"externalId": "your-transaction-id"
}
Pochi la Biashara
POST /v1/transactions/m-pesa/pochi/initiate
{
"amount": 100,
"pochiNumber": "0712345678",
"accountReference": "Account123",
"transactionDesc": "Payment to pochi"
}
Till (Buy Goods)
POST /v1/transactions/m-pesa/till/initiate
{
"amount": 100,
"tillNumber": "123456",
"accountReference": "Account123",
"transactionDesc": "Payment to till",
"initiatorPhoneNumber": "0712345678"
}
Paybill
POST /v1/transactions/m-pesa/paybill/initiate
{
"amount": 100,
"paybillNumber": "123456",
"accountNumber": "5551234",
"accountReference": "Account123",
"transactionDesc": "Payment to paybill",
"customerNumber": "0712345678"
}
PesaLink bank transfer (send to a bank account)
Pays out to any PesaLink-connected bank account. Unlike the M-Pesa payouts, this is a two-step flow so you can show the resolved account holder name to your user before any money moves.
:::info Path prefix
These two endpoints live under /v1/pesalink, not /v1/transactions.
:::
Step 1 — verify the beneficiary and prepare the transfer. This verifies the destination account with the receiving bank, reserves your float for amount + fee, and returns a transactionId for a pending transfer:
curl -X POST https://sandbox.merchants.taifapay.africa/v1/pesalink/bank-transfer/verify \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"destinationBankCode": "40407000",
"destinationAccount": "7357060021",
"amount": 5000,
"narrative": "Supplier payment",
"reference": "INV-2024-001"
}'
{
"transactionId": "WDRPSLK1A2B3C4D5E6F7A8B9",
"accountHolderName": "John ****",
"amount": 5000,
"fee": 22,
"currency": "KES",
"beneficiaryAccount": "7357060021",
"beneficiaryBankCode": "40407000",
"status": "pending",
"message": "Beneficiary verified and transfer prepared. Confirm with the transactionId to complete."
}
If verification fails or your float is insufficient, this returns 400 and no transfer is created.
Step 2 — complete the transfer using the transactionId from step 1. On success the amount + fee is drawn down from your float:
curl -X POST https://sandbox.merchants.taifapay.africa/v1/pesalink/bank-transfer/complete \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "transactionId": "WDRPSLK1A2B3C4D5E6F7A8B9" }'
{
"message": "Bank transfer completed successfully",
"transaction": { /* transaction object, type BANK_TRANSFER */ }
}
404 if the transactionId isn't a pending transfer for your merchant; 422 if it's already settled or the receiving bank rejects it.
See the PesaLink bank codes table below for the destinationBankCode values.
Tracking transactions
Every initiate endpoint responds with a transaction object. Fetch its latest state at any time:
curl https://sandbox.merchants.taifapay.africa/v1/transactions/{id} \
-H "Authorization: Bearer $ACCESS_TOKEN"
You can pass either the API's transaction id or your own externalId reference. Statuses move pending → complete or failed; payouts release their float reservation if they fail.
Idempotency and reconciliation
- Always set
externalIdto your own unique id for the operation. It is echoed back on transactions and webhooks, letting you match events to your records. - If an initiate call times out on your side, do not blindly retry — first look the transaction up by your
externalIdto see whether it was created.
PesaLink bank codes
Use the code as destinationBankCode when preparing a PesaLink bank transfer. The Live column reflects PesaLink availability at the time of writing — banks marked No or ON PILOT may reject transfers.
| Bank | Code | Live |
|---|---|---|
| ABC Bank | 40435000 | Yes |
| ABSA | 40403000 | Yes |
| Access Bank | 40426000 | Yes |
| Bank of Africa | 40419000 | Yes |
| Bank of Baroda | 40406000 | Yes |
| Bank of India | 40405000 | No |
| Caritas Microfinance Bank | 40448000 | Yes |
| Choice Microfinance Bank | 40436000 | Yes |
| CIB Bank Kenya | 40465000 | Yes |
| Citi Bank | 40416000 | Yes |
| Consolidated Bank | 40423000 | Yes |
| Cooperative Bank | 40411000 | Yes |
| Credit Bank | 40425000 | Yes |
| Development Bank | 40459000 | ON PILOT |
| DIB Bank | 40475000 | Yes |
| DTB | 40463000 | Yes |
| Eco Bank | 40443000 | Yes |
| Equity Bank | 40468000 | Yes |
| Family Bank | 40470000 | Yes |
| Faulu Bank | 40479000 | Yes |
| Gt Bank | 40453000 | Yes |
| Guardian Bank | 40455000 | Yes |
| Gulf African Bank | 40472000 | Yes |
| Habib Bank AG Zurich | 40417000 | Yes |
| Housing Finance | 40461000 | Yes |
| I&M Bank | 40457000 | Yes |
| KCB Bank | 40401000 | Yes |
| Kingdom Bank | 40451000 | Yes |
| KWFT | 40478000 | Yes |
| LOOP-NCBA | 40401380 | Yes |
| M-Oriental | 40414000 | Yes |
| Middle East Bank | 40418000 | Yes |
| National Bank | 40412000 | Yes |
| NCBA | 40407000 | Yes |
| Paramount | 40450000 | Yes |
| Post Bank | 40462000 | Yes |
| Premier Bank | 40474000 | Yes |
| Prime Bank | 40410000 | Yes |
| SBM | 40460000 | Yes |
| Sidian Bank | 40466000 | Yes |
| Stanbic Bank | 40431000 | Yes |
| Standard Chartered Bank | 40402000 | Yes |
| Stima SACCO | 40489000 | Yes |
| Telkom Kenya | 40497000 | Yes |
| UBA Bank | 40476000 | Yes |
| UMBA Microfinance Bank | 40467000 | Yes |
| Unaitas Sacco | 40432000 | Yes |
| Victoria Bank | 40454000 | Yes |
| VOOMA | 40493000 | Yes |