Skip to main content
Share
Guides

Slack Token Types Explained: xoxb vs xoxp vs xapp, Rotation and Auth Errors

Slack issues six kinds of token and most integration bugs come from holding the wrong one. What each prefix acts as, where to find it in app settings, which scopes and methods it works with, how rotation turns tokens into xoxe tokens, and what every auth error means.

Content & Engineering
Oct 16, 2026
12 min read
Updated Nov 2026Last verified November 2026
slack token typesxoxb vs xoxpslack bot tokenxapp token socket modeslack user token vs bot token
TL;DR

Slack issues six kinds of token and most integration bugs come from holding the wrong one. What each prefix acts as, where to find it in app settings, which scopes and methods it works with, how rotation turns tokens into xoxe tokens, and what every auth error means.

Key Takeaways
  • Slack bot tokens start with xoxb- and act as your app's bot user; user tokens start with xoxp- and act as the specific person who authorized the app; app-level tokens start with xapp- and represent the app itself, which is what Socket Mode needs (with the connections:write scope).
  • Most bots need an xoxb- token for the Web API and, only if they use Socket Mode, an xapp- token to open the WebSocket .
  • Last verified: November 2026 against Slack's token types documentation , token rotation guide and Socket Mode guide .
  • Slack documents six current token types.

Slack token types at a glance

Slack bot tokens start with xoxb- and act as your app's bot user; user tokens start with xoxp- and act as the specific person who authorized the app; app-level tokens start with xapp- and represent the app itself, which is what Socket Mode needs (with the connections:write scope). Most bots need an xoxb- token for the Web API and, only if they use Socket Mode, an xapp- token to open the WebSocket.

Last verified: November 2026 against Slack's token types documentation, token rotation guide and Socket Mode guide.

Slack documents six current token types. The prefix tells you which one you are holding:

TokenPrefixActs asExpiresTypical use
Bot tokenxoxb-The app's bot user, independent of any personNever, unless rotation is on (then 12 h)Posting messages, reading channels the bot is in, almost all bot work
User tokenxoxp-The human who authorized the app, with their visibilityNever, unless rotation is on (then 12 h)Acting on behalf of a user, user-only methods such as search.messages
App-level tokenxapp-The app across every organization it is installed inNever by default; opt-in rotation (12 h, xoxe.xapp-)Socket Mode, listing event authorizations
Configuration tokenxoxe.xoxp- (refresh xoxe-)A user in one workspace, for any of their apps12 hoursApp Manifest APIs: creating and updating apps from code
Workflow tokenxwfp-A subset of the bot token, for one workflow step15 minutes or when the step finishesWorkflow function steps; cannot perform actions that need a user scope
Service tokenNot specifiedA long-lived user tokenNever; cannot be rotatedSlack CLI automation for Deno Slack SDK apps only

The rest of this guide covers which of these your code actually needs, where each one lives in the app settings, how scopes attach to them, how rotation changes their shape, and what each auth error means.

xoxb vs xoxp: bot token or user token?

Slack's own advice is that a bot token "is usually for the best", and the reason is lifecycle. A bot token is tied to the app, not to a person, so the integration keeps working when the employee who installed it leaves and their account is deactivated. A user token is tied to that person: it sees exactly the channels, DMs and files they can see, its write actions appear as that person, and deactivating the person revokes it.

QuestionBot token (xoxb-)User token (xoxp-)
Who appears as the authorThe app's bot userThe authorizing human
What it can readConversations the bot is a member of, within its scopesEverything the user can see, within its scopes
Survives the installer leavingYesNo
Where scopes are declaredBot Token Scopes in OAuth & PermissionsUser Token Scopes in OAuth & Permissions
In the oauth.v2.access responseaccess_token with token_type: "bot"authed_user.access_token with token_type: "user"

Choose a user token only when the action is inherently personal: setting someone's status, posting as them, or calling a method that has no bot equivalent. Slack's method reference lists the accepted token types for each method under Scopes. Some representative cases:

