Slash commands allow users to invoke your app by typing a string into the message composer box. By enabling slash commands, your app can be summoned by users from any conversation in Slack. Slash commands created by developers cannot, however, be invoked in message threads.
A submitted slash command will cause a payload of data to be sent from Slack to the associated app. The app can then respond in whatever way it wants using the context provided by that payload.
When part of an app, they can be installed for your workspace as a single workspace app or distributed to other workspaces via the Slack Marketplace.
Built-in slash commands
There is a set of built-in slash commands. These include slash commands such as /topic
and /remind
.
Built-in slash commands are unique commands with unique additional features - they, along with Giphy app slash commands, are the only slash commands that can be invoked in message threads.
Slash commands require a particular invocation structure that makes them less universally usable compared to other app entry points. Ensure you understand your app's audience before implementation.
Let's look at an example slash command for an app that stores a list of to-do tasks:
/todo ask @crushermd to bake a birthday cake for @worf in #d-social
Here's the structure:
/todo
- This is the command
, the part that tells Slack to treat it as a slash command and where to route it. You'll define yours below.ask @crushermd to bake a birthday cake for @worf in #d-social
- This is the text
portion, it includes everything after the first space following the command. It is treated as a single parameter that is passed to the app that owns the command (we'll discuss this more below).We want to make sure that birthday cake gets baked, so read on to find out how to set up commands for your apps as well as how to handle and respond to them.
In order to get slash commands up and running with your app, you'll have to create the command itself, then prepare your app to be able to handle the interaction flow. We'll describe that flow in more detail in the steps below, but the basic pattern is:
That's all pretty orderly, so let's look at the recipe for making a great slash command.
You need two things to create a command:
If you don't already have a Slack app, click below to create one:
Now let's get to actually creating that command. First, head to your App Management dashboard, select the app you wish to work with, then select Slash Commands under Features in the navigation menu. You'll be presented with a button called Create New Command, and when you click it, you'll see a screen prompting you to define your new slash command:
Command: The name of the command, which is the actual string that users will type to trigger a world of magic. Bear in mind our naming advice below when you pick this.
Request URL: The URL we'll send a payload to when the command is invoked by a user. You'll want to use a URL that you can set up to receive these payloads as we'll describe later in this doc. If public distribution is active for your app, this needs to be an HTTPS URL (and self-signed certificates are not allowed). If you're building an app solely for your own workspace, it should also be HTTPS.
Short Description: A short description of what your command does.
Usage Hint: Displayed to users when they try to invoke the command, so if you have any parameters that can be used with your command, we recommend showing them here. You'll see a preview of the autocomplete entry where this hint is displayed, so make sure you're keeping this hint brief enough not to get truncated.
Escape channels, users, and links sent to your app: Turning this on will modify the parameters sent with a command by a user. It will wrap URLs in angle brackets and it will translate channel or user mentions into their correlated IDs. Private channels will only include the channel ID (<C987654321|>
) while public channels will display the channel ID and name (<C123456789|public-channel-01>
). So, if a user invoked your command like this:
/todo ask @crushermd to bake a birthday cake for @worf in #d-social
You'll receive the following in the sent data payload:
ask <@U012ABCDEF> to bake a birthday cake for <@U345GHIJKL> in <#C012ABCDE>
If disabled, the payload will repeat the plain text:
ask @crushermd to bake a birthday cake for @worf in #d-social
While your eyes might take less offense to the second example, in that case you'd have to resolve those plain-text names yourself using users.list
or conversations.list
if you planned to use any Slack API in your response to the command.
We recommend that you enable this feature if you expect to receive user or channel mentions in the command text.
Consider your command's name carefully. Slash commands are not namespaced. This means multiple commands may occupy the same name. If this happens and a user tries to invoke the command, Slack will always invoke the one that was installed most recently. It's an important thing to consider, especially if you're planning to distribute your app.
When you're picking a command name, you'll want to avoid terms that are generic and therefore likely to be duplicated. On the other hand, you don't want the command to be too complicated for users to easily remember.
In essence, a great command is descriptive and understandable but also unique. Naming it after your service is often a good idea.
Once you've created your command, any channel or workspace where your app is installed will immediately be able to start using it, so let's learn what to do when a user types one of your app's commands.
When a slash command is invoked, Slack sends an HTTP POST to the Request URL you specified above. This request contains a data payload describing the source command and who invoked it, like a really detailed knock at the door.
For example, imagine a workspace at example.slack.com installed an app with a command called /weather
. If someone on that workspace types /weather 94070
in their #test
channel and submits it, the following payload would be sent to the app:
token=gIkuvaNzQIHg97ATvDxqgjtO
&team_id=T0001
&team_domain=example
&enterprise_id=E0001
&enterprise_name=Globular%20Construct%20Inc
&channel_id=C2147483705
&channel_name=test
&user_id=U2147483697
&user_name=Steve
&command=/weather
&text=94070
&response_url=https://hooks.slack.com/commands/1234/5678
&trigger_id=13345224609.738474920.8088930838d88f008e0
&api_app_id=A123456
This data will be sent with a Content-type
header set as application/x-www-form-urlencoded
. Here are details of some of the important fields you might see in this payload:
Parameter | Description |
---|---|
token |
(Deprecated) This is a verification token, a deprecated feature that you shouldn't use any more. It was used to verify that requests were legitimately being sent by Slack to your app, but you should use the signed secrets functionality to do this instead. |
command |
The command that was entered to trigger this request. This value can be useful if you want to use a single Request URL to service multiple slash commands, as it allows you to tell them apart. |
text |
This is the part of the slash command after the command itself, and it can contain absolutely anything the user might decide to type. It is common to use this text parameter to provide extra context for the command. You can prompt users to adhere to a particular format by showing them in the Usage Hint field when creating a command. |
response_url |
A temporary webhook URL that you can use to generate message responses. |
trigger_id |
A short-lived ID that will allow your app to open a modal. |
user_id |
The ID of the user who triggered the command. |
user_name |
(Deprecated) The plain text name of the user who triggered the command. Do not rely on this field as it has been phased out. Use the user_id instead. |
team_id , enterprise_id , channel_id , etc. |
These IDs provide context about where the user was in Slack when they triggered your app's command (e.g. the workspace, Enterprise Grid, or channel). You may need these IDs for your command response. The various accompanying *_name values provide you with the plain text names for these IDs, but as always you should only rely on the IDs as the names might change arbitrarily. We'll include enterprise_id and enterprise_name parameters on command invocations when the executing workspace is part of an Enterprise Grid. |
api_app_id |
Your Slack app's unique identifier. Use this in conjunction with request signing to verify context for inbound requests. |
ssl_check
set to 1
and a token
parameter. token
value corresponds to the verification token registered with your app's slash command. See the token
field above for more information on validating verification tokens. Mostly, you may ignore these requests, but please do confirm receipt as below.
This payload is like getting all the ingredients to bake a really nice cake, so let's take a look at the recipe.
There are three main ingredients in the response cake:
The first is like the cake itself, a required minimum, but the other two are like optional icing and toppings. We'll examine this more closely.
This is the step which lets Slack, and therefore the user, know that the command was successfully received by the app, regardless of what the app intends to do. Your app can do this by sending back an empty HTTP 200 response to the original request.
If you don't do this, the user will be shown an error message that indicates that the slash command didn't work — not a great experience for the user, so you should always acknowledge receipt (unless you didn't receive the command, but then you wouldn't know not to respond, and now we've fallen into a logical paradox).
This confirmation must be received by Slack within 3000 milliseconds of the original request being sent, otherwise an operation_timeout
error will be displayed to the user. If you couldn't verify the request payload, your app should return an error instead and ignore the request.cThe HTTP 200 response doesn't have to be empty however, it can contain other useful stuff — a plain cake isn't all that tasty, so maybe we should add some icing.
As mentioned, you can include more substantive info in the body of your HTTP 200 response. In fact, you can use any of the complex formatting or Block Kit layout options that are available when sending any message.
You can include this message either as plain text in the response body:
It's 80 degrees right now.
Or as a JSON payload in the response body, with a Content-type
header of application/json
:
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*It's 80 degrees right now.*"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Partly cloudy today and tomorrow"
}
}
]
}
These message responses can even include interactive elements like buttons or menus to allow users to interact more and keep the workflow active. Read our guide to composing messages to explore the full range of possibilities.
Message Visibility
There's one special feature to response messages — when responding with a JSON payload you can directly control whether the message will be visible only to the user who triggered the command (we call these ephemeral messages), or to all members of the channel where the command was triggered.
The response_type
parameter in the JSON payload controls this visibility; by default it is set to ephemeral
, but you can specify a value of in_channel
to post the response into the channel, like this:
{
"response_type": "in_channel",
"text": "It's 80 degrees right now."
}
When the response_type
is in_channel
, both the response message and the initial slash command entered by the user will be shared in the channel:
For the most clarity, we recommend always declaring your intended response_type
, even if you wish to use the default ephemeral
value.
Now that you've added some response icing, this cake is looking pretty tasty. But is there anything else can you do to respond?
If you need to respond outside of the 3-second window provided by the request responses above, you still have plenty of options for keeping the workflow alive.
Read our guide to responding to user interactions. There, we'll explain how you can use fields such as response_url
or trigger_id
from your slash command payload to open modals and send messages.
A note about replace_original
and delete_original
When you're reading our guide to responding to user interactions, you'll encounter fields called replace_original
and delete_original
, which can be used in conjunction with your response_url
to modify previously-posted app messages in that interactive chain.
It's important to note that these fields cannot modify the original user-posted message that was used to invoke the slash command.
We also explain all the multitude of other ways you can top this cake.
There are going to be times when you need to let the user know that something went wrong — perhaps the user supplied an incorrect text parameter alongside the command, or maybe there was a failure in an API being used to generate the command response.
It would be tempting in this case to return an HTTP 500 response to the initial command, but this isn't the right approach. The status code returned as a response to the command should only be used to indicate whether or not the request URL successfully received the data payload — while an error might have occurred in processing and responding to that payload, the communication itself was still successful.
Instead, you should continue to follow the above instructions to send either a response back via the HTTP request or using the request_url
in a message response. In that response message, communicate the error back to the user:
{
"response_type": "ephemeral",
"text": "Sorry, slash commando, that didn't work. Please try again."
}
response_type
field set to in_channel
: {"response_type": "in_channel"}
./please
, you should provide a response to /please help
that lists the other actions available.token
field in an incoming slash command request that has been issued to you by Slack.api_app_id
to differentiate which app is intended for the slash command invocation.