Phone number formats
How Wajub reads a Mobile Money number, country by country, and what it rejects.
Mobile Money is identified by phone number everywhere Wajub touches it: a customer paying in, a payout leaving your balance, a marketplace settling a seller. Get the format wrong and the payment fails before the customer ever sees a prompt, which makes this the most common integration error we see.
Send E.164
Wajub expects the international form: a +, the country dialling code, then the subscriber number.
No spaces, no dashes, no parentheses, no leading zero from the national format.
+[country code][subscriber number]
+237670000000 Cameroon, MTN
+221770000000 Senegal, Orange
+254700000000 Kenya, SafaricomA number that arrives without its country code is not automatically refused. When the call carries
a country to anchor it on (a channel like cm.mtn is enough), a local number such as 670000000
is read as +237670000000. Without that anchor there is nothing to guess from, and the number comes
back as a 422.
Send E.164 anyway
Relying on the fallback means relying on your own channel being right. Normalise on your side,
at the point where the customer types the number, and you remove a whole class of failure from
every call that follows.
Every country Wajub reaches
Twenty-three countries: each one where the catalogue announces a channel paid with a phone number, with its dialling code and those channels.
| Country | Dialling code | Channels |
|---|---|---|
+229 | MTN MoMo, Moov Money | |
+226 | Orange Money, Moov Money | |
+237 | MTN MoMo, Orange Money, Express Union | |
+242 | MTN MoMo, Airtel Money | |
+225 | MTN MoMo, Orange Money, Moov Money, Wave, Djamo | |
+243 | Airtel Money, Vodacom M-Pesa, Orange Money, Africell Money | |
+20 | Vodafone Cash | |
+241 | Airtel Money, Moov Money | |
+233 | MTN MoMo, Vodafone Cash, AirtelTigo Money | |
+224 | MTN MoMo, Orange Money | |
+254 | M-Pesa, Airtel Money | |
+231 | MTN MoMo | |
+265 | Airtel Money, TNM Mpamba | |
+223 | Orange Money, Moov Money | |
+258 | M-Pesa | |
+250 | MTN MoMo, Airtel Money | |
+221 | Orange Money, Free Money, Wave, Djamo | |
+232 | Orange Money | |
+27 | Capitec Pay | |
+255 | M-Pesa, Tigo Pesa, Airtel Money, HaloPesa | |
+228 | T-Money, Moov Money | |
+256 | MTN MoMo, Airtel Money | |
+260 | MTN MoMo, Airtel Money, Zamtel Kwacha |
How the operator is identified
This is where production and sandbox genuinely differ, and the difference matters.
In production, Wajub identifies the operator that owns the number from current numbering data. A number it cannot attribute to a supported operator is treated as having none.
Some channels are named after a wallet instead of an operator, and the number is read accordingly.
Wave (ci.wave, sn.wave), Express Union (cm.eu), Djamo (ci.djamo, sn.djamo), Capitec Pay
(za.capitec), OPay (ng.opay) and PalmPay (ng.palmpay) are held whatever the payer's operator,
so a number is never refused on them because of the network it belongs to. The other wallets belong to one operator, and a number of that
operator pays on the wallet's channel: ke.mpesa takes Safaricom numbers, tz.mpesa and
mz.mpesa take Vodacom numbers, and gh.vodafone takes the numbers of Telecel, formerly Vodafone
Ghana.
Every other channel is named after its operator, such as zm.zamtel, mw.tnm or cd.africell. An
aggregate resolves the same way: a Telecel number sent on mobile is charged on gh.vodafone.
In sandbox, no real number exists to look up, so Wajub matches the prefixes below instead. They
are synthetic: they exist to make test outcomes deterministic, not to describe how real numbers are
allocated. A wallet held whatever the operator, such as Capitec Pay, takes a test number of any
operator, its own prefix (+2761…) or another's, but never a number that is no test number.
Do not build a prefix table of your own
The prefixes below are the sandbox's. Copying them into your code means reimplementing operator detection with data that is wrong in production, and stale the day a range is reassigned. Send the number and let Wajub resolve it.
| Country and operator | Sandbox prefix | Channel |
|---|---|---|
+23767… | cm.mtn | |
+23769… | cm.orange | |
+23768… | cm.eu | |
+22505… | ci.mtn | |
+22507… | ci.orange | |
+22501… | ci.moov | |
+22503… | ci.wave | |
+22177… | sn.orange | |
+22176… | sn.free | |
+22178… | sn.wave | |
+22990… | bj.mtn | |
+22991… | bj.moov | |
+22670… | bf.orange | |
+22671… | bf.moov | |
+23324… | gh.mtn | |
+23320… | gh.vodafone | |
+23327… | gh.airteltigo | |
+25470… | ke.mpesa | |
+25478… | ke.airtel | |
+25677… | ug.mtn | |
+25675… | ug.airtel | |
+25078… | rw.mtn | |
+25073… | rw.airtel | |
+25567… | tz.airtel | |
+25565… | tz.tigo | |
+25562… | tz.halopesa | |
+25576… | tz.mpesa | |
+24399… | cd.airtel | |
+24389… | cd.orange | |
+24381… | cd.vodacom | |
+24390… | cd.africell | |
+2410… | ga.airtel | |
+2416… | ga.moov | |
+24206… | cg.mtn | |
+24204… | cg.airtel | |
+26096… | zm.mtn | |
+26097… | zm.airtel | |
+26095… | zm.zamtel | |
+26599… | mw.airtel | |
+26588… | mw.tnm | |
+22370… | ml.orange | |
+22366… | ml.moov | |
+22899… | tg.moov | |
+22890… | tg.tmoney | |
+22466… | gn.mtn | |
+22462… | gn.orange | |
+23188… | lr.mtn | |
+25884… | mz.mpesa | |
+2010… | eg.vodafone | |
+23276… | sl.orange | |
+2761… | za.capitec |
Letting Wajub pick the channel
The hosted page and the components pick the operator with the customer, so this only concerns the
charge call of a Direct API integration. There, channel is required. Send
mobile, or cm.mobile for Cameroon, and Wajub resolves the operator from the number, then routes
to the best available provider for it. This is the case to prefer. These are the only two
aggregates: in another country, name the operator's channel, such as ci.orange.
The body of the charge call then names the aggregate and the number:
Name a precise channel such as cm.mtn only when you want to force one operator. If the number and
the channel disagree, the call is refused rather than silently rerouted.
An aggregate also works on a payment restricted with
accept. The restriction is checked on
the operator the number resolves to, so cm.mobile with an MTN number is accepted wherever cm.mtn
is, and refused where it is not.
What comes back when it is wrong
| Response | Cause | Fix |
|---|---|---|
422 on phone | No +, no country code, and nothing to anchor the number on | Normalise to E.164 before you send |
422 on channel | The channel names an operator that does not serve that country | Check the country's operators above |
422, number and channel disagree | The number belongs to another operator than the one you named | Send cm.mobile and let Wajub resolve it |
422 on phone, no operator found | No supported operator owns the number | Ask the customer to confirm their number, or send the operator they picked in data.operator |
All four are refused on the charge call, POST /payments/{id}, not when the payment is created. The
payment stays pending and nothing reaches an operator, so the same payment can be charged again
once the number is fixed. A number the operator refuses after the prompt went out is a different
case: the payment fails, and the reason is in the failure reasons
catalogue.
Related pages
- Payment methods & channelsEvery operator, its channel, and where it works.
- Sandbox test numbersThe suffixes that drive each outcome in test mode.
- Failure reasonsWhy a payment that reached an operator came back failed.
- TransfersThe same format, used as a payout destination.
- SyncThe same format, used to settle a marketplace seller.