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. |
| 402 | PREPAID company: balance finished or no active pack. JSON has prepaid: true, error_en, error_ar. Nothing was sent; top up to continue (Call / WhatsApp +968 9732 9001). |
| 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","شباب"]}'
Sends a WhatsApp message from SHABAB's own number (company #1) to SB or a staff phone, for example "new lead". Not for customers' companies.
Token: the server's WA_NEXUS_TOKEN (same token NEXUS uses), sent as Authorization: Bearer ….
POST /api/nexus/alert
{ "to": "96897329001", "template": "new_lead_alert", "lang": "en", "vars": ["Ahmed", "Perfume POS", "+968 9xxx"] }
or { "to": "96897329001", "text": "New lead: Ahmed - Perfume POS" }
200 { "ok": true, "kind": "template", "id": "m…", "wamid": "wamid…", "status": "sent", "to": "9689••••700" }
400 missing / wrong "to", or neither template nor text · 401 token wrong · 405 not POST
422 "window closed – template needed" (text only works inside 24 h after that phone wrote to SHABAB's number; a template always works)
429 too many alerts (30 per minute) · 502 Meta refused (message says why)
The alert log keeps only masked numbers. The NEXUS summary shows "alerts today".
The company owner makes API keys in the portal: Portal → API & webhook → New key (shown once; only a hash is kept; keys can be revoked). The key works like the admin key: Authorization: Bearer sbwa_…. Limit: 120 requests per minute per company (then 429).
POST /api/v1/messages same body as /api/v1/send: {"to","template","lang","params"} or {"to","text"} (text only inside 24 h)
POST /api/v1/contacts { "phone": "96891234567", "name": "Fatma", "tags": ["customer"], "fields": {"city": "Muscat"},
"order": {"no": "SO-1024", "status": "out_for_delivery", "total": "OMR 12.500"} }
→ { "ok": true, "contact": { phone, name, tags, fields, optedOut, windowOpen, lastIn, unread, assigned, isNew } }
(fields order_no / order_status / order_total are saved; a follow-up rule "order_status = out_for_delivery" fires at once)
GET /api/v1/contacts/96891234567 → the same contact object (404 if unknown)
GET /api/v1/conversations?since=2026-10-14T06:00:00Z&limit=100
→ { "conversations": [ { phone, name, lastAt, lastIn, unread, windowOpen, lastMessage: {dir, kind, text, status, at}, tags, assigned, needsHuman, optedOut } ] }
curl -X POST https://wa.shababinfotech.com/api/v1/messages -H "Authorization: Bearer sbwa_xxxxxxxx" -H "Content-Type: application/json" \
-d '{"to":"96891234567","template":"order_confirmed","lang":"en","params":["Fatma","SO-1024","OMR 12.500","Estee Perfumes"]}'
curl -X POST https://wa.shababinfotech.com/api/v1/contacts -H "Authorization: Bearer sbwa_xxxxxxxx" -H "Content-Type: application/json" \
-d '{"phone":"96891234567","name":"Fatma","order":{"no":"SO-1024","status":"out_for_delivery"}}'
curl "https://wa.shababinfotech.com/api/v1/conversations?since=2026-10-14T06:00:00Z" -H "Authorization: Bearer sbwa_xxxxxxxx"
Set in the portal: API & webhook → Webhook (https address + secret shown once). We POST JSON with headers X-Shabab-Event, X-Shabab-Delivery (number), X-Shabab-Timestamp (unix seconds),
X-Shabab-Signature: sha256=HMAC_SHA256(secret, raw body) and X-Shabab-Signature-V2: sha256=HMAC_SHA256(secret, timestamp + "." + raw body). Answer 2xx within 10 s; otherwise we retry after 1 min, 5 min, 15 min and 1 hour, then mark it failed (visible in the portal log).
{ "event": "message.received", "company_id": 7, "at": "2026-10-14T06:01:02.000Z",
"message": { "id": "m…", "wamid": "wamid…", "from": "96891234567", "name": "Fatma", "type": "text", "text": "Is my order ready?", "media_id": null, "timestamp": "…", "new_contact": false } }
{ "event": "message.status", "message": { "id": "m…", "wamid": "…", "to": "96891234567", "status": "delivered" | "read" | "failed", "error": "", "template": "order_confirmed" } }
{ "event": "contact.stop" | "contact.start", "contact": { "phone": "96891234567" } } { "event": "ping", "message": "Hello from SHABAB WHATSAPP 1.3.0" }
Portal → Settings → Website chat button: switch ON, copy the one-line script and put it before </body> on the website. A floating WhatsApp button with a greeting appears; it opens wa.me (on Android the WhatsApp app). Colours follow the company theme; clicks are counted in the portal (Settings and Reports).
If the website has a strict Content-Security-Policy, allow script-src https://wa.shababinfotech.com (the button sets its styles from the script, no inline styles needed).
<script src="https://wa.shababinfotech.com/widget.js?c=PUBLIC_ID" async></script>
SHABAB WHATSAPP 1.3 · SHABAB INFOTECH, Muscat.