Skip to main content
Scopes constrain what an OAuth access token can do. A client can only request scopes inside its registered allowed_scopes allowlist, and the user granting consent can further narrow the set on the consent screen. Applies to OAuth only. API keys don’t use scopes — they’re bound to the tenant with all-or-nothing access inside their allowed_scopes.

OIDC core

ScopePurpose
openidRequired for OIDC. Returns an id_token.
profileAdds name and tenant_name claims to /userinfo.
emailAdds email and email_verified claims.
offline_accessRequired to receive a refresh_token. Without it, tokens cannot be refreshed.

Resource scopes

ScopeUnlocks
campaigns:readGET /api/v1/campaigns, GET /api/v1/campaigns/{id}
campaigns:writePOST, PATCH, DELETE /api/v1/campaigns/*
leads:readGET /api/v1/campaigns/{id}/leads, list + get
leads:writePOST /api/v1/campaigns/{id}/leads, :bulk
calls:readGET /api/v1/calls, GET /api/v1/calls/{id}
calls:writeFuture — agent triggered hangup etc. Reserved.
agents:readGET /api/v1/agents

Composition

Scopes are space-separated in the token request:
scope=campaigns:read leads:write openid offline_access
The issued access token carries them in its scope claim. Server-side gate check is exact-match per route — requesting only campaigns:read and trying to POST /api/v1/campaigns returns a 403 { "detail": "missing scope campaigns:write" }.

Principle of least privilege

Partners are expected to request only the scopes they need. AppExchange and similar review processes will flag a listing that requests campaigns:write but never actually writes campaigns. Narrow the set to what your integration actually uses.

Future scopes

We version by adding new scopes (never changing the semantics of existing ones). When a new resource ships, its scopes appear here within the same release. Subscribe to the changelog to track additions.