WWaSphere Docs
Core Concepts

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:

  1. Navigate to API Keys in the sidebar
  2. Click New Key
  3. Enter a descriptive name (e.g. whmcs-integration or mobile-app)
  4. Select the permissions this key needs (see table below)
  5. Optionally restrict to a specific session
  6. 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.

PermissionDescription
sessions:readList all sessions and get individual session status, phone number, connection timestamps, and anti-ban settings.
sessions:writeCreate new sessions, update session settings (name, anti-ban config), initiate reconnects, and delete sessions.
messages:sendSend any of the 14 supported message types (text, image, video, audio, document, sticker, location, contact, poll, reaction, list, buttons, template, link preview).
messages:readRead historical message logs for sessions this key can access.
webhooks:readList registered webhooks and view delivery history.
webhooks:writeCreate, update, delete webhook endpoints and trigger test deliveries.
contacts:readFetch WhatsApp contact information and check if numbers are registered on WhatsApp.
contacts:writeUpdate contact display names and manage block lists.
groups:readFetch group metadata — name, participants, admins, description.
groups:writeCreate groups, add/remove participants, update group settings.
media:uploadUpload media files (images, videos, documents, audio) to prepare them for sending. Required when sending media from local files rather than URLs.
keys:readList 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

IntegrationRecommended Permissions
Send-only notifications (e.g. WHMCS, CRM)messages:send, sessions:read
Two-way chat botmessages:send, messages:read, sessions:read
Full integration with mediamessages:send, messages:read, sessions:read, media:upload
Admin dashboard / monitoringAll permissions
Contact verificationcontacts:read, sessions:read
Group managementgroups: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 Forbidden for 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:

SettingDefault
Window60 seconds
Max requests per window100

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:

  1. Go to API Keys
  2. Find the key to rotate
  3. Click Rotate
  4. 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

StatusErrorMeaning
401Missing API keyX-API-Key header not present
401Invalid API keyKey not found or deleted
403Insufficient permissionsKey exists but lacks required permission
403Session not in scopeKey is scoped to a different session
429Rate limit exceededToo many requests in the current window

On this page