Scopes
Scopes are the unit of authorization on Roam API tokens (API keys, OAuth grants,
and Personal Access Tokens). Request them at OAuth consent (space-separated
scope query parameter) or when creating a PAT / API key in Developer settings.
Canonical names and descriptions are defined in the server scope registry
(common/apiScopes.json). Endpoint pages restate the Required scope for
each operation; if a page and this catalog disagree, trust the running API and
file a docs bug.
Requesting scopes
OAuth authorize URL — space-separated list:
scope=chat:history%20chat:send_message%20user:read
PAT creation UI — choose PAT groups (below). Groups expand to concrete scopes stored on the token.
Aliases: the only documented alias today is groups:read → canonical
group:read. Comparisons always use the canonical form. Some write endpoints
also accept the legacy alternate chat:write as a synonym for
chat:send_message at the require-scope check (it is not a separately
grantable scope in the registry).
OAuth / API key scopes
| Scope | Description |
|---|---|
addr:info | Read information about a chat participant (historical spelling; public resolution is user.info / group.info) |
admin:compliance:read | Read certain compliance data exports |
admin:meetings:read | Org only. View all meetings, recordings, and transcripts in this Roam (see Meeting width) |
calendar:read | Read events from a user's connected calendars (calendar.list) |
calendar:write | Create and update calendar events |
chat:history | Read messages from public chats, or chats the app has been added to |
chat:join | Join public chats |
chat:read | List public chats, or ones the bot has been added to |
chat:send_message | Send chat messages (also accepted as chat:write on write endpoints) |
commands | Slash commands and message actions (interactivity; limited public surface today) |
group:read | View public groups (groups:read is a legacy alias) |
group:write | Create and manage groups; group.join (self-join a public group) |
item:read | View item / attachment details |
item:write | Create items / uploads |
links:read | Receive links shared in chats the app can see (chat.link.shared) |
links:write | Attach rich previews via chat.unfurl |
lobby:read | View lobbies and lobby bookings |
magicast:read | View Magicasts |
meetinglink:read | View meeting links |
meetinglink:write | Create / update meeting links |
meetings:read | View meetings the app has access to (see Meeting width; calendar:read for calendars) |
onair:read | View On-Air events, guests, and hosts |
onair:write | Create and manage On-Air events, guests, and hosts |
recordings:read | View meeting recordings the app has access to (legacy recording.list surface) |
transcript:read | View meeting transcripts the app has access to (prefer meetings:read on v1) |
user:read | List / resolve users |
user:read.email | Include user email addresses |
user:read.status | View member check-in status and will-return times; required for user.status.update webhooks |
user:write | Create and update users |
userauditlog:read | Read user audit logs |
webhook:read | List webhook subscriptions and failed deliveries |
webhook:write | Subscribe / unsubscribe webhooks; lifecycle events |
Hidden / legacy registry entries (create_tags, meeting:write, hidden
groups:read) may appear in older grants; prefer the canonical names above.
calendar:read and meetings:read
Reading a user's connected calendars (calendar.list,
and the calendar_list MCP tool) requires calendar:read. That access used
to fall under meetings:read.
| Client registered | Scope that grants calendar access |
|---|---|
| Before 2026-07-29T00:00Z | meetings:read (grandfathered) or calendar:read |
| On or after 2026-07-29T00:00Z | calendar:read only |
A client registered on or after the cutoff that holds only meetings:read gets
403 / missing_scope naming calendar:read when it calls calendar.list.
Existing grants keep working; new authorizations should request calendar:read.
Requesting both is harmless if one codebase serves grants from either side of
the cutoff.
This affects calendar access only — meetings:read is unchanged for meetings,
transcripts, participants, search, and prompt.
Personal Access Tokens created with the
pat:meetings:read group are unaffected on
either side of the cutoff: the group expands to include calendar:read.
Meeting width (admin:meetings:read)
meetings:read / recordings:read / transcript:read are the capability to
call those APIs and subscribe to those events. They do not mean every
meeting in the Roam. Width is:
| Credential | Meetings, recordings, transcripts, and those webhooks |
|---|---|
| Personal | Meetings the token owner participated in. Personal tokens never get roam-wide access — admin:meetings:read is stripped on authorize and is not in any PAT group. |
Org without admin:meetings:read | Meetings the install's bot already has access to. This set is usually empty unless the bot was a participant on the saved meeting. |
Org with admin:meetings:read | Every meeting, recording, and transcript in the installed Roam. |
API keys default admin:meetings:read on in Developer Settings when you
enable a meeting scope (the Sales Ops / CRM path: an admin-issued key should
see every recording). OAuth apps default it off (the app developer is not
the installing account). Adding the scope to an OAuth app does not widen
existing tokens until they re-authorize.
Existing API keys that already had roam-wide meeting access keep it without the new scope. There is no Meeting Access toggle — that control is gone.
/v0/meeting.list, /v1/conversation.list, and /v1/recording.list cannot
filter by bot access today. Org clients without roam-wide meeting access get
403; use /meeting.list instead.
This is the same split chat already has: ordinary scopes for “what you can
see,” admin:compliance:read for company-wide export. All-recordings is the
original public API, not a compliance product.
Vocab notes (honest)
addr:infois a historical spelling (notaddress:info). Public principal and group resolution isuser.info/group.info.meetings:read/recordings:readuse plural nouns; most other scopes are singular (group:read,user:read).chat:writeis accepted by many write handlers alongsidechat:send_messagebut is not a first-class grantable name in the scope picker registry.
Webhook event → required scope
Subscribe requires the listed scope (and delivery enforces it). Enrichment
scopes (e.g. user:read.email) are additional when noted on the event page.
| Event | Required scope |
|---|---|
chat.message | chat:history |
chat.reaction | chat:history |
chat.link.shared | links:read |
meeting.started | meetings:read (width: Meeting width) |
meeting.ended | meetings:read (width: Meeting width) |
user.status.update | user:read.status (not user:read alone) |
lobby.booked | lobby:read |
magicast.created | magicast:read |
onair.event.created | onair:read |
onair.event.updated | onair:read |
onair.event.canceled | onair:read |
onair.guest.added | onair:read |
onair.guest.rsvp | onair:read |
token.revoked | webhook:write |
app.uninstalled | webhook:write |
Personal Access Token groups
PAT creation offers grouped scopes that expand server-side:
| PAT group | Expands to (canonical) |
|---|---|
pat:chat:read | chat:read, chat:history, item:read, addr:info, user:read, user:read.email, group:read, webhook:read, webhook:write |
pat:chat:write | chat:send_message, item:write, user:read, user:read.email, group:read, webhook:read, webhook:write |
pat:meetings:read | meetings:read, calendar:read, recordings:read, transcript:read, meetinglink:read, lobby:read, user:read, user:read.email, webhook:read, webhook:write |
pat:magicast:read | magicast:read, user:read, user:read.email, webhook:read, webhook:write |
pat:calendar:write | calendar:write, user:read, user:read.email, webhook:read, webhook:write |
pat:groups:write | group:write, group:read, user:read, user:read.email, webhook:read, webhook:write |
token.info reports the expanded OAuth scopes on the credential, not the
pat:* group names.
pat:meetings:read does not include admin:meetings:read. Personal tokens
cannot hold roam-wide meeting access.
Missing scope errors
When a token lacks a required scope, the API returns HTTP 403 with
error: "missing_scope" and machine-readable needed / provided arrays.
See Responses and Errors.
Related guides
- OAuth & Authentication
- Access Models
- Migrating from v0 to v1 (scope re-consent)