Skip to content
SocialMate

SocialMate Local API

Authentication & API keys

The x-api-key header, SHA-256 hashed keys, read/send/admin scopes, and per-key rate limits.

Trust in the SocialMate API is the key — not the transport. Every route except /health and the in-app docs requires a valid x-api-key header, matched against keys stored on your machine.

Pass your key on every request:

curl http://localhost:3456/v1/accounts \
  -H "x-api-key: YOUR_KEY"
  • Missing header401 Missing x-api-key header.
  • Unknown key401 Invalid API key.

Keys are stored SHA-256 hashed — the plaintext is never written to disk and never returned again after creation. If you lose a key, rotate or recreate it.

Scopes

Each key carries one or more scopes. The server maps every route to the minimum scope it needs and rejects a key that lacks it with 403 (error.code insufficient_scope), the body naming what was required and what you have.

ScopeGrants
readYour WhatsApp data — accounts, chats, contacts, groups, messages, media, queue, antiban, status.
sendPosting a message (/messages, plus the deprecated /messages/media), enqueuing a queue item, importing a batch, and forcing a media download.
adminEverything that mutates — creating/managing groups, queue pause/resume, media cleanup — and reading or writing the server's own configuration: webhooks and API keys, GETs included.

read is deliberately “your data”, not “everything you can GET”. Webhook and key listings are admin because they expose how the server is wired — a webhook row carries its target URL, which is a capability, and the key list inventories your other credentials.

An unlisted GET defaults to read; any unlisted non-GET defaults to admin — destructive routes fail closed for read-only keys.

Managing keys over the API

Keys can be managed from the app UI or scripted via the API (the CRUD routes need the admin scope).

GET /v1/api-keys

scope: admin

List keys with their id, label, scopes, account scope, and last-used time. The key hash is never returned — only the first-10-character prefix (every key starts sm_).

Listing keys needs admin. Enumerating the credentials issued against a server is a configuration read, not a data read — a read-only key you hand to a dashboard or a contractor must not be able to inventory your other keys.

curl http://localhost:3456/v1/api-keys \
  -H "x-api-key: YOUR_KEY"
{ "data": [ { "id": "k_123", "label": "n8n", "prefix": "sm_aB3xK9p", "scopes": ["read","send"], "accountId": "a1b2c3", "accounts": ["a1b2c3"], "createdAt": 1718800000000, "lastUsedAt": 1718805000000, "revoked": false } ] }
POST /v1/api-keys

scope: admin

Create a key. Body: label (required), scopes (optional array, defaults to ["read","send"]), accounts (optional array — omit/null for all accounts, or list ids to scope the key). The plaintext is returned once; capture it now.

curl -X POST http://localhost:3456/v1/api-keys \
  -H "x-api-key: ADMIN_KEY" -H "Content-Type: application/json" \
  -d '{ "label": "n8n", "scopes": ["read","send"], "accounts": ["a1b2c3"] }'
{ "data": { "plaintext": "sm_aB3xK9p2QrT5wYz0vN8mLk…", "key": { "id": "k_123", "label": "n8n", "prefix": "sm_aB3xK9p", "scopes": ["read","send"], "accountId": "a1b2c3", "accounts": ["a1b2c3"], "createdAt": 1718800000000, "lastUsedAt": null, "revoked": false } } }
POST /v1/api-keys/{id}/rotate

scope: admin

Issue a fresh secret for an existing key (same id, same scopes). Returns the new plaintext once; the old value stops working immediately.

curl -X POST http://localhost:3456/v1/api-keys/k_123/rotate \
  -H "x-api-key: ADMIN_KEY"
{ "data": { "plaintext": "sm_cD4yL0q3RsU6xZpW1mB7nJ…", "key": { "id": "k_123", "label": "n8n", "prefix": "sm_cD4yL0q", "scopes": ["read","send"], "accountId": "a1b2c3", "accounts": ["a1b2c3"], "createdAt": 1718800000000, "lastUsedAt": 1718805000000, "revoked": false } } }
DELETE /v1/api-keys/{id}

scope: admin

Revoke and delete a key. Revocation is immediate.

curl -X DELETE http://localhost:3456/v1/api-keys/k_123 \
  -H "x-api-key: ADMIN_KEY"
{ "data": { "ok": true } }

Rate limits

Each key is independently rate-limited by a token bucket: 600 requests/minute sustained (refilling ~10/second, burst 600). Exceeding it returns 429 rate_limited with a Retry-After header and a retryAfterMs field. This is separate from anti-ban, which governs how fast messages actually go out.

Treat keys like passwords. Use one key per integration, label it clearly, and store it in your automation's secret store or environment — never in client-side code or a public repo. If one leaks, rotate just that key without disrupting the others.

Free forever · no card Download free