Scopes
A key gets exactly the scopes you grant. Each endpoint requires one.
| Scope | Allows |
|---|---|
messages:send | POST /v1/messages |
messages:read | GET /v1/messages, GET /v1/messages/{id} |
templates:read | GET /v1/templates |
templates:write | POST /v1/templates, POST /v1/templates/{id}/submit |
numbers:read | GET /v1/numbers |
campaigns:read | GET /v1/campaigns, GET /v1/campaigns/{id} |
contacts:read | GET /v1/contacts, GET /v1/contacts/tags |
Defaults when you create a key without choosing: messages:send, messages:read, templates:read.
What keys cannot do
API keys cannot reach management endpoints — /v1/keys, /v1/logs, /v1/segments, auth, number
onboarding or campaign creation. Those require a dashboard session. This is deliberate: a leaked
integration key can send messages, not reconfigure your workspace.
Combining restrictions
Scopes, number binding and IP allowlists combine with AND:
request allowed = token valid
AND scope granted for the endpoint
AND (no number binding OR the number is bound)
AND (no IP allowlist OR the address matches)
Checking from your side
A cheap way to verify a new key is scoped correctly:
# should be 200
curl -o /dev/null -w '%{http_code}\n' https://api.msgpanda.com/v1/templates \
-H "Authorization: Bearer $API_KEY"
# should be 403 scope_not_permitted for a send-only key
curl -o /dev/null -w '%{http_code}\n' https://api.msgpanda.com/v1/numbers \
-H "Authorization: Bearer $API_KEY"