Skip to Content
TwinsDaraja (M-Pesa)

Daraja

Twin of the Safaricom Daraja (M-Pesa) API.

Twin of the Safaricom Daraja (M-Pesa) API.

Coverssandbox.safaricom.co.ke
AuthOAuth client credentials, consumer key and secret
WebhooksYes
Operations6

Coverage

The OAuth token endpoint, STK push and STK push query, B2C payment requests, transaction status and account balance. Every business call acknowledges immediately and delivers its real result to the CallBackURL or ResultURL you send, so those have to be reachable from the sandbox. See Webhooks.

Daraja pins its version in the path and nowhere else, one version per product: B2C is v3, everything else v1. There is no header, no query selector and no negotiation, so you choose a dialect by choosing a URL.

Credentials

GET /oauth/v1/generate?grant_type=client_credentials takes HTTP Basic with your consumer key and secret, and mints the bearer token every other call carries. Any well-formed pair mints one by default, so your existing key and secret need no change. Set auth.mode to enforced to accept only the pair the world knows. See Credentials.

SecurityCredential and InitiatorName are checked for presence only, which is all the real gateway does on the synchronous call.

Vendor specific details

  • The acknowledgement is always a success. Once validation passes you get ResponseCode "0", whatever the transaction eventually does. The outcome, including failure, arrives later on your callback URL.
  • Account balance with a missing SecurityCredential answers HTTP 200 carrying ResponseCode "1005". Code that branches on the status line reads that as success.
  • The amount limit is a string length rule, not a numeric one. At most eight characters, so 99999999 is accepted and the smaller 99999999.99 is refused.
  • Result codes are strings in some places and integers in others. STK query and the balance callback send "ResultCode": "0"; the B2C result and the STK callback send an integer. So does expires_in on the token, which is a string.
  • A repeated OriginatorConversationID is refused with HTTP 500 and 500.002.1001. That is the dedup that stops a double disbursement, so a retry loop reusing the id will not get a second payment.

QueueTimeOutURL is validated and stored but never called, because Safaricom’s timeout payload has never been captured cleanly enough to reproduce. Test that path with an armed fault instead.

Its own notes

Once a sandbox is running, this twin serves its own operating notes at {control_url}/veris/manual: the credentials it accepts, the API versions it serves, and the fault statuses you can arm against it. They are generated from the same source as its behaviour, so they cannot drift.