MethodToken types acceptedScope
chat.postMessageBot or userchat:write
search.messagesUser onlysearch:read (user scope)
apps.connections.openApp-level onlyconnections:write
apps.event.authorizations.listApp-level onlyauthorizations:read
apps.manifest.createConfiguration tokenapp_configurations:write
auth.testAnyNone required

Calling a method with a token type it does not accept returns not_allowed_token_type, which is a different problem from a missing scope and is not fixed by adding scopes. The classic example is calling search.messages with a bot token.

Where to find each token in your app settings

All of these live at api.slack.com/apps under your app. The bot and user tokens only exist after the app is installed to a workspace.

You needWhere it is
Bot token (xoxb-)OAuth & Permissions, add at least one Bot Token Scope, click Install to Workspace, then copy it from OAuth Tokens on the same page. For apps installed by other workspaces, it arrives in your oauth.v2.access response instead.
User token (xoxp-)Same OAuth Tokens section, only if you requested User Token Scopes. Other users' tokens come from OAuth under authed_user.
App-level token (xapp-)Basic Information, scroll to App-Level Tokens and use the generate button; add connections:write for Socket Mode. Enabling Socket Mode also prompts you to create one.
Configuration tokenThe app list page (not inside an app): Your App Configuration Tokens, Generate Token. One per user and workspace, valid for all your apps there.
Client ID / client secretBasic Information, App Credentials. Used to exchange OAuth codes and refresh tokens; not a token itself.
Signing secretBasic Information, App Credentials. Used to verify that incoming HTTP requests came from Slack; it replaces the deprecated verification token.

The signing secret is the one most often pasted into the wrong field. It never goes in an Authorization header; it is only used to compute an HMAC over incoming request bodies. If your bot rejects every event with a signature error, check that the signing secret and not a token is configured for verification.

Once you have a token, confirm what it is rather than trusting the prefix you think you copied:

curl -s -X POST https://slack.com/api/auth.test \
  -H "Authorization: Bearer xoxb-..."

A bot token's response includes a bot_id; a user token's response does not. An invalid token returns invalid_auth, and a missing one returns not_authed. Every Web API response also carries an x-oauth-scopes header listing the scopes the calling token has, which settles most "which scopes does this token really have" debates in one request.

Try it yourself
Build your first chatbot free
Free plan, no credit card required. Live on your site in about 10 minutes.
Start building free

Which scopes go on which token

A scope is not global to your app; it is granted to a specific token type. In OAuth & Permissions you declare Bot Token Scopes and User Token Scopes separately, and each scope's reference page states its supported token types. Three behaviors from Slack's OAuth guide explain most scope confusion:

  • Scopes are additive. Re-running OAuth with new scopes adds them to the existing token's set. There is no way to remove a scope from a token except revoking it entirely.
  • Bot and user scope sets are independent. Granting channels:history as a user scope does nothing for your xoxb- token, and calls made with the bot token still fail with missing_scope.
  • App-level scopes are their own family. connections:write and authorizations:read only work on xapp- tokens. Their reference pages list app-level as the only supported token type, and ordinary bot or user scopes do not apply to an app-level token.

When a call fails with missing_scope, the error response typically names the needed scope and what the token provided. Add it to the right token family, then reinstall the app so a token carrying the new scope is issued; the reinstall step is covered in detail in our Slack bot troubleshooting guide. Request only what you use: Slack's security guidance is least privilege, and its OAuth guide states that apps must demonstrate usage of every scope they request.

The xapp token and Socket Mode

Socket Mode lets your app receive Events API and interactivity payloads over a WebSocket instead of a public HTTPS Request URL, which is why it is popular behind corporate firewalls. It needs two tokens, and missing either one is a common setup failure:

  1. App-level token (xapp-) with connections:write, used only to call apps.connections.open, which returns a temporary wss:// URL. Slack's reference specifies that this token must be sent in the HTTP Authorization header; sending it as a POST parameter results in an error.
  2. Bot token (xoxb-), used for everything the bot does once events arrive: chat.postMessage, conversations.history and so on.
