Go to Slack

Upgrading permissions progressively

Developer preview

Workspace apps are currently in developer preview. You are welcome to install as internal integrations or distribute to other workspaces, but we aren't yet accepting workspace app submissions for the Slack app directory.

OAuth and the Permissions API work just fine for apps wanting to ask for all the permissions up front.

But a workspace app may ask for additional permissions conversationally, progressively adding resources and scope to its capabilities.

Asking for additional permissions progressively

Your app's initial moment of installation is just the beginning.

By default, your app is only installed into a single conversation with a single user — the installer.

You can request a list of OAuth scopes as part of an OAuth-based installation process, getting assigned specific scopes and resource combinations by the installer. But it'd be pretty tedious for every user that wants to work with your app to have to also go through this installation flow.

Imagine another user starting a DM with your app and realizing you don't have the permission to examine their profile, or your recently created slash command wasn't yet installed.

Prerequisites

To effectively use progressive permissions, your app should make use of the Events API, app events, and either interactive messages or slash commands.

Flow

The progressive permissions flow begins with a user interacting with your app.

Step 1: User interaction

To ask for an additional permission, a user must first interact with your application one of two ways: 1. Invoke a slash command, if your app offers one 2. Interact with a message button or message menu as part of the interactive message framework.

Step 2: Your app receives a trigger_id

When that interaction occurs, your slash command request URL or interactive message request URL will receive a standard payload with a bonus field called trigger_id.

A trigger_id is an artifact of interaction between the user and your app. Use it with three seconds of receiving to draw a theoretical dotted line between the interaction and your requesting permissions. Learn more about triggers.

Step 3: Use the trigger_id to request upgraded permissions

Using the apps.permissions.request method, request any additional OAuth scopes, passing the time-sensitive trigger_id as a kind of symbolic proof of user interest.

Step 4: User approves or revokes your request

After receiving a successful apps.permissions.request, Slack will pop open an in-client dialog with the end user, making the case for your upgraded permissions.

Step 5: Your app receives additional grants

Depending on whether they approve or revoke, you'll then receive a set of accompanying Events API events: scope_granted and resources_granted if it's successful, or scope_denied if they turn your request down.


Walkthrough

Here are the nuts and bolts of a typical progressive permissions flow:

Your app is installed in a direct message conversation with @izzy. You only have the default permissions and they only apply to your conversation with @izzy.

You want to install a slash command in your app's conversations.

Step 1: Tell @izzy you have a slash command and to install it just click this button by posting an interactive message to your direct message conversation.

We'll send a simple message using chat.postMessage with a message button attachment delivered like:

POST slack.com/api/chat.postMessage
Authorization: Bearer xoxa-token-token-token
Content-type: application/json; charset=utf-8
{
    "text": "New feature alert: Try our new slash command to look up all the intersections you've collected.",
    "attachments": [
        {
            "callback_id": "perm_request_1234",
            "attachment_type": "default",
            "fallback": "Adding this command requires an official Slack client.",
            "actions": [
                {
                    "name": "add_commands",
                    "text": "Add /insectoid command",
                    "type": "button",
                    "value": "add"
                }
            ]
        }
    ],
    "channel": "D0BH95DLH"
}

Your message will deliver to the "app home" conversation with the user in "channel" D0BH95DLH.

Step 2: Press the button, get a trigger.

When a user (probably you, if you're following along with this) presses the Add /insectoid command message button we created, your Action URL will receive a standard message button invocation, including a trigger_id.

Trigger IDs are pointers to a point of time in a conversation or interaction your app was involved in. They are short-lived and can be used to perform a few different operations. The one we're most concerned with is opening a permission upgrade request dialog with the user.

Here's an example: 11820576016.6048553856.093b9205636ddbfc180e1e74d33e9af6

Step 3: Use apps.permissions.request to prompt Slack to invoke a modal permissions dialog with the user.

apps.permissions.request takes three parameters:

  • token - your workspace token for the workspace, typical!
  • trigger_id - the trigger_id value you only just received from Step 2. It's time-sensitive. Use it context with your moment of conversation.
  • scopes - a comma-separated list of the scopes you want to add to the app, in this case commands but could've been users:read,channels:history or similar.

Our request may look something like:

POST https://slack.com/api/apps.permissions.request?token=xoxa-token-token-token
trigger_id=11820576016.6048553856.093b9205636ddbfc180e1e74d33e9af6
&scopes=commands

The HTTP response is a simple HTTP 200 with {"ok":true} if things check out or {"ok":false} if you don't provide a valid scope or trigger_id or otherwise can't properly make this request.

Step 4: The user is presented with a dialog in their open Slack client. It's super-friendly. It asks whether this app can upgrade its permissions to include commands, the ability to use Slash commands. Let's assume the user says yes here.

Step 5: Your Events API request URL receives a small flurry of events (OK, 2):

The first is scope_granted and it tells you that the commands scope was added to the installation. It even includes the trigger_id you just used in case you want to track it roundtrip:

{
    "token": "verification_token",
    "team_id": "T061EG9R6",
    "api_app_id": "A0BLA3EP2",
    "event": {
        "type": "scope_granted",
        "scopes": [
            "commands"
        ],
        "trigger_id": "11820576016.6048553856.093b9205636ddbfc180e1e74d33e9af6"
    },
    "type": "event_callback",
    "authed_teams": [],
    "event_id": "Ev0BQ5FTL0",
    "event_time": 1497911545
}

The second event gets into more detail about the resources involved and where that commands scope is now applicable to. In the case of commands, that'll be any channel contexts your app is installed in. Slash commands are used in channels and other conversational contexts.

{
    "token": "verification_token",
    "team_id": "T061EG9R6",
    "api_app_id": "A0BLA3EP2",
    "event": {
        "type": "resources_added",
        "resources": [
            {
                "resource": {
                    "type": "im",
                    "grant": {
                        "type": "specific",
                        "resource_id": "D0BH95DLH"
                    }
                },
                "scopes": [
                    "chat:write",
                    "im:read",
                    "im:history",
                    "commands"
                ]
            }
        ]
    },
    "type": "event_callback",
    "authed_teams": [],
    "event_id": "Ev0BLTJ7JM",
    "event_time": 1497911545
}

Now @izzy can use /insectoid in your app-to-user DM conversation and any other channels your app is installed in.