Differences between classic apps and Slack apps

This quickstart guide explains how Slack apps differ from classic apps.

If you'd like to learn from the ground up, start with our installation guide for Slack apps. Otherwise, read on.


Getting started

You can create a Slack app with the click of a button. This button, specifically:

Create a Slack app

With a created app, you can now follow along.


Configuring your app

Slack apps use bot users as the basis for API calls. You'll notice some resulting changes while developing your app.

First things first: select the OAuth & Permissions sidebar in your app management page. You'll see some changes:

  • You can now select individual scopes for your bot token, rather than just one umbrella bot scope that grants access to many methods, some of which you may not need.
  • If you select a scope under the Bot Token Scopes selector, and then click to install your app, you'll automatically gain a Bot User OAuth Access Token, with no corresponding user tokens.

Always assign scopes to your bot user token, rather than a user token. User token scopes should be selected only for user impersonation.

New bot user API access tokens may not access RTM. For most apps, the Events API lets your app listen to Slack goings-on in a more structured, safe way. If you require access to RTM (say, because you're building your app behind a corporate firewall), continue to use a classic app bot token to call rtm.connect.


Installing your app

When you want to install your Slack app on other workspaces using OAuth, your app should redirect users to a new OAuth v2/authorize URL: slack.com/oauth/v2/authorize.

Once the user checks out the scopes you've requested and okays them, the user is redirected back to your app along with a temporary access code. Exchange the code for an API access token by calling a new OAuth v2.access method: oauth.v2.access.

Dive deeper into the new OAuth flow with this in-depth guide.

Here's the response you'll see from the v2.access method:

{
    "ok": true,
    "access_token": "xoxb-123abc...",
    "token_type": "bot",
    "scope": "commands,incoming-webhook",
    "bot_user_id": "U123ABC456",
    "app_id": "A123ABC456",
    "team": {
        "name": "Slack Softball Team",
        "id": "T123ABC456"
    },
    "enterprise": {
        "name": "slack-sports",
        "id": "E123ABC456"
    },
    "authed_user": {
        "id": "U222ABC222",
        "scope": "chat:write",
        "access_token": "xoxp-123abc...",
        "token_type": "user"
    }
}

Here are a few important things to note:

  • The response from oauth.v2.access has a slightly different shape than from the previous, non-V2 endpoint. That's because we now present the bot user access token at the top level, not the user token. Check out the method documentation page for more detail.
  • Once you initiate OAuth with the new OAuth v2/authorize URL, you have to complete it with the new v2.access method—you can't combine the v2/authorize URL with the old V1 access method.

Making API calls

Unlike tokens imbued with the old bot scope, your new API access token requests granular scopes for each method it wishes to call.

The granular scopes work exactly like the scopes applied to the old user token. However, your new API access token is a bot token, and as such it isn't tied to a specific user.

One effect worth noting: chat.postMessage and other chat.* methods no longer mess around with the as_user parameter. You're granted a single chat:write scope (no :user or :bot is appended). If you call the chat.postMessage method with your bot token, you post as the bot. If you've obtained a user token through the new install flow, and you call the method with your user token, you post as the user.

New bot user API access tokens may not access RTM. For most apps, the Events API lets your app listen to Slack goings-on in a more structured, safe way. If you require access to RTM (say, because you're building your app behind a corporate firewall), continue to use a classic app bot token to call rtm.connect.


Receiving events

Your app receives only the events it has scopes to listen for. For example, subscribing to the channels_created event automatically means that your app requests the channels:read scope.


Using Slash commands

New: Bot users in Slack apps may request the commands scope, allowing them to implement Slash commands.

Similar to the way that user deactivation for an installing user could deactivate a Slash command with older Slack apps, revoking the bot user token may cause the Slash command to be removed from a workspace.


Using Incoming Webhooks

New: Bot users in Slack apps may request the incoming-webhook scope, allowing them to post messages via incoming webhooks.

Similar to the way that user deactivation for an installing user could deactivate a webhook with older Slack apps, revoking the bot user token may cause the webhook to be removed from a workspace.


Using channels:join and channels:manage

Request the channels:join scope to allow your app to join public channels.

Request the channels:manage scope to allow your app to create new channels and manage the ones it's already part of.


Deprecating perspectival scopes

Scopes that indicate a "perspective" by appending either :user or :bot are deprecated. Slack apps nearly always act on their own behalf, rather than doing actions in the name of a human user.

Specifically, the chat:write scope replaces chat:write:user and chat:write:bot, and the files:write scope replaces files:write:user.

Perspectival scopes are deprecated for all bot and user tokens created through the new OAuth flow. This means the as_user field for the chat.postMessage method is also no longer necessary for Slack apps.


Link unfurling

You can request the links:read and links:write scopes so that your app can handle unfurls.

A link shared in a channel will only be unfurled if the token with links:write has access to the message that contains the link. For example, if you have a Slack app and an installing user shares a link to a private channel, but the Slack app is not in that private channel, that link will not unfurl.


Miscellaneous

Effects of user deactivation on tokens

Here's one big step forward for bots in Slack apps: deactivation of an installing user no longer has an effect on the app.

Posting in public channels and customizing message authorship

Slack apps do not begin life with the ability to post to any public channel without joining, as classic bots did. Nor do they start with the ability to adjust username or icon when posting messages.

Good news: with two special scopes, you can gain those abilities by asking for them explicitly. Request the chat:write.public scope and chat:write.customize scope, respectively, to gain the ability post in all public channels and adjust your app's message authorship.

Check out the chat.postMessage documentation for more details.

Revoking tokens

For a Slack app, bot user API access tokens are still revoked in the same way, via the auth.revoke method. When that happens:

  • The bot token no longer works.
  • The bot user is removed from the workspace.
  • Slash commands associated with the bot token will be removed from the workspace if no user tokens for the same app exist and carry the commands scope.
  • Incoming webhooks that were installed and associated with the bot token will be removed.
  • If no user tokens for the same app exist, the app will appear to be uninstalled from the workspace.

Migration

In March 2026, we will discontinue support for classic apps. For your apps to continue working, you will need to migrate them to Slack apps. Any custom bots or classic apps you have built will no longer work after these dates. Refer to this changelog article for more details.

Check out our guide to migrating your classic app to a Slack app.

Recommended reading