export SLACK_APP_TOKEN='xapp-...'   # opens the socket
export SLACK_BOT_TOKEN='xoxb-...'   # calls the Web API

curl -s -X POST https://slack.com/api/apps.connections.open \
  -H "Authorization: Bearer $SLACK_APP_TOKEN"
# {"ok": true, "url": "wss://..."}

Bolt for JavaScript, Python and Java handle the connection and its regular refresh for you; you pass both values (appToken and token in Bolt for JavaScript). Two constraints from Slack's docs to know before you commit to it: Socket Mode is only available to apps using granular permissions, and Socket Mode apps are not currently allowed in the public Slack Marketplace. If you plan to distribute publicly, build for HTTP Request URLs.

Swapping the tokens produces the most confusing errors in this area. Passing an xoxb- token to apps.connections.open, or an xapp- token to chat.postMessage, both return not_allowed_token_type.

Telegram Bot Token Checker
Free tool - no signup, runs in your browser.
Open free tool

Token rotation: xoxe tokens, refresh tokens and the 12-hour clock

By default, bot and user OAuth tokens never expire. Token rotation, enabled per app in OAuth & Permissions (or under Published App Settings for Marketplace apps), replaces them with access tokens that expire every 12 hours plus single-use refresh tokens. Once enabled, rotation cannot be turned off, so test in a development app first.

ItemWith rotation enabled
Bot access tokenxoxe.xoxb-...
User access tokenxoxe.xoxp-...
Refresh tokenxoxe-1-..., single use
Lifetimeexpires_in: 43200 seconds (12 hours), always
Refresh calloauth.v2.access with grant_type=refresh_token, client_id, client_secret
Active tokensAt most 2; refreshing again revokes the oldest extra token
Manifest settingsettings.token_rotation_enabled: true
curl -s -X POST https://slack.com/api/oauth.v2.access \
  -d client_id=$CLIENT_ID \
  -d client_secret=$CLIENT_SECRET \
  -d grant_type=refresh_token \
  -d refresh_token=xoxe-1-...

The rules that bite in production:

  • Persist the new refresh token every time. Each refresh returns a new refresh token, and the one you used is revoked after a short grace period. Code that keeps reusing the original refresh token works once and then fails with invalid_refresh_token.
  • Refresh on a schedule, before expiry. An expired access token returns token_expired. With several workers, centralize refresh in one place, or the two-active-token limit will revoke tokens other workers are still using.
  • Migrating an existing app means calling oauth.v2.exchange once per long-lived token. Each token can only be exchanged once, and Slack warns not to auth.revoke the old long-lived token yourself, or the user must reinstall; it is expired for you after the first refresh.
  • Bolt handles it automatically; Slack lists Bolt for JavaScript 3.5, Bolt for Python 1.7 and Bolt for Java 1.9 as the versions with rotation support.

Rotating app-level and configuration tokens

App-level tokens can opt into rotation separately, from the Token rotation control in the App-Level Tokens section or with settings.app_level_token_rotation_enabled in the manifest (granular bot apps only; workflow apps are not eligible). New ones then start with xoxe.xapp-, work anywhere xapp- tokens do including apps.connections.open, expire after 12 hours, and refresh through the same oauth.v2.access call, returning token_type: "app_level". Up to two can be live at once so you can refresh without dropping the socket. The access and refresh values are shown only once at creation; lose them and you must create a new token. Existing app-level tokens created before you enable rotation keep working unchanged.

Configuration tokens always rotate: they expire 12 hours after generation and are refreshed with tooling.tokens.rotate, passing the xoxe- refresh token issued alongside them.

Token errors and what each one means

