Home/ Developers/Authentication
API keys, scopes, and X-Tenant-ID
Every request authenticates as one scoped API key, bound to one workspace, able to do exactly what its scopes say and nothing else.
Authenticate with Authorization: ApiKey smk_<prefix>_<secret>
and X-Tenant-ID: <your workspace UUID> on every request.
A key only works for the workspace it was created in, and only for the scopes it was
granted — an unscoped or wrong-workspace request is refused before it touches any data.
What every request needs
Authorization: ApiKey smk_a1b2c3d4e5f6g7h8_kJ8x...your-secret X-Tenant-ID: 3f9c7e2a-1b4d-4e8a-9c3f-2a1b4d4e8a9c Content-Type: application/json
Authorization
ApiKey, a space, then the full secret exactly as shown once at creation:
smk_ + a public prefix + an underscore + the private secret. BotPulsar
stores only a salted digest of the secret half — losing it means revoking the key and
creating a new one, not resetting it.
X-Tenant-ID
The workspace UUID the key was created in. Every key is bound to exactly one workspace
at creation; sending a different workspace's ID here fails authentication
(401) before scopes are even checked — it's a credential mismatch, not a
permissions gap.
From the dashboard
-
Operations → Developer → Create API key
Owner, admin, or manager role required. Give it a name and the scopes it needs.
-
Copy the secret immediately
It's shown once, in the creation response. Store it in a secret manager, not a chat message or a spreadsheet.
-
Revoke it the moment it's no longer needed
A revoked key fails every request with
401immediately — there's no grace period.
Every scope an API key can hold
Request only what the integration uses. Live scopes are wired to real endpoints today; Planned ones can be granted to a key and validate correctly, but no endpoint checks for them yet.
| Scope | Grants | Status |
|---|---|---|
contacts:read |
List and look up contacts. | Live |
contacts:write |
Create contacts and update existing ones. | Live |
conversations:read |
List and look up conversations. | Live |
conversations:write |
Start a conversation with a contact. | Live |
messages:read |
Read a conversation's message history. | Live |
messages:write |
Send a message into a conversation. | Live |
templates:read |
List approved WhatsApp message templates and their parameters. | Live |
templates:write |
Create or edit message templates. | Planned |
webhooks:read |
List webhook subscriptions and delivery history. | Live |
webhooks:write |
Create, update, and rotate the secret on webhook subscriptions. | Live |
automations:read |
List automations. | Planned |
automations:write |
Trigger an automation via its API trigger endpoint. | Live |
campaigns:read |
List campaigns and their delivery stats. | Planned |
campaigns:write |
Create and launch campaigns. | Planned |
media:read |
Download media attached to a message. | Planned |
media:write |
Upload media to attach to an outbound message. | Planned |
analytics:read |
Read workspace analytics and reports. | Planned |
Common questions
What is X-Tenant-ID and why is it required?
BotPulsar is multi-tenant: one API key belongs to exactly one workspace, and every request must name that workspace explicitly in `X-Tenant-ID`. A key whose workspace doesn't match the header is refused before any scope is even checked, so a copy-paste mistake fails loudly instead of touching the wrong workspace.
How many scopes should I request?
Only the ones the integration uses. A key that only reads contacts and sends messages should hold exactly `contacts:read messages:write conversations:read conversations:write` — not the full list. Scopes can be widened later by rotating the key; a leaked minimally-scoped key does far less damage than a leaked all-scopes one.
Can I see a key's secret again after creating it?
No. The full secret (`smk_<prefix>_<secret>`) is returned exactly once, at creation. BotPulsar stores only a salted digest of it. If it's lost, revoke that key and create a new one — there is no recovery path, by design.
How do I rotate a key without downtime?
Create a second key with the same scopes, switch your integration over to it, then revoke the first one. Because keys are independent credentials rather than versions of one secret, both work at once during the switch.