Scopes
Four scopes, resolved live on every request. Which endpoints need which, and why marking read is a send.
Scopes are per credential — per API key, or per OAuth grant. There are four.
| Scope | Grants |
|---|---|
whatsapp.read | GET /connections, GET /connections/{id}, GET /account, GET /messages |
whatsapp.send | POST /messages, POST /messages/read |
whatsapp.events | GET /webhook, PUT /webhook, POST /webhook/rotate, and receiving deliveries at all |
whatsapp.manage | POST /connections, POST /connections/{id}/pairing-code, POST /connections/{id}/restart, POST /connections/{id}/logout, DELETE /connections/{id} |
Marking read is a send, not a read
POST /messages/read needs whatsapp.send. It looks like a read operation, but
it transmits a receipt from your number to the customer's device — it is
outbound. The upside is that any client which can already send can also mark
read: no new scope, no re-consent.
Resolution is live
Scopes and the reachable connection list are resolved on every request. Nothing is cached in the token and nothing has to be refreshed. Two consequences:
- Revoking a key, or narrowing its scopes, takes effect on the very next call.
- A connection you just created is visible on the next call. There is no propagation delay and nothing to re-issue.
Insufficient scope
A credential that lacks the scope an endpoint needs gets
403 insufficient_scope. Branch on the code, not the status — 403 is also
what an IP-allowlist refusal returns (ip_not_allowed) and the two need
opposite fixes: one means edit the key's scopes, the other means add an address.
See Errors & idempotency.
Ask for less
The scope you do not grant is the one that cannot be abused if the key leaks. A
read-only reporting job wants whatsapp.read and nothing else; a notification
sender wants whatsapp.send; only a provisioning flow needs
whatsapp.manage, which can delete connections.