Slack Web API failures come back as "ok": false with an error string, usually with HTTP 200, so check ok rather than relying on the status code. The token-related errors, with Slack's own definitions:

ErrorSlack's definitionUsual fix
not_authedNo authentication token providedThe header is missing or misspelled, or the env var is empty at runtime
invalid_authSome aspect of authentication cannot be validated: the token is invalid, or the request comes from an IP address not on the app's allowed listCheck for truncation, whitespace or the wrong workspace's token; check IP allowlisting
not_allowed_token_typeThe token type used in this request is not allowedUse the token type the method accepts (bot vs user vs app-level)
missing_scopeThe token is not granted the specific scope requiredAdd the scope to the correct token family and reinstall
token_revokedToken is for a deleted user or workspace, or the app has been removed (user token)Reinstall or re-authorize; the old token cannot be revived
token_expiredAuthentication token has expiredRotation is on and the refresh did not happen in time
account_inactiveToken is for a deleted user or workspace (bot token)The workspace or bot user is gone; reinstall if the workspace still exists
invalid_refresh_tokenThe given refresh token is invalidYou reused a spent refresh token; restart from the latest stored one or re-run OAuth

The fastest triage is still auth.test with the exact value your code loads at runtime. If it succeeds, the token is valid and the problem is token type or scope; if it fails, nothing else you check matters until it passes. The full list of Slack API error strings, with causes and fixes, is in our Slack error reference, and rate limits by method tier are on the Slack limits page.

Revoking tokens and keeping them safe

auth.revoke revokes the token you call it with; pass test=1 to check behavior without actually revoking. Its effect depends on the token and on rotation:

  • Revoking a bot token does not uninstall the app, but it deactivates the bot user and removes its channel memberships. Incoming webhooks associated with the bot token are removed, and so are its slash commands unless a user token for the same app carries the commands scope.
  • With rotation enabled, auth.revoke revokes only that single token; the installation, event delivery and the ability to mint new tokens remain.
  • apps.uninstall removes the app and revokes every token for that installation.

Slack's security best practices add a few rules worth adopting as policy:

  • Never hardcode tokens or the client secret; use a secrets manager in production and inject them at runtime.
  • Send tokens in the Authorization: Bearer header or a POST body, never in a GET query string, where they end up in logs.
  • Never echo tokens back to users, including in error messages, and store each one linked to its workspace and user so one tenant's token can never be used for another.
  • Restrict token use by IP under OAuth & Permissions, Restrict API Token Usage: up to 20 IPv4 addresses or CIDR ranges. Calls from anywhere else fail with invalid_auth, which is worth remembering when a new server suddenly cannot authenticate.
  • Turn on token rotation for anything distributed, so a leaked access token is useful for at most 12 hours.

Two legacy notes. User tokens created before August 2016 may have 6- or 10-character secrets; Slack offers a one-time rotation to a longer secret with oauth.v2.beginShortTokenRotation and oauth.v2.completeShortTokenRotation. And legacy bot tokens, workspace-app tokens (fully deprecated in August 2021) and legacy custom-integration tokens are no longer recommended; newer platform features are not supported on legacy bot tokens, so migrate rather than debug them.

If the goal is a working support or FAQ bot in Slack rather than owning OAuth and rotation code, Conferbot's Slack integration lets you build the conversation in a visual editor. Either way, the token model above is what runs underneath.

Share this article:

Was this article helpful?

Ready to build your chatbot?

Join the businesses. Deploy on website, WhatsApp, and 11 more channels in minutes. Free forever plan available.

No credit cardNo coding13+ channels
Start Building Free

Get chatbot insights delivered weekly

Join 5,000+ professionals getting actionable AI chatbot strategies, industry benchmarks, and product updates.

🎯Automate this with a free chatbot

Build and deploy in 10 minutes. No coding needed.

❓FAQ

Slack Token Types Explained FAQ

Everything you need to know about chatbots for slack token types explained.

🔍
Popular:

