Send SMS and bulk email from your systems using API keys. Check credit balance, buy credits via M-Pesa, and receive delivery webhooks when configured in the app.
Download and import the collection into Postman to test SMS sending safely. Sandbox requests are accepted and statuses are simulated immediately — nothing is sent to a live SMS provider.
After importing, seed the sandbox firm on your server:
php artisan db:seed --class=CommunicationApiSandboxSeeder
Sandbox credentials (pre-filled in the environment file):
wl_test_communication_sms_sandboxhttps://www.worldledgers.com/api/v1/communicationSandbox test numbers:
254712345678 — simulated delivery success254700000099 — simulated delivery failure254700000088 — simulated insufficient credits| Method | Path | Description |
|---|---|---|
GET | /sms-credits/balance | Current SMS credit balance and firm rate |
POST | /sms-credits/purchase | Start M-Pesa STK to buy credits |
GET | /sms-credits/purchase/{payment_id} | Poll purchase status and applied credits |
POST | /sms | Schedule SMS to one or more numbers |
POST | /email | Schedule bulk email |
GET | /sms/{message_id} | SMS delivery status |
GET | /email/{message_id} | Email delivery status |
https://www.worldledgers.com/api/v1/communication
Include your API key on every request using either header:
X-API-Key: wl_comm_your_api_key_here
or
Authorization: Bearer wl_comm_your_api_key_here
GET /sms-credits/balance
Returns the current SMS credit balance for the firm tied to your API key, plus the KES rate per credit.
curl "https://www.worldledgers.com/api/v1/communication/sms-credits/balance" \
-H "X-API-Key: wl_comm_your_api_key_here"
{
"status": "success",
"data": {
"sms_credits": 125.5,
"sms_rate": 1.8,
"currency": "KES"
}
}
sms_rate is KES per 1 SMS credit (set by platform admins per firm).
Credits granted on purchase = amount paid ÷ sms_rate.
POST /sms-credits/purchase
{
"phone": "0712345678",
"amount": 500
}
Initiates M-Pesa STK for amount in KES. Poll purchase status after the customer pays:
GET /sms-credits/purchase/{payment_id}
{
"status": "success",
"data": {
"payment_status": "Received",
"amount_kes": 500,
"sms_rate": 1.8,
"sms_credits_applied": 277.7778,
"firm_sms_credits": 403.2778,
"estimated_credits": 277.7778
}
}
POST /sms
Schedule one or more SMS messages. Messages are queued and sent by the platform scheduler.
{
"message": "Hello {first_name}, your balance is {account_balance}.",
"recipients": ["254712345678", "0712345678"],
"send_at": "2026-06-10 14:00:00",
"reference": "invoice-reminder-42"
}
message (required) — up to 1000 characters.recipients (required) — array of Kenya mobile numbers (2547…, 07…, or 7…).send_at (optional) — schedule for later. Defaults to now.reference (optional) — your correlation ID, echoed in webhooks.
Message placeholders {first_name}, {last_name}, {name}, and {account_balance}
are filled automatically when the recipient matches a client or address-book contact in your firm.
{
"status": "success",
"message": "SMS scheduled successfully.",
"data": {
"blast_id": "SMS-…",
"scheduled": 2,
"skipped": [],
"reference": "invoice-reminder-42"
}
}
POST /email
Schedule HTML or plain-text emails to multiple recipients.
{
"subject": "Payment reminder",
"message": "<p>Hello {first_name}, please pay your invoice.</p>",
"recipients": ["customer@example.com", "billing@example.com"],
"send_at": "2026-06-10 09:00:00",
"reference": "billing-campaign-june"
}
Placeholders in the message body are resolved the same way as SMS when a matching contact exists.
{
"status": "success",
"message": "Emails scheduled successfully.",
"data": {
"blast_id": "MAIL-…",
"scheduled": 2,
"skipped": [],
"reference": "billing-campaign-june"
}
}
GET /sms/{message_id} · GET /email/{message_id}
Poll delivery status for a message created via the API.
{
"status": "success",
"data": {
"type": "sms",
"event": "communication.sms.delivered",
"message_id": 123,
"blast_id": "SMS-…",
"reference": "invoice-reminder-42",
"recipient": "254712345678",
"status": "delivered",
"status_code": 1,
"response": "Success"
}
}
Status codes: 0 pending, 3 processing, 1 delivered, 2 failed.
When a webhook URL is configured in the app, World Ledgers sends a POST request
for each status change on API-sent messages.
Content-Type: application/jsonX-WL-Event — event name (e.g. communication.sms.delivered)X-WL-Signature — sha256=<hmac> when a webhook secret is set{
"type": "sms",
"event": "communication.sms.delivered",
"message_id": 123,
"blast_id": "SMS-…",
"reference": "invoice-reminder-42",
"recipient": "254712345678",
"status": "delivered",
"status_code": 1,
"response": "Success",
"firm_id": 1,
"timestamp": "2026-06-10T14:05:00+03:00"
}
Email events include communication.email.delivered, communication.email.failed, communication.email.opened, and communication.email.clicked.
expected = 'sha256=' + HMAC_SHA256(raw_request_body, webhook_secret)
valid = timing_safe_equal(expected, request.headers['X-WL-Signature'])
401 — Missing or invalid API key
403 — Account suspended or expired
422 — Validation error or no valid recipients
429 — Rate limit exceeded
curl "https://www.worldledgers.com/api/v1/communication/sms-credits/balance" \
-H "X-API-Key: wl_comm_your_api_key_here"
curl -X POST "https://www.worldledgers.com/api/v1/communication/sms" \
-H "Content-Type: application/json" \
-H "X-API-Key: wl_comm_your_api_key_here" \
-d '{
"message": "Hello {name}, your payment is due.",
"recipients": ["254712345678"],
"reference": "demo-001"
}'