The need
An internal WhatsApp channel — light and reusable — to send OTPs and notifications without paying for the official APIs or going through their review. One building block that every product (Mon Bara, TransitSoft) consumes through an SDK.
Baileys, without Chromium
No WhatsApp Web inside a headless Chromium (heavy, fragile, RAM-hungry). Baileys speaks the WhatsApp protocol directly over a WebSocket. The result: it runs on a small VPS.
- Server — Express + Socket.IO + Baileys + BullMQ
- Dashboard — Next.js: create a session, scan the QR code, generate the keys
packages/shared— types + Zod schemas + socket events (the source of truth)packages/sdk— the@wa/sdknpm client
Multiple sessions & keys
Each session is one WhatsApp number, created by QR code from the dashboard, with an API key shown exactly once. The consuming project never sees Baileys: just a baseUrl and a key.
Rate limiting is survival
WhatsApp is quick to ban a number that spams. Everything sent goes through a rate-limited BullMQ queue (15 messages/min, configurable), with retry backoff and controlled concurrency. Live statuses over Socket.IO:
queued → sending → sent → delivered → read | failed
Stack
Baileys · Express · BullMQ · Socket.IO · Drizzle ORM · Redis · npm SDK
Full write-up: An internal WhatsApp API with Baileys (no Chromium).