Libraries & tools
CLI
Send a real M-Pesa STK Push from your terminal, decode any Daraja result code offline, and forward live webhooks to localhost without ngrok.
The paylod CLI sends a real M-Pesa STK Push from your terminal, decodes Daraja result codes offline, and forwards live webhooks to localhost without ngrok. It talks to the same API documented in the API reference.
npm install -g @paylod/cliThe package is @paylod/cli; the command it installs is paylod.
Or run it without installing:
npx @paylod/cli errors 1032Node 20 or newer.
Set up
paylod initinit signs you in (it opens your browser), lets you pick an application, and mints an API key for this machine. Credentials go into your OS keychain, or a 0600 file under ~/.config/paylod if there is no keychain available.
If you only want to sign in:
paylod login
paylod whoami # who am I, and where is the token stored?For CI, skip the login entirely and set PAYLOD_API_KEY in the environment. Every data-plane command (collect, status, listen) will use it.
Take a payment
paylod collect --phone 254712345678 --amount 100The customer's phone rings. The CLI polls until the payment settles and prints the outcome — an M-Pesa receipt on success, or a decoded reason on failure.
| Flag | What it does |
|---|---|
-p, --phone <msisdn> | Customer's number. 0712…, 254712…, +254712… all work. |
-a, --amount <kes> | Whole shillings. M-Pesa rejects decimals. |
-r, --ref <reference> | Your own correlation id (invoice/order number), returned on the result. ≤ 12 chars. Shown to the payer only on a Paybill; a Till never displays it. |
-d, --description <text> | Payment description on the STK prompt. ≤ 64 chars. |
--idempotency-key <key> | Reuse to retry safely without double-charging. |
--no-wait | Fire the push and exit immediately, without polling. |
--timeout <secs> | How long to wait for settlement. Default 120. |
--env <sandbox|production> | Which M-Pesa environment to charge on. |
--api-key <key> | Use this key instead of PAYLOD_API_KEY or your stored profile. |
Look a payment up later, or watch it settle:
paylod status e69e5c00-8a01-44ed-b003-48e2e86a7c9e
paylod status e69e5c00-8a01-44ed-b003-48e2e86a7c9e --watchstatus decodes the failure reason for you — you never have to look 2001 up in a PDF.
Decode a result code
Offline. No login, no network, no API key.
paylod errors 1032 # why did that payment fail?
paylod errors --list # the whole catalogue
paylod errors 2001 --json # machine-readable, for your error handlerThis is the fastest way to answer "what does 2001 mean" mid-incident. The same catalogue is browsable in the error reference.
4999 and 500.001.1001 decode as pending, not failed — they mean the customer has not entered their PIN yet. The payment is still live and still payable. Do not show a customer a failure on those codes.
Webhooks on localhost, without ngrok
paylod listen --forward http://localhost:3000/webhooklisten streams your application's live webhook events and re-POSTs each one to your local server, with the signature intact — so the code you are writing verifies a real signature against a real payload. No tunnel, no public URL, no ngrok.
| Flag | What it does |
|---|---|
-f, --forward <url> | POST each event to this local URL. |
-e, --events <types...> | Only forward these types (payment.success, payment.failed). |
--print-json | Print the full JSON payload of every event. |
--secret <whsec> | Signing secret. Otherwise taken from your profile, or fetched. |
--tolerance <secs> | Signature freshness window. Default 300; 0 disables the check. |
--skip-verify | Do not verify the HMAC. Debugging only. |
--port <port> | Local receiver port. Default 4242. |
Roll (or fetch) the signing secret with:
paylod webhooks secretTest without spending money
paylod simulate --outcome user_cancelled
paylod simulate --interactivesimulate creates a payment in the paylod sandbox and forces an outcome — no Daraja call, no handset, no money. It is how you exercise your failure paths in CI.
| Outcome | Result code |
|---|---|
approve | 0 |
wrong_pin | 2001 |
insufficient_funds | 1 |
user_cancelled | 1032 |
timeout | 1037 |
Manage the integration
These commands do everything the dashboard does.
| Command | What it manages |
|---|---|
paylod apps list | create | use | rename | delete | Applications — one per till or paybill. |
paylod creds | Your Daraja consumer key, secret, shortcode and passkey. Write-only. |
paylod keys mint | list | revoke | use | paylod API keys (mp_test_… / mp_live_…). |
paylod webhooks list | add | toggle | delete | secret | Webhook endpoints. |
paylod payments | Browse your M-Pesa payments. |
paylod orgs | Organizations. |
Scripting
Every command takes a global --json flag and prints machine-readable output.
paylod collect -p 254712345678 -a 100 --json | jq -r '.mpesaReceipt'paylod collect exits 5 if the payment is still pending when the timeout expires — which is not a failure, just an unanswered prompt. A non-zero exit on a settled failure carries the decoded reason in the JSON.
Environment variables
| Variable | What it does |
|---|---|
PAYLOD_API_KEY | Merchant API key. Skips the config file entirely — this is how you run paylod in CI. |
PAYLOD_WEBHOOK_SECRET | Signing secret for paylod listen. |
PAYLOD_API_BASE | Override the API base. Defaults to https://paylod.dev/functions/v1. |
PAYLOD_CONFIG_DIR | Where the CLI stores config. Default ~/.config/paylod. |
PAYLOD_NO_KEYCHAIN=1 | Never use the OS keychain; use the 0600 file instead. Useful on headless Linux and in containers. |
NO_COLOR | Disable colour. |
mp_live_… keys move real money. Keep them out of shell history and out of committed .env files.
Next
- Building an app rather than a script? Use the Node SDK.
- Wiring an AI agent? The MCP server exposes the same capabilities as tools.
- The HTTP endpoints underneath: API reference.