How to Initiate STK Push for Any Bank or Custom Paybill in Kenya Using Palpluss
Initiate M-Pesa STK Push to any bank Paybill (Equity, KCB, Co-op…) or any custom Paybill and account number with one API call. No bank API or Daraja setup.
- By
- PalPluss Engineering
- Published
- Reading time
- 9 min
The short answer
- Almost every Kenyan bank has an M-Pesa Paybill (Equity 247247, KCB 522522, Co-op 400200 and so on). An STK Push to that Paybill with your bank account number as the account reference deposits the customer's payment straight into your bank account.
- The same works for any custom Paybill — your own business Paybill, a SACCO, a school or a landlord's Paybill with a specific account number.
- With PalPluss you add the Paybill and account number once as a payment channel, then collect with a single POST to /v1/payments/stk — no bank API, no Daraja credentials, no passkey.
- The money goes from the customer's M-Pesa directly to your bank; PalPluss never holds it. PalPluss charges a flat KES 3 per STK Push (KES 10 and above) from a pre-funded service wallet.
Most businesses in Kenya want customer payments to end up in the bank, not sitting in an M-Pesa float that someone has to sweep every evening. The usual routes are painful: each bank has its own API, its own onboarding, its own contract and its own way of telling you a payment arrived. Building that for one bank takes weeks; supporting several is a project in itself.
There is a far simpler path that already works with practically every bank in the country: M-Pesa STK Push to the bank's Paybill. This guide shows how it works and how to wire it into your app in a few minutes with PalPluss.
How STK Push to a bank account works
Banks publish an M-Pesa Paybill number so customers can deposit into any account at that bank. When a customer pays that Paybill, the account number field tells the bank which account to credit. An STK Push simply fills both in for the customer: their phone shows a prompt for the right Paybill and account, they enter their PIN, and the bank credits your account.
- Your server asks PalPluss to send an STK Push for, say, KES 2,500.
- PalPluss sends the prompt with your bank's Paybill as the destination and your bank account number as the account reference.
- The customer enters their M-Pesa PIN.
- M-Pesa pays the bank's Paybill and the bank credits your account.
- PalPluss sends your webhook with
status: SUCCESSand the M-Pesa receipt, matched to your own order reference.
Why this beats integrating each bank's API
| Direct bank API | STK Push to bank Paybill (PalPluss) | |
|---|---|---|
| Banks supported | One per integration | Any bank or SACCO with an M-Pesa Paybill |
| Onboarding | Bank contract, API access request, sandbox approval | Add a payment channel in the console |
| Customer experience | Varies — often card or bank transfer | Familiar M-Pesa PIN prompt on their phone |
| Payment confirmation | Different callback format per bank | One webhook format for every bank |
| Switching or adding banks | New integration | Add another channel; pass its channelId |
| Time to first payment | Weeks | Minutes |
Not just banks: any custom Paybill
Nothing here is bank-specific. A PalPluss Paybill channel is simply a Paybill number plus the account number the payment should carry, so you can initiate STK Push to any Paybill that accepts an account number:
- Your own business Paybill with a fixed account, e.g.
SALESor a branch code. - A SACCO or microfinance Paybill with a member or loan account number.
- A school fees Paybill with the student's admission number.
- A landlord's or property manager's Paybill with a house or unit number.
Collecting into a Till (Buy Goods) instead? Add a Till channel — no account number needed — and the same API call works.
Step 1: Find the Paybill and account number
You need two things: the Paybill number and the account number to credit. For a bank, that's the bank's M-Pesa Paybill and your bank account number. These are some of the commonly used bank Paybills:
| Bank | M-Pesa Paybill |
|---|---|
| Equity Bank | 247247 |
| KCB Bank | 522522 |
| Co-operative Bank | 400200 |
| NCBA Bank | 880100 |
| Absa Bank Kenya | 303030 |
| Family Bank | 222111 |
Step 2: Add the Paybill as a payment channel (1 minute)
In the PalPluss console open Payment Channels and create a channel:
- Type: Paybill
- Paybill / shortcode: the bank's or custom Paybill, e.g.
247247 - Account number: your bank account number (or the account the custom Paybill expects)
- Name: something you will recognise, e.g. “Equity — main account”
Mark it as your default channel and every STK Push will pay into that Paybill and account. You don't need Daraja credentials, a passkey or your own shortcode — PalPluss sends the prompt for you.
Step 3: Create an API key and fund your service wallet (1 minute)
Open API Keys, create a key and store it as an environment variable on your server — it is shown only once. Then top up your service wallet, which pays the flat KES 3 fee per STK Push. New accounts start with KES 15 in free tokens, enough for a few test payments.
PALPLUSS_API_KEY=pk_live_xxxxxxxxxxxxxxxxxxxxxxxx
# Optional: only if you collect into more than one bank account
PALPLUSS_BANK_CHANNEL_ID=6b5184bf-06a9-4f1a-98ed-02d5fb6e2b95Step 4: Send the STK Push (1 minute)
One authenticated POST. Put your own order or invoice ID in accountReference — the bank account number already lives on the channel, so this reference is yours for reconciliation and comes back in the webhook.
curl -X POST https://api.palpluss.com/v1/payments/stk \
-u "$PALPLUSS_API_KEY:" \
-H "Content-Type: application/json" \
-d '{
"amount": 2500,
"phone": "0712345678",
"accountReference": "INV-2041",
"transactionDesc": "Invoice 2041",
"channelId": "'"$PALPLUSS_BANK_CHANNEL_ID"'",
"callbackUrl": "https://yourapp.com/webhooks/palpluss"
}'Leave out channelId to use your default channel. The API answers straight away with a PENDING transaction — save its transactionId against the invoice. The same call from Node.js and Python:
import { PalPluss } from "@palpluss/sdk";
const palpluss = new PalPluss({ apiKey: process.env.PALPLUSS_API_KEY });
export async function collectToBank(invoiceId: string, phone: string, amount: number) {
const stk = await palpluss.stkPush({
amount,
phone,
accountReference: invoiceId, // your reference, echoed in the webhook
transactionDesc: "Invoice payment",
channelId: process.env.PALPLUSS_BANK_CHANNEL_ID, // omit to use the default channel
callbackUrl: "https://yourapp.com/webhooks/palpluss",
});
await saveTransactionId(invoiceId, stk.transactionId);
return stk.status; // "PENDING"
}import os, requests
resp = requests.post(
"https://api.palpluss.com/v1/payments/stk",
auth=(os.environ["PALPLUSS_API_KEY"], ""),
json={
"amount": 2500,
"phone": "0712345678",
"accountReference": "INV-2041",
"transactionDesc": "Invoice 2041",
"channelId": os.environ.get("PALPLUSS_BANK_CHANNEL_ID"),
"callbackUrl": "https://yourapp.com/webhooks/palpluss",
},
timeout=30,
)
resp.raise_for_status()
print(resp.json()["data"]["transactionId"])Step 5: Confirm the deposit with a webhook (1 minute)
When the customer pays, cancels or lets the prompt time out, PalPluss POSTs to your callbackUrl. Mark the invoice paid only on SUCCESS, and keep the mpesa_receipt — it is the same receipt that appears against the deposit on your bank statement, which makes reconciliation a lookup instead of a guessing game.
app.post("/webhooks/palpluss", async (req, res) => {
const { transaction } = req.body;
if (transaction.status === "SUCCESS") {
// Idempotent: safe if the same callback arrives twice.
await markInvoicePaid({
invoiceId: transaction.external_reference, // "INV-2041"
mpesaReceipt: transaction.mpesa_receipt, // matches the bank statement
amount: transaction.amount,
});
}
res.sendStatus(200); // acknowledge quickly
});What it costs
- PalPluss: a flat KES 3 per STK Push for KES 10 and above, whatever the amount; KES 1–9 is free. No monthly or setup fees. See pricing.
- M-Pesa and your bank: standard Safaricom Paybill tariffs apply to the payment, and some banks charge their own fee on M-Pesa deposits — check your bank's tariff.
Production checklist
- Test with a real deposit to each bank channel before you switch customers over.
- Compute the amount on your server from the order — never trust an amount sent by the browser or app.
- Make the webhook idempotent — key fulfilment on the transaction ID or your invoice ID.
- Store the M-Pesa receipt for every successful payment; it is your link to the bank statement.
- Respect rate limits — up to 2 STK requests per phone number per minute (
429 STK_RATE_LIMITED) and 60 requests per minute per API key. - Keep your success rate healthy — accounts whose recent STK success rate drops below 30% are paused for 30 minutes (
429 TRANSACTION_HEALTH_BLOCKED). Only send prompts customers asked for. - Keep the service wallet funded — requests fail with
402 INSUFFICIENT_SERVICE_BALANCEwhen it is empty.
When you need something else
New to STK Push? Start with the general STK Push guide. Need to pay people out — refunds, commissions, payroll? That's B2C payouts. Want a checkout without writing backend code? Drop in the Paylink widget.
Frequently asked questions
Can I receive M-Pesa STK Push payments directly into my bank account?
Yes. Banks in Kenya publish an M-Pesa Paybill, and the account number on that Paybill is your bank account number. With PalPluss you save the bank's Paybill and your account number as a payment channel, and every STK Push deposits straight into that account.
Can I initiate STK Push to a custom Paybill with a specific account number?
Yes. Any Paybill that accepts an account number works — your own business Paybill, a SACCO, a school fees Paybill or a landlord's Paybill. Add it in PalPluss as a Paybill channel with the account number, and STK Push payments go to that Paybill and account.
Which banks does this work with?
Any bank or SACCO that accepts deposits through an M-Pesa Paybill — including Equity (247247), KCB (522522), Co-operative Bank (400200), NCBA (880100), Absa (303030) and Family Bank (222111). Confirm the Paybill and account format with your bank before going live.
Do I need my bank's API or Safaricom Daraja credentials?
No. You don't integrate with the bank and you don't need a Daraja app, passkey or your own shortcode. PalPluss sends the STK Push to your bank's Paybill using the account number on your channel.
Does PalPluss hold my money?
No. The customer pays the bank's Paybill directly from M-Pesa and the bank credits your account. PalPluss only sends the prompt and reports the result by webhook.
How do I match a bank deposit to an order?
Put your order or invoice ID in accountReference when you send the STK Push. PalPluss returns it as external_reference in the webhook together with the M-Pesa receipt number, which also appears on your bank statement.
Can I collect into more than one bank account?
Yes. Create one Paybill channel per account — the same bank Paybill can be reused with different account numbers — and pass the channelId on each STK Push to choose the destination.
How much does it cost?
PalPluss charges a flat KES 3 per STK Push for amounts of KES 10 and above (KES 1–9 is free), with no monthly or setup fees. Standard M-Pesa Paybill tariffs and any bank deposit fees apply separately.
Build on PalPluss
M-Pesa collections and payouts in one API. KES 15 in free tokens to start.