Daraja
Twin of the Safaricom Daraja (M-Pesa) API.
Twin of the Safaricom Daraja (M-Pesa) API.
| Covers | sandbox.safaricom.co.ke |
| Auth | OAuth client credentials, consumer key and secret |
| Webhooks | Yes |
| Operations | 6 |
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
SecurityCredentialanswers HTTP 200 carryingResponseCode "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
99999999is accepted and the smaller99999999.99is 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 doesexpires_inon the token, which is a string. - A repeated
OriginatorConversationIDis refused with HTTP 500 and500.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.