Skip to main content

Webhooks

Results arrive at your server the moment a transaction settles. No polling required. Supply a callback URL on each payment request. PalPluss sends a POST to that URL when the transaction reaches a terminal state. Your endpoint must safely handle repeated deliveries — the same callback may arrive more than once on retry.

Callback payload

Payload fields

event_type values

M-Pesa result_code

Non-zero codes indicate failure.
Use mpesa_receipt as proof of payment in your records. It is the official M-Pesa confirmation number and is unique per successful transaction.

Delivery and retries

Return a 2xx status code to acknowledge receipt. Any other response triggers a retry. After 5 failed attempts, delivery stops. Poll GET /transactions/{id} to retrieve the final status.

Callback URL requirements

  • Must be a publicly accessible HTTPS URL
  • Must respond within 30 seconds
  • Must return 2xx regardless of how you process the payload
  • Must handle duplicate deliveries safely
Do not use localhost, 127.0.0.1, or private/RFC1918 addresses. PalPluss cannot reach these from its servers.

Idempotent handler

Use transaction.id as your idempotency key to guard against duplicate deliveries.
Return 200 before processing. This prevents delivery timeouts from triggering unnecessary retries.

Local testing

Expose your local server with ngrok or localtunnel:

Per-request callback URLs

PalPluss does not use a single global webhook URL. Each payment request carries its own callback URL. This lets you route callbacks for different flows or environments to different endpoints. Both endpoints use callbackUrl (camelCase): STK Push:
B2C Payout: