Phone lookup
Turn a phone number into its hashed form, or a hashed phone back into a number. Safaricom hands out SHA-256 hashes instead of MSISDNs in some reports, and a delivery report you cannot tie back to a customer is a delivery report you cannot use.
Why this exists
On some Safaricom traffic the subscriber number arrives as a 64-character SHA-256 hash rather than the number itself. It appears that way in delivery reports, in shortcode events, and in the message record you download. Your CRM stores 0712345678, so without a way to move between the two forms you cannot answer “did this customer get their message”.
This endpoint converts in both directions and tells you which direction it took. It is the same lookup the portal's Hashed phone tool uses.
Look up a value
# Paste your token once (it starts with mbs_):
export MOBILESASA_TOKEN="mbs_your_token_here"
curl "https://api.mobilesasa.com/api/v1/tools/phone?value=0712345678" \
-H "Authorization: Bearer $MOBILESASA_TOKEN"Pass either form in value. A phone number may be local (0712345678), international (254712345678) or prefixed (+254712345678); a hash is any 64-character hex string. There is no separate endpoint per direction — the input decides.
Number to hash
{
"success": true,
"data": {
"input_type": "phone",
"msisdn": "254712345678",
"network": "safaricom",
"hash": "9532b0ef1e65c7dadf2d133e05cfe9b00fb8e9ad5806182bcb5255c2e7e9648bf",
"resolved": true
}
}This direction is arithmetic, not a lookup: the same number always produces the same hash, so you can compute it once and store it beside the number in your own database. That is the cheapest way to match hashed delivery reports at volume — no API call per report.
Hash to number
{
"success": true,
"data": {
"input_type": "hash",
"msisdn": "254712345678",
"network": "safaricom",
"hash": "9532b0ef1e65c7dadf2d133e05cfe9b00fb8e9ad5806182bcb5255c2e7e9648bf",
"resolved": true
}
}This direction is a genuine lookup, and it can miss. A hash is one-way arithmetic, so the only way back is a directory of numbers already seen. When there is no match you get a 200 with resolved: false and a plain-language note — a miss is a normal outcome, not an error:
{
"success": true,
"data": {
"input_type": "hash",
"hash": "0000000000000000000000000000000000000000000000000000000000000000",
"resolved": false,
"note": "We could not match this hashed phone to a phone number. It may belong to a subscriber we have never messaged."
}
}Fields
input_type—phoneorhash, whichever you sent.msisdn— E.164 without the plus, e.g.254712345678. Absent on an unresolved hash.network—safaricom,airtel,telkomorequitel. Empty when the prefix is unknown to us.hash— the SHA-256 form, always present when the number is known, so the two can be matched by eye.resolved—falseonly when a hash could not be turned back into a number.note— present only on an unresolved lookup, written for a human.
Scope
Requires team:messages:view on an mbs_ token — the same scope that reads your message history, since this answers a question about your own messages.
Using it well
- Store the hash beside the number. Hashing is deterministic, so computing it once at import beats calling this per delivery report.
- Treat an unresolved hash as expected. It means we have never messaged that subscriber, not that anything failed.
- Do not use it to expand lists. The lookup answers about numbers the platform has already handled; it is not a directory to enumerate, and volume patterns that look like enumeration are rate limited.