Skip to main content

Scopes

A scope is a capability (resource:action), not a surface. The same scope authorizes the same capability whether you arrive over REST or MCP.

Request only what you need — space-delimited in an OAuth authorize request, or selected per key in the admin center.

ScopeGrantsSpends money
sms:readTexting configuration — which numbers can text, blocked senders, saved replies, schedules. Not message content.No
sms:writeResolve and assign conversations; manage blocked senders, saved replies and drafts. Cannot send to anyone.No
sms:sendSend SMS from the account's numbers, and schedule one to go out laterYes
messages:readRead message content — the inbox, a thread's text and media, delivery receipts, unread countsNo
messages:writeMark messages read or unread; permanently delete messages or whole conversationsNo
users:readThe account's users — names, emails, extensions. Personal data.No
recordings:readCall recordings and transcripts — the contents of conversationsNo
webhooks:readList and view webhook subscriptionsNo
webhooks:writeCreate, update, delete, enable and disable subscriptionsNo
reports:readCall history — who called whom, when, how long, how it endedNo

The live list is scopes_supported in the discovery document.

Why texting is five scopes

Because they are five different decisions:

  • Building a number picker (sms:read) should not require the ability to spend money.
  • A help-desk integration that only closes threads (sms:write) should not be able to text customers.
  • A send-only integration (sms:send) should not be able to enumerate the account's number inventory.
  • Reading message content is its own consent. messages:read is separate from sms:read for the same reason recordings:read is separate from everything else: a thread is the contents of a conversation, not metadata about it. Folding it into sms:read would also have silently upgraded every token already issued for a number picker into one that can read the account's entire message history.
  • Destroying messages is separate from bookkeeping. sms:write is reversible — a thread can be reopened, an assignment changed. A delete under messages:write is permanent.

Ask for each only if you do that thing.

:::note Deleting has a second gate that scopes do not override messages:write lets you call the delete endpoints. If the account has restrict deleting SMS switched on, they still answer 403 — that setting exists so an account owner can stop their own staff and integrations from destroying message history, and no scope overrides it. :::

A scope is a ceiling, not a key

Holding a scope does not mean you get everything under it.

For an OAuth token, the portal permissions of the person who authorized your app still apply on top. A scope can only narrow what they could already do themselves — never widen it.

EndpointScope…and the authorizing user's portal permission
GET /usersusers:readUsers — you see the users they can see, no more
GET /sms/linessms:readPhone numbers — only the numbers assigned to them
POST /sms/messagessms:sendPhone numbersfrom must be one of theirs
POST /sms/conversations/…/resolve, …/assignsms:writePhone numbers — the line must be one of theirs
GET /sms/conversations, GET /sms/conversations/{line}/{number}messages:readPhone numbers — only threads on lines that are theirs
DELETE /sms/conversations/…messages:writePhone numbers, plus the account's restrict-deleting-SMS setting
POST /calls/search, GET /calls/{id}reports:readCall reports — only the calls their role exposes
GET /calls/{id}/recordings, .../transcriptsrecordings:readCall reports → allow recordings — without it, 403 even with the scope

So the same request can legitimately return different data for two tokens on the same account. If your app sees less than you expect, check the authorizing user's role in the portal before assuming a bug.

The two failures look alike and are fixed in different places:

error.typeWhat is wrongFixed by
insufficient_scopeThe credential lacks the scopeYour code — re-authorize
permission_deniedThe scope is fine; the role lacks the permissionTheir admin, in the portal

API keys are checked on scope only today — a key gets the whole account within its scopes. See What a key can reach.

Scopes a key can hold but OAuth cannot

flows:call — triggering an outbound call into a flow — is key-only and can never be granted through the user-consent flow. Internal and service capabilities stay out of reach of delegated third-party apps by design.