An xoxb- token is a bot token: it acts as your app's bot user, is tied to the app rather than a person, and keeps working if the installer leaves. An xoxp- token is a user token: it acts as the specific person who authorized the app, sees what they see, and posts as them. Slack recommends bot tokens for most work and user tokens only for actions that must happen on a user's behalf.

An xapp- token is an app-level token. It represents your app across every organization where it is installed rather than any single workspace or user. Its main use is Socket Mode: with the connections:write scope, it calls apps.connections.open to get a WebSocket URL. It is created under Basic Information, App-Level Tokens, and it cannot call regular Web API methods like chat.postMessage.

Yes. The app-level xapp- token with connections:write only opens the WebSocket through apps.connections.open. Everything your bot does after an event arrives, such as posting a reply or reading history, uses the xoxb- bot token. Bolt takes both, for example appToken and token in Bolt for JavaScript, and fails to start or to respond if either is missing.

Open your app at api.slack.com/apps, go to OAuth & Permissions, add at least one Bot Token Scope, and click Install to Workspace. After approving, the bot token starting with xoxb- appears under OAuth Tokens on that same page. For workspaces that install your app through OAuth, the token arrives in the access_token field of the oauth.v2.access response instead.

Not by default. Slack OAuth tokens are long-lived until revoked, uninstalled or tied to a deactivated account. If you enable token rotation, bot and user access tokens are prefixed xoxe.xoxb- or xoxe.xoxp- and expire after 12 hours, and you refresh them with oauth.v2.access using a single-use refresh token. Rotation cannot be turned off once enabled.

It means the method does not accept the kind of token you sent, regardless of scopes. Common cases are calling search.messages with a bot token when it requires a user token, calling apps.connections.open with a bot token instead of an app-level token, or calling chat.postMessage with an xapp- token. Check the method's reference page for accepted token types and switch tokens.

missing_scope means the token type is acceptable but lacks the permission, so you add the scope to the right token family and reinstall. not_allowed_token_type means the method never accepts that token type, so no scope will help and you must use a different token. Running auth.test and reading the x-oauth-scopes response header tells you which situation you are in.

Slack could not validate the token. Usually it is truncated, wrapped in quotes or whitespace, from a different workspace or app, or already revoked. invalid_auth is also returned when the app restricts token use to allowed IP ranges and the request comes from an unlisted address, which often explains a token that works locally but fails from a new server. Test with auth.test first.

Call oauth.v2.access with grant_type=refresh_token, your client_id and client_secret, and the latest refresh token. The response contains a new access token valid for 43,200 seconds and a new refresh token; store both, because the refresh token you used is revoked after a short grace period. Refresh before expiry, and do it in one place so workers do not exceed the two-active-token limit.

Configuration tokens are used only with the App Manifest APIs, such as apps.manifest.create, to create and configure Slack apps from code. Each one is unique to a user and workspace but works for all of that user's apps there. They are generated from the app list page under Your App Configuration Tokens, expire after 12 hours, and are refreshed with tooling.tokens.rotate.

About the Author

Content & Engineering

The Conferbot team writes about building, deploying, and improving AI chatbots.

View all articles
Skip the blank canvas
Start from one of 250+ free chatbot templates for lead generation, support, e-commerce, and 20+ industries - customize and launch in minutes.
Browse free templates

Related Articles

From the reference shelf

Fact-checked reference pages and free tools for the platform this article covers.

Omnichannel Platform

One Chatbot,
Every Channel

Your chatbot works seamlessly across WhatsApp, Messenger, Slack, and 6 more platforms. Build once, deploy everywhere.

View All Channels
Conferbot
online
Hi! How can I help you today?
I need pricing info
Conferbot
Active now
Welcome! What are you looking for?
Book a demo
Sure! Pick a time slot:
#support
Conferbot
New ticket from Sarah: "Can't access dashboard"
Auto-resolved. Password reset link sent.