Quickstart
Get a working WA-B install talking to the WhatsApp Cloud API in five minutes. Prerequisites: Node 18+, PostgreSQL 14+, ngrok, and a Meta WhatsApp Cloud API app.
# 1. Install
npm install
# 2. Create the database
createdb whatsapp_saas
# 3. Configure environment
cp .env.example .env
# 4. Run the migration
npm run migrate
# 5. Seed the SaaS plans + sample products
npm run seed
# 6. Issue an admin API key
npm run issue-key admin "my dashboard"
# 7. Start the dev server
npm run dev
Server boots on http://localhost:3000. Health check: GET /health.
ngrok http 3000 in a second terminal and paste the HTTPS URL into PUBLIC_BASE_URL so Meta, Paystack, and Hubtel can reach your webhooks.
Webhook setup
WA-B persists every inbound webhook event to a durable queue before returning 200 OK. A background processor drains the queue and retries failed events with exponential backoff (up to 8 attempts).
Meta (WhatsApp Cloud API)
In Meta for Developers → your app → WhatsApp → Configuration:
- Callback URL:
https://your-host/api/webhooks/whatsapp - Verify token: same value as
WA_VERIFY_TOKENin.env - Subscribe to the
messagesfield
Tenant routing
Each business must have wa_phone_number_id set. This is how inbound webhooks are routed:
UPDATE businesses
SET wa_phone_number_id = '<Phone Number ID from Meta>'
WHERE id = '<your-business-uuid>';
Without this, inbound messages for that tenant are silently dropped (fail-safe: never leak between tenants).
WhatsApp command language
Customers and merchants drive WA-B by sending plain-text commands. They're case-insensitive and forgiving of punctuation.
| Command | Audience | What it does |
|---|---|---|
MENU / HI | Customer | Welcome message + Order Now button |
ORDER | Customer | Open the product list |
CANCEL | Customer | Clear current cart |
PAY / RENEW | Merchant | Initiate MoMo charge |
STATUS | Merchant | Plan, period end, message quota |
UPGRADE | Merchant | Switch plans |
API authentication
All mutating routes require an API key:
Authorization: Bearer sk_admin_xxxx
# or
X-Api-Key: sk_admin_xxxx
Two scopes are supported:
| Scope | Access |
|---|---|
admin | All routes |
tenant | Own business's orders & subscriptions only |
Issue a tenant key:
npm run issue-key tenant <business-uuid> "POS station 1"
The plaintext key is printed once. Only a SHA-256 hash is stored. Revoke any time:
UPDATE api_keys SET revoked_at = NOW() WHERE id = '<key-uuid>';
Orders API
| Method | Path | Auth |
|---|---|---|
| GET | /api/orders?business_id=... | admin / tenant |
| GET | /api/orders/:id | admin / tenant |
| POST | /api/orders | admin / tenant |
| PATCH | /api/orders/:id/status | admin / tenant |
/api/openapi.json on every WA-B install.

