Bot and user scopes with least privilege
Picking the least permissive scope for each task: the granular scopes behind common methods (join, read history, post, look up emails, react, slash commands), which scopes only user tokens can hold, why scopes accumulate across reinstalls, Sign in with Slack's separate flow, and how Marketplace review judges a scope list.
Request the appropriate scopes for your app's use case.
~7 min read · 11 practice questions ·
What the exam expects
- Map a task to the one granular scope that unlocks its method: joining, reading public or private history, posting, looking up users by email, reacting, adding slash commands.
- Tell bot scopes from user scopes, and spot scopes that only a user token can hold.
- Explain why a reinstall never removes scopes and how to read a token's current scopes at runtime.
- Keep Sign in with Slack in its own OpenID flow.
- Apply Marketplace scope practice: least privilege, a usage reason per scope, nothing for future features.
Key terms: scope (a permission granted to a token at install); bot scope (held by the app's bot user, acting with its own identity); user scope (held by a user token, acting as the installer); least privilege (the smallest set of least permissive scopes that still works); x-oauth-scopes (response header listing the calling token's scopes).
Key facts
channels:join- Detail
- Bot (and legacy bot) only; unlocks
conversations.join
chat:write.public- Detail
- Post in public channels without joining; needs
chat:write
- Detail
- Only while the app is a member
- Detail
channels:historyvsgroups:history, channels the app was added to
- Detail
users:read.email, requested together withusers:read
- Detail
reactions:writeforreactions.add/reactions.remove
- Detail
chat:write.customize(withchat:write) forusername,icon_url,icon_emoji
- Detail
files:writefor both upload methods
- Detail
commands, bot token
- Detail
search:read,users.profile:write
- Detail
x-oauth-scopesheader on every Web API response
- Detail
- Impossible without revoking the token
- Detail
/openid/connect/authorize, scopesopenid,email,profile
Sources for this section (13)
- channels:joindocs.slack.dev (opens in a new tab)
- chat:write.publicdocs.slack.dev (opens in a new tab)
- chat.postMessagedocs.slack.dev (opens in a new tab)
- channels:historydocs.slack.dev (opens in a new tab)
- groups:historydocs.slack.dev (opens in a new tab)
- users:read.emaildocs.slack.dev (opens in a new tab)
- reactions:writedocs.slack.dev (opens in a new tab)
- files.getUploadURLExternaldocs.slack.dev (opens in a new tab)
- commandsdocs.slack.dev (opens in a new tab)
- search:readdocs.slack.dev (opens in a new tab)
- users.profile:writedocs.slack.dev (opens in a new tab)
- Installing with OAuthdocs.slack.dev (opens in a new tab)
- Sign in with Slackdocs.slack.dev (opens in a new tab)
- Why can't a reinstall with a shorter scope list shrink a token?
- When should an app request a user scope instead of a bot scope?
- Which two scopes does `users.lookupByEmail` need?
- Why does a combined sign-in and install request fail with `invalid_scope`?
The questions are free to read. Answers with their sources and self-grading are included with the pack.
Keep reading with the Slack Developer pack
- Core concepts
- Exam traps
- Worked scenario
The pack includes:
- All 38 lessons of the Slack Developer guide, with reading progress
- Interactive decision walks and step-by-step sequence steppers
- Inline practice questions and one-click topic practice (11 questions for this lesson)
- An official source cited for every claim
- An AI tutor that answers from the guide
- Unlimited timed practice exams