API and resource commands
One grammar for every resource, plus a raw request when you need it.
Most of the ninety one commands are the same five verbs applied to eleven resources. Learn the grammar once and you can drive the whole API without opening the reference.
wajub <resource> list
wajub <resource> retrieve <id>
wajub <resource> create -d field=value
wajub <resource> update <id> -d field=value
wajub <resource> delete <id>payments, customers, refunds, transfers, beneficiaries, links, invoices, events,
disputes, accounts and webhooks all answer to it, minus the verbs that make no sense for
them. There is no refunds update, because a refund cannot be edited. shield, identity and
skills have their own shape, covered at the end of this page.
Sending a body
Two ways, and they do not mix. -d is for the common case, --data-raw for everything else.
# Repeatable key=value. Dots build nested objects.
wajub payments create \
-d amount=25000 \
-d currency=XAF \
-d channel=cm.mtn \
-d customer.email=amina@example.com \
-d customer.phone=+237670000000
# Anything with arrays, or a body you already have
wajub webhooks create --data-raw '{
"url": "https://example.com/webhooks/wajub",
"events": ["payment.succeeded", "payment.failed"]
}'Arrays need `--data-raw`
-d builds objects through dots, not lists. events[0]=payment.succeeded is sent as a field
literally named events[0], which the API rejects. Any body with an array goes through
--data-raw.
Listing
Every list command carries the same nine flags.
| Flag | Effect |
|---|---|
--limit | Maximum items. With --all, caps the total rather than the page |
--page | A specific page |
--all | Follow pagination until the end, or until --limit |
-q, --search | Full text search |
-f, --filter | key=value, repeatable, sent as a query parameter |
--status | Filter by status |
--since | Created on or after, YYYY-MM-DD |
--until | Created on or before, YYYY-MM-DD |
--expand | Expand a nested resource, repeatable |
wajub payments list --limit 5
wajub payments list --status failed --since 2026-01-01
wajub payments list -q amina@example.com
wajub transfers list --all --limit 500 --json > transfers.json--since and --until are whole days. The API filters payments on the calendar date, so there is
no way to ask for the last two hours from here. For a narrower window, list a day and filter the
JSON yourself.
Raw requests
When a route has no dedicated command, or you want to see exactly what the API returns, three commands take any path.
wajub get /balance
wajub get "/payments?per_page=5&status=succeeded"
wajub get /tax/settings
wajub post /customers -d email=amina@example.com -d name="Amina Nkem"
wajub post /transfers --data-raw '{"amount":100000,"currency":"XAF","beneficiary":"ben_…"}'
wajub delete /payments/trx_CSUGajfv9xh0XQ5wu2lxThe path is relative to the API base, and the key comes from your profile, so there is no header
to remember and no key in your history. Quote any path containing &, or your shell eats it.
Two flags that change what the request means
--idempotency-key sets the Idempotency-Key header. The CLI generates a fresh one for every
create call anyway, so you only pass it explicitly when you want two invocations to be treated
as the same attempt. It is honoured on payments, transfers and refunds.
--sync sets the X-Sync header, making the call act on behalf of a connected account. It is
available on every write command, and on nothing that would not make sense.
# Run this twice: the second call returns the first result, it does not charge twice
wajub payments create -d amount=25000 -d currency=XAF --idempotency-key order-4172
# Create the payment on a connected merchant's account
wajub payments create -d amount=25000 -d currency=XAF --sync acc_7Yh2MpL4tRb3nP8sZcXvOutput
Human output is a table. --json prints the API response envelope unchanged, so lists come back
under items and the balance under balance. That is what you pipe.
wajub payments list --status failed --json \
| jq -r '.items[] | [.id, .amount, .failure_reason] | @tsv'
wajub balance --json | jq '.balance.available'
# Every failed payout of the month, as CSV
wajub transfers list --status failed --since 2026-01-01 --json \
| jq -r '.items[] | [.id, .amount, .currency, .failure_reason] | @csv'Commands that ask before acting
Anything that moves money or destroys a record prompts for confirmation. --yes (or -y, or the
alias --force) skips the prompt, which is what you want in a script and nowhere else.
| Command | Why it asks |
|---|---|
transfers create | Sends money |
refunds create | Sends money back |
payments process | Charges the payer |
webhooks rotate-secret | Invalidates the secret your server is using |
accounts token | Rotates a connected account's token |
Every delete | Removes the record |
The read only commands
Four commands need no arguments and answer questions you would otherwise look up.
wajub balance # your balance, per currency
wajub channels # every channel slug you may use
wajub countries # supported countries
wajub currencies # supported currencieswajub channels is the one to reach for when a payment fails with an unknown channel: it lists
exactly the slugs your account may use, which is not always the full catalogue.
In sandbox, wajub balance returns a simplified object where available equals total and the
other figures are zero. The full breakdown exists in live only.
Identity and Shield
Two topics do not follow the CRUD grammar because the underlying products do not.
# Who owns this wallet, before you send money to it
wajub identity resolve -d phone=+237670000000 -d channel=cm.mtn
wajub identity validate -d account=00012345678 -d bank=afriland
# Shield
wajub shield stats
wajub shield settings
wajub shield blocklist
wajub shield block +237670000000 --type phone --reason "chargeback ring"
wajub shield unblock blk_9mWvL2xR7tB5nY4hC6dF--type accepts email, phone, ip, country and card_bin. Shield's blocklist applies to
live traffic, so a sandbox profile is not where you test whether a block works.