Appearance
Admin API
Header: x-admin-key: <ADMIN_API_KEY>. The dashboard uses a session cookie instead. Leave ADMIN_API_KEY unset to disable header access.
Apps
| Method | Path | Description |
|---|---|---|
POST | /api/apps | Create an app. Body { "name", "webhookUrl"? }. Returns the app plus apiKey and webhookSecret, shown once. |
GET | /api/apps | List apps with live status. Keys are never returned. |
GET | /api/apps/:id | App detail and live status. |
PATCH | /api/apps/:id | Update { "name", "webhookUrl", "rateLimitPerMin" }. |
DELETE | /api/apps/:id | Stop the client and delete the app, session, key and message log. |
POST | /api/apps/:id/restart | Restart the WhatsApp client. |
POST | /api/apps/:id/unlink | Log out the number and show a fresh QR. Keeps the app and key. |
GET / POST | /api/apps/:id/api-key | View the app key / issue a new one (the old key stops working). |
POST | /api/apps/:id/webhook-secret | Rotate the webhook secret. Returned only here. |
Create an app
bash
curl -X POST https://gateway.example.com/api/apps \
-H "x-admin-key: $ADMIN_API_KEY" -H "Content-Type: application/json" \
-d '{"name":"Support bot","webhookUrl":"https://example.com/hook"}'json
{
"id": "app_9f2c...",
"name": "Support bot",
"apiKey": "wa_...",
"apiKeyHint": "wa_9f2...c41d",
"webhookUrl": "https://example.com/hook",
"createdAt": "2026-10-01T01:50:00.000Z"
}Dashboard helpers
These act on an app without its key. Use the Tenant API from your own code.
| Method | Path |
|---|---|
GET | /api/apps/:id/qr |
POST / DELETE | /api/apps/:id/pairing-code |
POST / GET | /api/apps/:id/messages |
Webhook queue
| Method | Path | Description |
|---|---|---|
GET | /api/apps/:id/webhooks?limit=50 | Recent events and delivery stats. |
POST | /api/apps/:id/webhooks/retry | Requeue failed and skipped events. Returns { "requeued": n }. |
POST | /api/apps/:id/webhooks/test | Queue a signed webhook.test. 409 NO_WEBHOOK if no URL is set. |
Sessions
| Method | Path | Description |
|---|---|---|
GET | /api/sessions | Every stored session as Running, Idle, Inactive, Unreadable or Orphan. |
POST | /api/sessions/cleanup | Delete inactive sessions. Body { "dryRun": true, "days": 30 } previews first. |
DELETE | /api/apps/:id/session | Delete one app's stored session. Refused while it is running. |
Provisioning keys
| Method | Path | Description |
|---|---|---|
POST | /api/provisioners | Create a key. Body { "name", "maxApps"? }. The key is shown once. |
GET | /api/provisioners | List keys. |
PATCH | /api/provisioners/:provisionerId | Update a key. |
DELETE | /api/provisioners/:provisionerId | Revoke. Its apps keep running. |
The same can be done from a shell: node scripts/create-provisioner.js "Tally" 200.
Dashboard sign-in
GET /api/auth/config, POST /api/auth/request-code, POST /api/auth/verify, GET /api/auth/me and POST /api/auth/logout implement email and one-time-code sign-in. Codes last 5 minutes, work once and allow 5 attempts. Resends are limited to one a minute and five an hour per address. Sessions last 7 days in an HttpOnly cookie.