B2B Hakikisha
Verify an organization's registered name and tariff before you pay it.
B2BHakikishaClient wraps POST /sfcverify/v1/query/info. "Hakikisha" is Swahili for "make sure" that's exactly what this does: confirms a Till/Paybill number resolves to the organization you think it does, before you send a B2B payment to it.
This is a lookup, not a payment
Hakikisha does not move money. It's a pre-flight check you run before initiating an actual B2B payment through your own banking/ERP-side integration or Daraja's B2B Payment Request flow which this SDK does not currently implement (see Roadmap). Think of it as the M-Pesa equivalent of a bank account name lookup before a wire transfer.
Request & response
type B2BHakikishaIdentifierType = "2" | "4";
// '2' Lipa na M-PESA Till number / M-PESA agent till
// '4' PayBill, B2C account, or any other shortcode
interface B2BHakikishaRequest {
identifierType: B2BHakikishaIdentifierType;
identifier: string; // the shortcode being looked up, digits only
}
interface B2BHakikishaResponse {
ConversationID: string;
ResponseCode: string; // "0" means success
ResponseMessage: string;
DetailedMessage: string;
OrganizationShortCode: string;
OrganizationName: string;
ChargeProfileID: string;
}const result = await daraja.b2bHakikisha.query({
identifierType: "4",
identifier: "666677",
});
if (result.ResponseCode === "0") {
console.log(
`Paying: ${result.OrganizationName} (${result.OrganizationShortCode})`,
);
} else {
console.log("Lookup failed:", result.ResponseMessage);
}identifier is validated to be digits-only whitespace is trimmed
automatically, but letters or symbols throw INVALID_B2B_HAKIKISHA_REQUEST
immediately rather than sending a malformed lookup to Daraja.
Practical use
Wire this into your B2B payment form as a "confirm recipient" step, the same way banking apps show you the account holder's name before you approve a transfer:
async function confirmRecipient(shortCode: string) {
try {
const result = await daraja.b2bHakikisha.query({
identifierType: "4",
identifier: shortCode,
});
return {
name: result.OrganizationName,
chargeProfileId: result.ChargeProfileID,
};
} catch (error) {
// Wrong shortcode entirely surface this before the user commits to paying it
return null;
}
}Sandbox coverage is thin
Hakikisha's sandbox environment only resolves a small, undocumented set of test shortcodes correctly most arbitrary sandbox shortcodes come back with a generic "organization not found" response even when they're otherwise valid test Paybills for other endpoints. Don't treat a sandbox failure here as proof your integration is broken; verify against Safaricom's actual published Hakikisha test values (from the Daraja portal's test credentials page) before assuming a bug in your code.