Complete guide to the WA Gateway — workflows, API endpoints, and multi-number setup.
The WA Gateway is a self-hosted WhatsApp notification engine with three layers:
pending rows from MySQL and continues. No messages are lost.
opted_out and never sent. The contact can re-subscribe at any time by replying YES.
Every API request must include a shared secret key as an HTTP header:
X-API-Key: your_api_secret_here
The secret is set in two places — they must match exactly:
engine/.env → API_SECRET=your_secretdashboard/config.php → define('API_SECRET', 'your_secret')X-API-Key receive HTTP 401 Unauthorized.
Base URL: https://wa.libralb.online (or http://127.0.0.1:3000 internally)
/api/status
Returns the status of the default WhatsApp session.
{"name":"default","status":"online","phone":"9611234567","last_seen":"2025-05-11 14:00:00","qr_code":null}
curl -H "X-API-Key: YOUR_SECRET" https://wa.libralb.online/api/status
$result = apiCall('GET', '/api/status');
// $result['status'] === 'online' | 'offline' | 'connecting'
const res = await fetch('/api/status', { headers: { 'X-API-Key': SECRET } });
const data = await res.json();
// data.status, data.phone, data.last_seen
/api/sessions
Returns all configured WhatsApp sessions (supports multi-number).
[{"name":"default","status":"online","phone":"9611234567","last_seen":"..."},
{"name":"support","status":"offline","phone":null,"last_seen":null}]
curl -H "X-API-Key: YOUR_SECRET" https://wa.libralb.online/api/sessions
$sessions = apiCall('GET', '/api/sessions');
foreach ($sessions as $s) echo $s['name'] . ' → ' . $s['status'];
const sessions = await fetch('/api/sessions', { headers:{'X-API-Key':SECRET} }).then(r=>r.json());
sessions.forEach(s => console.log(s.name, s.status));
/api/send
Queues a text message. The engine delivers it with 8–22 s throttling.
| Field | Type | Required | Description |
|---|---|---|---|
phone |
string | Yes | Recipient phone number with country code, digits only. E.g. 9611234567 |
message |
string | Yes | Message text (supports Unicode, emoji, Arabic) |
session |
string | No | Session name to send from. Default: "default" |
{"success":true,"message_id":42}
curl -X POST https://wa.libralb.online/api/send \
-H "X-API-Key: YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{"phone":"9611234567","message":"Hello from the gateway!"}'
$result = apiCall('POST', '/api/send', [
'phone' => '9611234567',
'message' => 'Hello from the gateway!',
'session' => 'default', // optional
]);
echo $result['message_id']; // 42
const res = await fetch('/api/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': SECRET },
body: JSON.stringify({ phone: '9611234567', message: 'Hello!' })
});
const { message_id } = await res.json();
/api/send-image
Queues an image message. Image is auto-compressed (max 1280px, JPEG 78 %) before sending.
| Field | Type | Required | Description |
|---|---|---|---|
phone |
string | Yes | Recipient phone number |
file |
file | Yes | Image file (JPG/PNG/GIF/WEBP, max 10 MB) — multipart/form-data |
caption |
string | No | Optional image caption |
session |
string | No | Session name. Default: "default" |
{"success":true,"message_id":43}
curl -X POST https://wa.libralb.online/api/send-image \ -H "X-API-Key: YOUR_SECRET" \ -F "phone=9611234567" \ -F "caption=Check this out!" \ -F "file=@/path/to/photo.jpg"
$result = apiCall('POST', '/api/send-image',
['phone' => '9611234567', 'caption' => 'Hello!'],
'/var/www/uploads/photo.jpg' // absolute path — passed as CURLFile
);
const form = new FormData();
form.append('phone', '9611234567');
form.append('caption', 'Check this!');
form.append('file', fileInput.files[0]);
const res = await fetch('/api/send-image', {
method: 'POST', headers: { 'X-API-Key': SECRET }, body: form
});
/api/resend/:id
Resets a failed or opted_out message back to pending so the queue re-attempts delivery.
| Field | Type | Required | Description |
|---|---|---|---|
:id |
integer | Yes | Message ID from the messages table (visible in Message Audit log) |
{"success":true}
curl -X POST https://wa.libralb.online/api/resend/42 \ -H "X-API-Key: YOUR_SECRET"
$result = apiCall('POST', '/api/resend/42');
await fetch('/api/resend/42', { method:'POST', headers:{'X-API-Key':SECRET} });
/api/regenerate-qr
Forces a session disconnect and initiates a new QR code. Use when the dashboard shows "System Offline".
| Field | Type | Required | Description |
|---|---|---|---|
session |
string | No | Session name to reconnect. Default: "default" |
{"success":true,"message":"Reconnection initiated for session \"default\""}
curl -X POST https://wa.libralb.online/api/regenerate-qr \
-H "X-API-Key: YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{"session":"default"}'
$result = apiCall('POST', '/api/regenerate-qr', ['session' => 'default']);
await fetch('/api/regenerate-qr', {
method:'POST', headers:{'Content-Type':'application/json','X-API-Key':SECRET},
body: JSON.stringify({ session: 'default' })
});
/api/sessions
Creates a new WhatsApp session (adds a second/third number). The engine starts the connection immediately and a QR code appears in Sessions.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Unique session name — alphanumeric, dash, underscore, max 40 chars. E.g. "support", "sales" |
{"success":true,"message":"Session \"support\" created — check dashboard for QR code"}
curl -X POST https://wa.libralb.online/api/sessions \
-H "X-API-Key: YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{"name":"support"}'
$result = apiCall('POST', '/api/sessions', ['name' => 'support']);
await fetch('/api/sessions', {
method:'POST', headers:{'Content-Type':'application/json','X-API-Key':SECRET},
body: JSON.stringify({ name: 'support' })
});
/api/sessions/:name
Disconnects and removes a session. The default session cannot be deleted.
| Field | Type | Required | Description |
|---|---|---|---|
:name |
string | Yes | Session name to remove |
{"success":true}
curl -X DELETE https://wa.libralb.online/api/sessions/support \ -H "X-API-Key: YOUR_SECRET"
$result = apiCall('DELETE', '/api/sessions/support');
await fetch('/api/sessions/support', { method:'DELETE', headers:{'X-API-Key':SECRET} });
default, support, sales).engine/auth_state/<name>/."session": "support" to choose which number to use.support) and click Add.# API
curl -X POST https://wa.libralb.online/api/send \
-H "X-API-Key: YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{"phone":"9611234567","message":"From support line","session":"support"}'
# PHP (dashboard send.php selects session from a dropdown)
auth_state/<name> folder and regenerate from the Sessions page.
pm2 status or pm2 logs wa-gateway. Ensure the port 3000 is not blocked by a firewall. Verify API_SECRET matches in both .env and config.php.opt_in='yes' for internal contacts in the database, or trigger an inbound message from them.UPDATE contacts SET opt_in='pending', opt_in_at=NULL WHERE phone='9611234567' in MySQL.npm install inside the engine/ folder. Sharp requires native binaries that may need to be rebuilt: npm rebuild sharp.