SMS API
HTTP Basic Auth, JSON bodies, atomic wallet writes, refunds on failed sends, Mobile Money top-ups. The same API our dashboard uses.
The Wesendall API is intentionally small. Wallet, top-up, balance, send single, send bulk, send to group, list groups, history. That's it. Anything more is over-engineering.
POST /api/v1/sms/send — single or array of recipients.
POST /api/v1/sms/group — send to a saved contact group.
GET /api/v1/sms/history — paginated history, per wallet.
POST /api/v1/groups + /groups/{id}/contacts — manage groups.
POST /api/v1/account/topup — kick off a Mobile Money charge.
GET /api/v1/account/balance — current UGX balance.
Charges happen inside a Postgres transaction with a balance-guard. Concurrent OTP storms can't double-debit. Failed provider sends refund inside the same request. Idempotent deposit status transitions prevent double-credits.
Atomic wallet.updateMany with `balance >= cost` guard.
$transaction-wrapped send + log writes.
Refund-on-failure inside the request lifecycle.
Deposit status idempotency (only credit pending deposits).
Why developers pick it
Generate keys in the dashboard. Use them in any HTTP client. No SDK.
Prioritized routing on MTN and Airtel for verification traffic.
Per-recipient status callbacks for retry, escalation and reporting.
Automate wallet recharges from your own backend. No human intervention.
lastUsedAt on every key. Revoke compromised keys in one click.
The dashboard hands you cURL templates pre-filled with your live walletId.
FAQ
https://www.wesendall.com/api/v1 — every endpoint is under that prefix. Use HTTP Basic Auth with your API key as the username and your API secret as the password.
POST to /api/v1/sms/send with JSON body { walletId, message, recipient }. The recipient field accepts a single phone, an array, or a comma/space separated string of phones.
Median end-to-end delivery is 3-5 seconds on MTN Uganda, 4-7 seconds on Airtel Uganda. Wesendall prioritizes verification traffic on dedicated routing.
We don't enforce explicit per-minute caps for normal traffic. Wallet balance is the gate: any send is rejected with 402 insufficient_balance if you don't have enough. Contact us for high-volume routing.
If MARZ rejects a send (e.g. invalid number), Wesendall refunds the wallet automatically inside the same request lifecycle — you never pay for messages that didn't go out.
Create an account, mint a key, paste the cURL. The next message is yours.