One simple API. Every company has its own API key (starts with sbwa_, made in admin → Companies → New API key, shown once).
Send it as Authorization: Bearer sbwa_…. Base address: https://wa.shababinfotech.com. All bodies are JSON (UTF-8).
Use an approved template. Outside the 24-hour window only templates can be sent.
POST /api/v1/send
Authorization: Bearer sbwa_xxxxxxxx
Content-Type: application/json
{ "to": "96891234567", "template": "attendance_late_alert", "lang": "en",
"params": ["Ahmed", "08:25 AM", "SHABAB INFOTECH"] }
Answer: {"ok":true,"id":"m3f…","wamid":"wamid.HBg…","status":"accepted","category":"utility"}. Keep id to check the status later.
Template with a PDF on top (template must have a DOCUMENT header): add "header": {"document": {"url": "https://…/payslip.pdf", "filename": "payslip.pdf"}}.
OTP: {"to":"9689…","template":"otp_code","lang":"en","params":["482913"]} (the copy-code button is filled for you; the code is not kept in the log).
Allowed only if this person wrote to the company's number in the last 24 hours. Else the answer is 422 with "window": false — send a template instead.
{ "to": "96891234567", "text": "Your order is ready. Thank you!" }
{ "to": "96891234567", "document": { "url": "https://…/invoice.pdf", "filename": "invoice.pdf", "caption": "Invoice SO-1024" } }
{ "to": "96891234567", "image": { "url": "https://…/photo.jpg", "caption": "New stock" } }
POST /api/v1/media
{ "filename": "payslip.pdf", "mime": "application/pdf", "data": "<base64>" }
→ { "ok": true, "id": "1234567890" } then use { "document": { "id": "1234567890", "filename": "payslip.pdf" } }
PDF, JPG, PNG, Office files, plain text. Max 15 MB.
GET /api/v1/status/m3f… (our id or the wamid)
→ { "id": "m3f…", "status": "delivered", "category": "utility", "billable": true, "error": null, … }
Status goes queued → accepted → sent → delivered → read, or failed (with error).
| Call | What |
|---|---|
GET /api/v1/me | Company name, number, status. |
GET /api/v1/templates | Templates of the company with status (APPROVED / PENDING / REJECTED) and number of values. |
POST /api/v1/read | {"message_id":"wamid…"} — blue ticks for an incoming message. |
Give the number with country code, digits only: 96891234567. +968 9123 4567 and 0096891234567 also work.
An 8-digit Oman number (91234567) gets the company's default country code (968).
SB sets a Callback URL (https) for the company in admin → Companies → Edit, and makes a callback secret. Each incoming message is sent as:
POST <your callback URL>
X-Shabab-Event: message
X-Shabab-Signature: sha256=<HMAC-SHA256 of the raw body with the callback secret, hex>
{ "event": "message", "company_id": 7,
"message": { "id": "m…", "wamid": "wamid…", "from": "96891234567", "name": "Ahmed", "type": "text", "text": "Hello", "timestamp": "2026-10-13T08:00:00.000Z" } }
Check the signature (timing-safe compare) and answer 200 quickly.
| Code | Meaning |
|---|---|
| 400 | Something missing or wrong in the body (message says what). |
| 401 | API key wrong or missing. |
| 403 | Company disabled. |
| 409 | Company not ready (no WhatsApp token / number yet). |
| 422 | Outside the 24-hour window, or template rejected. |
| 429 | Too many requests (120 per minute per company). |
| 502 | Meta refused it — the message starts with Meta: and says why. |
| Name | Values (params) |
|---|---|
attendance_late_alert | employee name, time, company |
daily_absent_summary | date, absent, late, company |
payslip_ready | employee name, month, company |
leave_status | employee name, from, to, status, company |
order_confirmed | customer name, order no., total, shop |
otp_code (authentication) | code |
curl -X POST https://wa.shababinfotech.com/api/v1/send \
-H "Authorization: Bearer sbwa_xxxxxxxx" -H "Content-Type: application/json" \
-d '{"to":"96891234567","template":"payslip_ready","lang":"ar","params":["أحمد","سبتمبر 2026","شباب"]}'
SHABAB WHATSAPP 1.0 · SHABAB INFOTECH, Muscat.