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
fax:readSent and received faxes — the list, each fax's delivery status, and the fax documents themselvesNo
fax:sendSend faxes from the account's fax numbersYes
calls:makePlace click-to-call calls — ring a phone, then connect it to a numberYes
flows:callPlace an outbound call into one of the account's Flows — the flow call trigger, outside the REST API. API keys only.Yes

The live list is scopes_supported in the discovery document. It lists only scopes OAuth can grant, so flows:call (key-only, below) is not in it.

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.

Why faxing is two scopes​

For the same reasons, split the same way. fax:read is the contents of communications — a fax document is as sensitive as a message thread or a recording — and is its own consent. fax:send spends money and reaches someone outside the account, so an archiving integration that only reads fax history must not be able to send one. See Faxes.

Why placing a call is its own scope​

calls:make spends money and reaches someone outside the account, like sms:send and fax:send. An integration that reads call history (reports:read) must not also be able to dial. A key with calls:make must also list the phones it may ring, or it cannot place calls at all. See Click to call.

:::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 numbers — from 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
GET /faxes, GET /faxes/{id}fax:readFax History not Block, and Phone numbers — only faxes on their numbers
GET /faxes/{id}/filefax:readFax History → Allow with View Fax — without it, 403 even with the scope
POST /faxesfax:sendTeleConsole → Fax, and Phone numbers — from must be one of theirs
POST /callscalls:makeTheir own phones only; Phone numbers — a callerId number must be one of theirs

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.

:::note The fax and call endpoints already apply a role to keys On /faxes, a key is held to the role of the user it runs as (set on the key in the admin center), using the permissions in the table above. A key whose user has Fax History set to Block gets 403 permission_denied, whatever its scopes. See Faxes.

On POST /calls, a callerId number must be among that user's role numbers. The phone to ring is not taken from the role: a key may ring only the phones selected on it, and a key with no phones cannot place calls. See Click to call. :::

Scopes a key can hold but OAuth cannot​

flows:call — ringing a number and running one of the account's Flows when it answers — is key-only and can never be granted through the user-consent flow: it places billable calls that reach people outside the account, so no delegated third-party app may ask for it. Your own systems use it through a key you create.

It is the only scope for POST /api/v1/flows/{flowId}/calls (see Calling into a Flow), and that endpoint accepts nothing else — no REST endpoint accepts flows:call, and no other scope can place a flow call. Two things narrow it:

  • Flow grants. A key with flow resource grants may call only those flows. Without them it may call every flow on the account.
  • Allowed IPs, as for any key.

The key's run-as user's role is not consulted on this endpoint — not even the Flows permission. Narrow the key itself.