API Keys
All 12 permission scopes, session scoping, creating and rotating keys, header format, and rate limits.
API Keys
API keys are the primary authentication mechanism for your applications talking to the WaSphere Dashboard API. Every request your code makes must include an API key in the X-API-Key header.
X-API-Key: wsp_live_abc123...
API keys are distinct from the admin JWT used to log in to the dashboard. Keys are meant for programmatic access — server-to-server or automation scripts — not for browser sessions.
Key Format
WaSphere API keys use a prefixed format for easy identification:
wsp_live_<32 random characters>
The wsp_live_ prefix makes it easy to identify WaSphere keys in logs, configuration files, and secret scanners. If you're using a secret scanning tool (GitHub secret scanning, truffleHog, etc.), configure it to flag strings matching wsp_live_[a-zA-Z0-9]{32,}.
Creating an API Key
Via the dashboard:
- Navigate to API Keys in the sidebar
- Click New Key
- Enter a descriptive name (e.g.
whmcs-integrationormobile-app) - Select the permissions this key needs (see table below)
- Optionally restrict to a specific session
- Click Create
Copy the key immediately — it is only shown once. WaSphere stores a hashed version and cannot reveal the original value.
Via the API (requires a key with keys:write permission or admin JWT):
curl -X POST https://wa.yourdomain.com/api/keys \
-H "X-API-Key: YOUR_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "whmcs-integration",
"permissions": ["messages:send", "sessions:read"],
"sessionId": "sess_abc123"
}'
Response:
{
"id": "key_abc123",
"name": "whmcs-integration",
"key": "wsp_live_xK9mN2pQ7rT4vW6yZ1aB3cD5eF8gH0jL",
"permissions": ["messages:send", "sessions:read"],
"sessionId": "sess_abc123",
"createdAt": "2026-05-25T10:00:00.000Z"
}
The key field is only present in the creation response. Store it in your application's secrets manager (AWS Secrets Manager, HashiCorp Vault, environment variable, etc.) immediately. It cannot be retrieved again.
Permissions
WaSphere uses 12 granular permission scopes. Assign only the permissions each key actually needs — following the principle of least privilege.
| Permission | Description |
|---|---|
sessions:read | List all sessions and get individual session status, phone number, connection timestamps, and anti-ban settings. |
sessions:write | Create new sessions, update session settings (name, anti-ban config), initiate reconnects, and delete sessions. |
messages:send | Send any of the 14 supported message types (text, image, video, audio, document, sticker, location, contact, poll, reaction, list, buttons, template, link preview). |
messages:read | Read historical message logs for sessions this key can access. |
webhooks:read | List registered webhooks and view delivery history. |
webhooks:write | Create, update, delete webhook endpoints and trigger test deliveries. |
contacts:read | Fetch WhatsApp contact information and check if numbers are registered on WhatsApp. |
contacts:write | Update contact display names and manage block lists. |
groups:read | Fetch group metadata — name, participants, admins, description. |
groups:write | Create groups, add/remove participants, update group settings. |
media:upload | Upload media files (images, videos, documents, audio) to prepare them for sending. Required when sending media from local files rather than URLs. |
keys:read | List all API keys and their metadata (not the key values). Intended for admin tooling. |
There is no keys:write permission available to API keys themselves — key management requires either the admin JWT or manual action in the dashboard. This prevents a compromised key from escalating its own privileges.
Typical Permission Sets by Integration Type
| Integration | Recommended Permissions |
|---|---|
| Send-only notifications (e.g. WHMCS, CRM) | messages:send, sessions:read |
| Two-way chat bot | messages:send, messages:read, sessions:read |
| Full integration with media | messages:send, messages:read, sessions:read, media:upload |
| Admin dashboard / monitoring | All permissions |
| Contact verification | contacts:read, sessions:read |
| Group management | groups:read, groups:write, sessions:read |
Session Scoping
By default, an API key can interact with all sessions that its permissions allow. For stronger isolation, restrict a key to a single session.
A session-scoped key:
- Can only read/write data for the specified session
- Returns
403 Forbiddenfor any request involving a different session - Is ideal for multi-tenant setups where each customer has their own WhatsApp number
To scope a key at creation time:
{
"name": "customer-a-notifications",
"permissions": ["messages:send", "sessions:read"],
"sessionId": "sess_abc123"
}
To update scoping on an existing key (via the dashboard only — changing sessionId via API is not supported to prevent privilege escalation).
Using a Key in Requests
Include the key in the X-API-Key header on every request:
curl https://wa.yourdomain.com/api/sessions \
-H "X-API-Key: wsp_live_xK9mN2pQ7rT4vW6yZ1aB3cD5eF8gH0jL"const BASE_URL = 'https://wa.yourdomain.com/api';
const API_KEY = process.env.WASPHERE_API_KEY;
async function waRequest(path, options = {}) {
const response = await fetch(`${BASE_URL}${path}`, {
...options,
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
const error = await response.json();
throw new Error(`WaSphere API error: ${error.message} (${response.status})`);
}
return response.json();
}
// Usage
const sessions = await waRequest('/sessions');import os
import requests
WASPHERE_BASE_URL = 'https://wa.yourdomain.com/api'
WASPHERE_API_KEY = os.environ['WASPHERE_API_KEY']
session = requests.Session()
session.headers.update({
'X-API-Key': WASPHERE_API_KEY,
'Content-Type': 'application/json',
})
def wa_request(method, path, **kwargs):
response = session.request(method, f'{WASPHERE_BASE_URL}{path}', **kwargs)
response.raise_for_status()
return response.json()
# Usage
sessions = wa_request('GET', '/sessions')<?php
class WaSphereClient
{
private string $baseUrl;
private string $apiKey;
public function __construct(string $baseUrl, string $apiKey)
{
$this->baseUrl = rtrim($baseUrl, '/');
$this->apiKey = $apiKey;
}
public function request(string $method, string $path, array $body = []): array
{
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $this->baseUrl . $path,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $this->apiKey,
'Content-Type: application/json',
],
]);
if (!empty($body)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$response = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($statusCode >= 400) {
$error = json_decode($response, true);
throw new RuntimeException("WaSphere API error: " . ($error['message'] ?? 'Unknown'));
}
return json_decode($response, true);
}
}
$client = new WaSphereClient('https://wa.yourdomain.com/api', getenv('WASPHERE_API_KEY'));
$sessions = $client->request('GET', '/sessions');Rate Limiting
API requests are rate-limited per key. The defaults are:
| Setting | Default |
|---|---|
| Window | 60 seconds |
| Max requests per window | 100 |
When you exceed the limit, the API returns 429 Too Many Requests:
{
"statusCode": 429,
"message": "Rate limit exceeded",
"retryAfter": 23
}
The retryAfter field tells you how many seconds to wait before retrying.
Rate limit headers are included on every response:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1748170460
Rate limits apply to the API key, not the IP address. If you have multiple applications sharing one key, their combined requests count toward the same limit. Issue separate keys for each integration.
Rotating a Key
Key rotation invalidates the old key and issues a new one atomically — there is no gap where neither key works.
Via the dashboard:
- Go to API Keys
- Find the key to rotate
- Click Rotate
- Copy the new key value immediately
Via API:
curl -X POST https://wa.yourdomain.com/api/keys/KEY_ID/rotate \
-H "X-API-Key: YOUR_ADMIN_KEY"
Response:
{
"id": "key_abc123",
"name": "whmcs-integration",
"key": "wsp_live_newKeyValueHere...",
"rotatedAt": "2026-05-25T10:00:00.000Z"
}
Update WASPHERE_API_KEY in your application's environment and redeploy. The old key begins returning 401 Unauthorized immediately after rotation.
Deleting a Key
curl -X DELETE https://wa.yourdomain.com/api/keys/KEY_ID \
-H "X-API-Key: YOUR_ADMIN_KEY"
Deletion is immediate and irreversible. Any application still using the deleted key will receive 401 Unauthorized.
Listing Keys
curl https://wa.yourdomain.com/api/keys \
-H "X-API-Key: YOUR_ADMIN_KEY"
The response includes all key metadata except the key values:
{
"keys": [
{
"id": "key_abc123",
"name": "whmcs-integration",
"permissions": ["messages:send", "sessions:read"],
"sessionId": "sess_abc123",
"lastUsedAt": "2026-05-25T09:55:00.000Z",
"createdAt": "2026-05-20T08:00:00.000Z"
}
],
"total": 1
}
Error Responses
| Status | Error | Meaning |
|---|---|---|
401 | Missing API key | X-API-Key header not present |
401 | Invalid API key | Key not found or deleted |
403 | Insufficient permissions | Key exists but lacks required permission |
403 | Session not in scope | Key is scoped to a different session |
429 | Rate limit exceeded | Too many requests in the current window |