Beneficiaries
Save a payout destination once, then pay it by id instead of by number.
A beneficiary is a destination you have saved: a name, a Mobile Money number, and the operator that serves it. You create it once and pay it by id afterwards, which keeps the number out of every payout call you make and out of the logs those calls leave behind.
It is also the only way to pay the same person twice without retyping where the money goes, and retyping is how money reaches the wrong phone.
Mobile Money only, whatever the fields suggest
Creation accepts iban, swift and a set of vault fields, but the channel you pass has to be a
Mobile Money channel that can pay out. Anything else answers 422 with Channel not found or not available for payout. Only Mobile Money and phone-addressed wallet channels (e.g. Djamo) are supported for transfers. Take that message at its word on the first half only: no Djamo channel
is in the catalogue today, so Mobile Money is the whole of it. Providers &
channels lists the forty-nine that exist. Bank destinations do
exist, added by Wajub in the back office as the privileged type further down, and they are not
something your integration creates.
Creating one
https://api.wajub.com/beneficiarieschannelstringrequiredcm.mtn or cm.orange. See below for the generic form.namestringrequiredphonestringoptionalaccount_number. The field is phone, not phone_number.account_numberstringoptionalphone fills this in when you omit it.emailstringoptionalcountry_codestringoptionalcm.mtn gives CM.notestringoptionalmetadataobjectoptionalSend it with the secret key. Beneficiary creation carries its own tighter rate limit than the rest of the API, since adding a destination is half of what an attacker needs to move money.
curl https://api.wajub.com/beneficiaries \
-H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: beneficiary-amina" \
-d '{
"channel": "cm.mtn",
"name": "Amina Traoré",
"phone": "+237670000000"
}'The answer is 201 Created. The id is what every payout takes, prefixed ben_ in live and
ben_test_ in sandbox, so the two environments can never be confused for one another.
The destination is not in the response
payment_method carries an id, a type and a label, never the number the money goes to. Nothing on
the API reads a beneficiary's destination back, so if you need to show a payee where their money
is being sent, keep your own copy at creation time. Fields you left empty are dropped from the
response rather than returned as null, and an empty metadata comes back as [] rather than
{}, so type it as a map that can arrive as an empty array.
The channel decides the operator
Pass the operator directly when you know it, cm.mtn or cm.orange. Pass the country's generic
channel, cm.mobile, when you do not, and the operator is worked out from the number itself.
That resolution can fail, and it fails loudly rather than picking one at random.
If you already know the operator, name it. The generic form is there for the case where you hold a number and nothing else.
The same destination twice returns the first one
Creation is keyed on the destination, not on what you send with it. If an active beneficiary
already exists for that number on that channel, the call returns that one unchanged, with the same
201 Created and the same success message as a real creation. The name, email and note you
just sent are discarded. Read the id that comes back rather than assuming it is new, and never
treat a 201 here as proof that a fresh record was written.
Paying another Wajub account
When the person you pay already has a Wajub account, use the wajub channel and their 11-digit
account number. The money never leaves Wajub: one balance is debited and the other credited, with no
network in between and nothing to wait for.
The account has to exist, it cannot be your own, and it cannot be an account Wajub has frozen —
each is refused with 422 when you create the beneficiary, and a payout to an account frozen since
then fails on its own. Paying it is an ordinary transfer, with the same limits and the same statuses;
it simply succeeds at once, and it is free — there is no network to pay between two Wajub
accounts. On an account whose beneficiaries Wajub checks, the name is matched against the recipient's
compliance file instead of an operator's records.
In sandbox
A sandbox payout on this channel is simulated like any other: it succeeds, fails or stalls on the scenario you asked for, and the recipient's sandbox balance is never credited. Only live moves money between accounts.
Paying one
Pass the id where a payout expects a recipient. Everything else about the transfer is unchanged.
https://api.wajub.com/transfersThe body is the same one Transfer reference documents, with a string where the inline object would have been.
Saving a payee does not make paying them safe
A beneficiary is a destination, not a guarantee against duplicates. Every POST /transfers still
needs its own Idempotency-Key, saved payee or not, or a retried request pays twice. See
Idempotency.
What you can change, and what you cannot
https://api.wajub.com/beneficiaries/{id}The update accepts five fields, and the destination is not among them.
| Field | Editable |
|---|---|
name, email, country_code, metadata | Yes |
phone | Yes, as a contact detail only |
channel, account_number, the payout destination | No |
Changing phone does not change where the money goes
The destination is fixed on the payment method attached when the beneficiary was created, and
nothing on the API touches it afterwards. Updating phone rewrites the contact number on the
record while the payout keeps going to the original one, with no warning and no difference in the
response. To pay a different number, create a second beneficiary and use its id.
Retrieving one
https://api.wajub.com/beneficiaries/{id}The same object creation returned, read back by its id. Use it to check a payee is still active before a payout, and remember it still will not tell you the number the money goes to.
curl https://api.wajub.com/beneficiaries/ben_7Kq2mX9vL4tRb3nP8sZc \
-H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…"A 404 covers three different situations without distinguishing them: the id does not exist, it
belongs to another account, or it belongs to the other environment. A ben_test_ id read with a
live key is the common one.
Listing them
https://api.wajub.com/beneficiariesResults come back newest first, twenty-five at a time unless you raise per_page, which caps at
100. The envelope is items and meta, and passing cursor switches the pagination mode and the
shape of meta with it, the same way it does on transfers.
search does not behave the same in both environments. In sandbox it is a direct match on the name,
the email, the phone and the id. In live it goes through the search index, which also covers the
country code and the type, so a live search returns results a sandbox search would not.
is_active filters on the flag. type is the filter worth understanding.
| Type | Created by | Verified |
|---|---|---|
standard | Your own API calls | On creation |
privileged | Wajub, in the back office | Only once Wajub verifies it |
The list returns standard only unless you ask otherwise, and that default is deliberate. A
privileged beneficiary can sit unverified, and a live payout to an unverified destination is refused
with This beneficiary payment method has not been verified yet and cannot receive live payouts.
Pass type=privileged or type=all when you genuinely want to see them.
Your own beneficiaries are marked verified the moment they are created, because the merchant vouches for its own payees. That is what makes a standard beneficiary payable straight away.
Deleting one
https://api.wajub.com/beneficiaries/{id}The answer is 200. The record stops appearing in lists and can no longer be paid, while transfers
that already went to it keep pointing at it, so your history stays readable.
There is no restore on the API. Creating the same destination again gives you a new beneficiary with
a new id, not the old one back.
What reaches your webhook
| Event | Fires when |
|---|---|
beneficiary.created | A record is actually written. Not on the duplicate return above, which creates nothing |
beneficiary.updated | One of the five editable fields changes |
beneficiary.deleted | A beneficiary is deleted |
Related pages
- Transfers & PayoutsWhere payouts leave from, and what has to be true first.
- TransfersThe payout itself: statuses, routing, and failures.
- Transfers QuickstartSend your first payout to a beneficiary.
- IdempotencyThe header that makes a retried payout safe.
- Beneficiaries API referenceEvery field and the five endpoints.