Access Models
Roam supports two access models for API integrations: Organization access and Personal access. These determine what data your integration can see, how messages are sent, and which endpoints are available.
Comparison
| Organization Access | Personal Access | |
|---|---|---|
| Who it's for | Admins building org-wide bots and automations | Individual users connecting AI tools or personal automations |
| Principal | App (bot persona) | User's personal bot |
| Data scope | Chats the app is a member of. chat.post and chat.startStream can target a public group without joining. chat.search also includes unjoined public groups in the bot's roam. History, react, and ephemeral require membership. Meetings are roam-wide only with admin:meetings:read | Chats the owner can read (including public groups in their roam). Post, react, and streams require the owner is a member. |
| Message identity | App name and icon | Personal bot name and icon |
| Auth methods | API Key or OAuth (admin consent) | OAuth or Personal Access Token (user consent) |
| Webhook scope | Chat: groups and DMs the app is a member of. Meetings: same width as REST | chat.message in chats the owner is in ("filter": { "mention": true } for @-only) |
| Token type | Organization token | Personal token |
Organization access and Personal access are not a superset/subset relationship. They provide different views of data and different identity semantics.
Chat membership — when each token can read, post, react, and receive
chat.message — is the
Chat guide, including the Slack bot-token vs user-token
mapping.
When to Use Organization Access
Organization access is for integrations that operate at the workspace level, independent of any single user. The integration acts as its own entity with a bot persona.
Common use cases:
- Notification bots -- Post alerts from CI/CD, monitoring, or ticketing systems into Roam groups
- Sales automation -- Trigger messages based on CRM events or deal updates
- Compliance and archival -- Export message archives for legal holds or data warehousing
- User provisioning -- Sync users from Okta, Azure AD, or other identity providers via SCIM
- Post-meeting automation -- Process every recording and transcript in the Roam (org
API key with
admin:meetings:read)
Setup: An admin creates an API client in Roam Administration > Developer and generates an API Key or OAuth credentials. The integration authenticates with an organization token.
When to Use Personal Access
Personal access is for integrations that act on behalf of a specific user. The integration sees what the user sees and sends messages through that user's personal bot — a per-user persona attributed to the user (e.g. "Alex's Notetaker"). Mentioning the personal bot routes to the integration; messages it posts are clearly identified as bot output rather than impersonating the user.
Common use cases:
- AI assistants -- Connect Claude, ChatGPT, or other AI tools to search your meetings and messages via MCP
- Personal productivity -- Receive your meeting transcripts and action items for personal follow-up
- Message management -- Search and manage your Roam messages through AI assistants
- Personal automations -- Build workflows triggered by your own activity
Setup: The user authorizes the app via OAuth consent or creates a Personal Access Token. The integration receives a personal token scoped to that user's data.
Admin Policy for Personal Access
Workspace admins control whether Personal access is available. In Roam Administration > Developer, admins can set the policy to one of:
- Disabled -- Personal access is not available. Users cannot authorize apps or create Personal Access Tokens.
- With Approval -- Users can request Personal access, but an admin must approve each request before a token is issued.
- Auto-approve -- Users can authorize apps and create Personal Access Tokens without admin approval.
OAuth App Design
A single OAuth app can support both Organization access and Personal access. The access model is selected at authorization time, not when the app is created.
- One app, two modes -- Your app registration can declare which access models it supports. The mode is chosen per authorization grant.
- Organization access requires admin consent -- only workspace admins can authorize org-wide access.
- Personal access requires user consent -- any user can authorize access to their own data (subject to admin policy).
Endpoint Compatibility
Not every endpoint supports both access models. Most endpoints support Organization access only. The following endpoints additionally support Personal access:
| Endpoint | Notes |
|---|---|
/token.info | Introspect the current credential |
/token.revoke | OAuth only (not API keys). Personal may self-revoke its own grant |
/chat.post | Organization: app bot. Personal: user's personal bot |
/chat.postEphemeral | Personal rejects sender |
/chat.list / /chat.history / /chat.search / /chat.link.resolve / /chat.link.create | Personal: chats the user can access |
/chat.update / /chat.delete / /chat.typing / streams | Same write rules as org, scoped to accessible chats |
/chat.scheduled.list / /chat.scheduled.cancel | Messages scheduled by this credential |
/reaction.add / .remove / .list | Personal: chats the user can access |
/user.info / /user.list | Directory / principal resolution |
/user.activity.set / .clear / .list | Personal: the token owner only. Organization: any user in the workspace |
/group.list / /group.info / /group.create / /group.join | Personal: groups you belong to / can manage. group.join adds the owner, never the PAT bot |
/guest.badge.create / .list / .update / .revoke | Personal: only as the token owner (host). Organization: any host via hostUserId |
/asset.create | purpose: "story" is Personal only. file and avatar are Organization and Personal |
/story.post | Personal only |
/lobby.list / /lobby.booking.list | Organization and Personal |
/meeting.link.create / .info / .update | Personal: host must be the authenticated user for info/update |
/meeting.list / .info / .participants / .transcript / .prompt / .shareLink | Personal: meetings you participated in. Org: roam-wide only with admin:meetings:read; otherwise the bot's own access set |
/meeting.search | Personal only. Organization tokens are rejected |
/conversation.list / /calendar.event.create | Personal-filtered where noted on the page |
/calendar.list | Personal only. Organization tokens receive 400 access_mode_not_supported |
/webhook.list / .subscribe / .unsubscribe / .deliveries | Personal: your dynamic subscriptions |
/onair.* (event, guest, host, attendance) | Personal where allowlisted; creator rules on write |
/magicast.list / /magicast.info / /magicast.shareLink | Organization: every Magicast in the account, including never-shared ones. Personal: only Magicasts you own |
Not Personal on v1 (examples): org-only admin surfaces, and anything absent from
the server allowlist — including some siblings of allowlisted routes (e.g.
/group.members may still be org-only while /group.list is allowlisted).
Check each page's Access line.
v0 only (legacy Personal): /v0/transcript.*, /v0/chat.*, /v0/item.upload,
/v0/user.list, /v0/meetinglink.create, etc. Prefer the v1 meeting/chat
surfaces above.
This table is a summary — the server PersonalAccessAllowlist is authoritative.
Endpoints not on the allowlist return 403 for personal tokens even when scopes
would otherwise permit the call.
Each endpoint's documentation indicates which access models it supports.
Identity lookup follows the same principal policy in both access modes. A known user UUID can
resolve as a member or guest with user:read; email is included only with user:read.email.
Automated actors remain constrained by their Roam, account, or owning principal. See
Identity & Principals.
Webhooks
Most activity events can be subscribed in both Organization and Personal access modes. Delivery scope differs (full chat matrix: Chat):
- Organization:
chat.messageandchat.reactionfor chats the app is a member of. @-mention does not join a group; the Roam client may prompt Invite for a non-member org app in a public group. Usegroup.joinorgroup.add. Meeting, recording, and transcript events follow meeting width — roam-wide only withadmin:meetings:read. - Personal:
chat.messagefor every message in chats the owner is in (groups and DMs), plus the self-DM with the personal bot. Subscribe with"filter": { "mention": true }for @-mentions only. Reactions use the same owner-in-chat gate with no mention filter.
Lifecycle events (token.revoked,
app.uninstalled) are delivered to the app's
subscriptions when that grant or install ends — see those event pages for
scopes (webhook:write).
Handling revocation and uninstall
Apps learn that access ended via two lifecycle webhook events (subscribe with webhook:write):
| Event | When | What to do |
|---|---|---|
token.revoked | A specific OAuth grant for your app died (user revoke, admin action, your token.revoke call, or SCIM deactivation) | Discard that grant's access + refresh tokens; stop retrying with them |
app.uninstalled | Your install was removed from a Roam (admin revoke-installed-app, client archive) | Discard all tokens for the install; tear down local state |
Delivery is signed with your app's webhook secret — not the revoked bearer token — so you still receive the last-gasp event after the grant is gone. You may also receive token.revoked for a token you already discarded (for example after calling token.revoke yourself); treat handlers as idempotent.
token.revoke kills only the presented grant (access and refresh). It does not uninstall the app or delete webhook subscriptions. The historical v0 /app.uninstall route is the same operation.
Error Handling
If you call an endpoint that doesn't support your token's access model, the API
usually returns HTTP 403 with:
{
"error": "This token's access mode cannot use this endpoint.",
"code": "access_mode_not_supported"
}
(On /v1/ paths the body is {"ok": false, "error": "access_mode_not_supported"}.)
calendar.list is the exception: organization tokens
get the same access_mode_not_supported code as HTTP 400.
Check the compatibility table above or each endpoint's documentation to confirm support before making requests. See the Error Codes guide for the full catalog.