Repository navigation
Releases: kepas-tech/daraja-js
Release list
@kepas/daraja-js@1.6.3
Patch Changes
- 7c7b563: Catalogue
TP40153forb2c,b2b,balanceandreversal: the API operator that signed the request has no permission for this API, or is not in this organisation. Safaricom returns it as plain text today, so a caller gets theResultDescwith no meaning, no retriable hint and no terminal hint attached. The proof is a production integration's own operator classification (production-code), the same class of proof as the2001handler entry; Safaricom publishes no reference for it, so nothing is claimed beyond what that integration handles. Only these four scopes carry it — an endpoint that has not shown the code keeps passing it through verbatim.
@kepas/daraja-js@1.6.2
Patch Changes
- 74bf3d9: Bill Manager: a 401
Invalid Access Tokenon the documented path (/v1/billmanager-invoice/…) is sent once more on the path Safaricom's go-live email lists for the production product (/v1/billmanager-invoice/v1/billmanager-invoice/…). On a production app with the product ticked, the documented path is refused at the gateway while the same token runs every other product. A 401 never reaches Bill Manager, so nothing can be sent twice. Proof and the email's full list in docs/specs/bill-manager.md.
@kepas/daraja-js@1.6.1
Patch Changes
- ead6290:
hakikisha.lookup: when Safaricom answers HTTP 400 with its own envelope (body.message, e.g. "The customer does not exist."), the thrownDarajaAPIErrornow carries that message and the request id, the same as a 200 withheader.status400, instead of a bare "Daraja request failed (HTTP 400)".
@kepas/daraja-js@1.6.0
Minor Changes
- 8f6e53a: B2C Hakikisha:
hakikisha.lookup({ phone })returns a customer's registered name (first name in the clear, middle and last masked by Safaricom) from a phone number before a payout. Synchronous, retryable; a non-200header.statusthrowsDarajaAPIErrorwith Safaricom'sbody.message. Spec proof in docs/specs/b2c-hakikisha.md.
Patch Changes
- 7819028: A 401 from Daraja's gateway now discards the cached OAuth token (memory and the shared
TokenStore) and sends the request once more with a fresh one. Daraja binds a token to the API products the app had when it was issued, so after a product is added on the portal every call to it answered 401 "Invalid Access Token" for up to an hour. The second send is safe for every call: a 401 is refused at the gateway before M-Pesa sees the request. A second 401 with the fresh token still throwsDarajaAuthError.
@kepas/daraja-js@1.5.1
Patch Changes
- 412f4ee: A 401 from Daraja keeps the gateway's own
errorMessageinDarajaAuthError.message(for example "Invalid Access Token" or "Invalid API call as no apiproduct match found"), so a caller can tell a bad token from a product that is not enabled on the app. The response body still lives only in the non-enumerableraw.
@kepas/daraja-js@1.5.0
Minor Changes
-
eca78c8: Transaction Status by OriginatorConversationID, B2C v3, and initiator-credential codes.
status.transactionacceptsoriginatorConversationIdin place of (or alongside)transactionId, so a transaction that never returned a receipt can still be queried. Neither id →DarajaValidationError. The receipt-only body is unchanged.parseStatusResultliftstransactionStatusandreceipt(from theTransactionStatus/ReceiptNoresult parameters, key spelling tolerated) ontoStatusResult;paramsis untouched.b2c.sendacceptsoriginatorConversationId; when given the request goes to/mpesa/b2c/v3/paymentrequestwithOriginatorConversationIDin the body. Omitted → v1 exactly as before.- Catalog:
b2centries for2001(initiator information invalid) and8006(security credential locked), and ab2bentry for2001, proofsafaricom-docs(docs/specs/business-to-pochi.md,docs/specs/b2c-account-topup.md,docs/specs/tax-remittance.md).b2b8006 is not catalogued yet (no Safaricom document for a b2b endpoint lists it); the SDK passes Safaricom'sResultDescthrough verbatim. status.transactionnow rejects blank or whitespace-only ids locally withDarajaValidationError(1.4.1 sent an emptyTransactionIDto Safaricom and surfaced the rejection asDarajaAPIError).- Type note:
TransactionStatusInput.transactionIdis now optional (string | undefined); callers that only read the type may need a narrowing.
@kepas/daraja-js@1.4.1
Patch Changes
- 4ea2ebf: Repository moved to the KEPAS Technologies organization:
github.com/kepas-tech/daraja-js. Package metadata (repository,homepage,bugs), the API reference URL, and all community links now point at the new home. No runtime changes.
@kepas/daraja-js@1.4.0
Minor Changes
-
f368fa2: Sync all documentation to v1.3.0 code reality and add the proven Lipa na Bonga result codes.
- Add Bonga catalog entries to
CATALOG(sync200; processing6000/6001/6004–6009/6011/1037/1031/2001/17, proof=safaricom-docs) so the declaredbongascope is no longer hollow. docs/ERROR_CODES.mdnow renders all 13 scopes (was missingbillmanager,ratiba,b2bexpress,bonga).- README "What's covered" lists every shipped namespace/method (the 8 v1.3.0 APIs were absent); the roadmap no longer advertises shipped features as "post-1.0". ROADMAP.md rewritten to reflect shipped reality.
- CI now fails if
docs/ERROR_CODES.mddrifts from the catalog, and a unit test asserts every catalogued scope is rendered — making doc drift impossible to merge.
- Add Bonga catalog entries to
Patch Changes
- 16bfea6: Add PREREQUISITES.md — a per-API guide to the exact Safaricom credentials, Daraja product enablement, operator roles, and Go-Live each capability requires (auth tiers grounded in the SDK's own guards; product/role facts from the official specs). Linked from the README and shipped in the npm tarball.
@kepas/daraja-js@1.3.0
Minor Changes
- 9eac3d6: Add B2B Express Checkout (
daraja.express.checkout) — vendor-initiated USSD push to a merchant's till (/v1/ussdpush/get-msisdn). OAuth-only, camelCase body,code/statussync ack; auto-generatesRequestRefID(UUID) when omitted. AddsparseExpressCallbackfor the FLAT async callback (top-levelresultCode, noResult{}envelope). Newb2bexpresscatalog scope (0,4104,4102sync;0,4001async). - 9eac3d6: Add
b2b.topUp(B2C Account Top Up —BusinessPayToBulk, loads a B2C shortcode's Utility account; optionalrequester) andb2b.remitTax(Tax Remittance to KRA —PayTaxToKRA, PartyB fixed572572,prnsent as AccountReference, own/mpesa/b2b/v1/remittaxendpoint). Both reuse the b2b initiator guard, ack, andparseB2bResult. Verified live against production:topUprejected an invalid PartyB synchronously;remitTaxaccepted (ResponseCode 0). - 9eac3d6: Add Bill Manager support (
daraja.billManager):optIn,updateOptIn,sendInvoice,sendBulkInvoices(≤1000),cancelInvoice,cancelBulkInvoices,acknowledgePayment, plusparseBillManagerPayment(inbound payment push) andbillManagerAck. Bill Manager uses its own success convention (stringrescode "200", notResponseCode "0") and anapp_keyheader obtained fromoptIn(pass per-call asappKeyor setconfig.billManagerAppKey).http.postnow accepts an additiveheadersoption; fixed auth/content-type headers always win the merge. Newbillmanagercatalog scope (200/409). - 9eac3d6: Add Lipa na Bonga (
daraja.bonga):calculatePoints(read-only points→KES conversion, retryable) andredeem(redeem Bonga points as payment to a paybill/till). OAuth-only, nestedheader/bodyenvelope, success isheader.responseCode 200. The redemption result settles on the existing C2B confirmation callback (parseC2bConfirmation) — no Bonga-specific result parser. Newbongacatalog scope. - 9eac3d6: Add Query Organization Info (
daraja.orgInfo.query) — synchronous, read-only shortcode validation (/sfcverify/v1/query/info) returning org name, tariff (ChargeProfileID), and asuccessflag. OAuth-only, idempotent (retryable).identifierTypemapspaybill→4 /till→2. Success is gated onResponseMessage === "Success"+ an OrganizationName (the spec's numeric success code is contradictory —4000vs0— so the raw code is exposed verbatim, not asserted). - 9eac3d6: Add Business To Pochi (
daraja.b2c.toPochi) — pay a customer's business wallet (pochi la biashara) via/mpesa/b2pochi/v1/paymentrequest+BusinessPayToPochi. Reuses B2C auth, theResponseCode "0"ack, andparseB2cResult. Caller-suppliedoriginatorConversationId(dedupe guard, auto-generated UUID if omitted) and optionaloccasion(sent as Safaricom's misspelledOccassion). - 9eac3d6: Add M-Pesa Ratiba support (
daraja.ratiba.create) — create a customer standing order (recurring collection) via/standingorder/v1/createStandingOrderExternal. OAuth-only; success is the nestedResponseHeader.responseCode "200". AddsparseRatibaCallbackfor the async result (nestedresponseBody.responseData[]name/valuepairs, parsed case-insensitively),RatibaFrequency(1–8), andpaybill/buygoodstransaction-type mapping (the latter sends Safaricom's misspelledStanding Order Customer Pay Marchant). Newratibacatalog scope (200,0,1037,1032,2001,1050,1051).
v1.0.0 — @kepas/daraja-js
First stable release. @kepas/daraja-js on npm; API reference.
npm install @kepas/daraja-jsComplete Daraja coverage
STK Push · C2B (register + parse) · B2C · B2B + float transfers · Account Balance · Transaction Status (sync + async) · Reversal · Dynamic QR · Pull Transactions — every endpoint a production PayBill uses.
Plus: generateSecurityCredential, Stripe-compatible webhooks (sign/verify, sync + edge), a pluggable cross-process token cache (tokenStore, e.g. Redis — optional, in-memory by default), and the full DarajaError hierarchy.
Quality
- 130 tests, CI green on Node 20/22/24 × ubuntu/macOS/Windows.
- Published via npm OIDC Trusted Publishing with provenance; SBOM attached per release.
- Passed an independent pre-1.0 security review — no blocking findings. Hardening:
DarajaError.rawis non-enumerable (won't dump customer PII into logs); webhook verification is raw-body + constant-time + replay-windowed; retries never re-send a timed-out payment POST (no double-charge). - Encodes 16 production gotchas distilled from a live PayBill.
Stability
The public API is now under SemVer — breaking changes bump the major.