Go to Slack

Interacting with users through dialogs

Create focused workflows with Dialogs, the quickest way to collect information on the go in Slack.

Conversation with bots is best when it's casual, as natural as spoken language. If you need more structured information from a user, a straight forward form-based approach will lead to clear, actionable information.

Introducing Slack Dialogs

Users trigger your app's dialogs by clicking on buttons, selecting from menus, or invoking slash commands. Dialogs contain a variety of guided input types.

The dialog experience focuses squarely on the task at hand. User input is validated before being sent back to your app.

Implementing dialogs in your app

Dialogs are part of the interactive framework already powering message menus and buttons.

While many aspects of dialog development are similar, the JSON used to compose a form differs significantly from interactive message components.

To continue interacting with a user after form submission, your app will need to create new messages or utilize the response_url associated with the interaction that originally spawned the dialog.

Apps can invoke dialogs when users interact with slash commands, message buttons, or message menus. Each interaction will include a trigger_id, a kind of short-lived pointer to interaction's who, what, where, and when. Form submissions deliver to the Request URL associated with your Slack app.

Dialogs may contain a careful mixture of standard inputs: short text entry, long-form text areas, and drop-down menus. More are on the way.

Implementation overview

Most developers building and handling dialogs will follow steps similar to these:

  1. Build an interactive message, a slash command, or both. Dialogs cannot open until users interact with buttons, menus, or slash commands.
  2. As users interact or invoke commands, look for a trigger_id in the command invocation or interactive action payload.
  3. Use dialog.open to initiate a dialog in context with the user, providing a trigger_id and desired form elements.
  4. Once completed, results are sent to your application's interactive Request URL.
  5. Your app posts the results to a channel or provides some other submission confirmation message.

Preparing apps for dialogs

You'll need to create, configure, and install a Slack app before getting started with dialogs. Legacy custom integrations are not supported.

At minimum, you must configure the Interactive Components section of app management. Follow the UI's instructions to provide a Request URL to receive form submissions and other interactions.

If you will use a slash command to initiate dialogs, you will also need to configure your app's slash command.

It is necessary to reinstall your app after adding features and capabilities.

Using dialogs can greatly enhance your in-house internal integration tools.

Interactive triggers

A dialog cannot be invoked without first being initiated by a message interaction or a slash command. That means a user needs to interact with a message button, message menu, or slash command provided by your app before they can engage with any dialog experiences your app provides.

Slack attaches a trigger_id value as part of all interaction payloads you receive, which acts as a pointer to a specific moment in the space-Slack-time continuum where a user interacted with your app.

Here's an example of an interactive message action containing a trigger_id:

{
    "actions": [
      {
        "name": "channels_list",
        "selected_options": [
          {
          "value": "C012AB3CD"
          }
        ]
      }
    ],
    "callback_id": "select_simple_1234",
    "team": {
      "id": "T012AB0A1",
      "domain": "pocket-calculator"
    },
    "channel": {
      "id": "C012AB3CD",
      "name": "general"
    },
    "user": {
      "id": "U012A1BCD",
      "name": "musik"
    },
    "action_ts": "1481579588.685999",
    "message_ts": "1481579582.000003",
    "attachment_id": "1",
    "token": "iUeRJkkRC9RMMvSRTd8gdq2m",
    "response_url": "https://hooks.slack.com/actions/T012AB0A1/123456789/JpmK0yzoZDeRiqfeduTBYXWQ",
    "trigger_id": "13345224609.738474920.8088930838d88f008e0"
}

And here's an example of a slash command execution containing a trigger_id:

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

These interactions are the inciting event to your app opening a dialog. The trigger_id is the key to unlock you app's momentary, focused dialog functionality. Because the trigger_id expires in 3 seconds, you must exchange the trigger to open a dialog in the given time interval. Using an expired trigger causes the trigger_expired error.

Use dialog.open soon after receiving a trigger_id. Triggers expire 3 seconds after being issued to your app.

Opening a dialog

To begin a modal dialog, call the dialog.open method.

As with all of our Web API methods, dialog.open typically takes URL-encoded parameters as arguments. We also support posting JSON.

Opening dialogs with JSON

The easiest way to open a dialog is to send a POST with a Content-type HTTP header set to application/json and a raw POST body containing your dialog's JSON presented as the dialog argument:

{
  "trigger_id": "13345224609.738474920.8088930838d88f008e0",
    "dialog": {
      "callback_id": "ryde-46e2b0",
      "title": "Request a Ride",
      "submit_label": "Request",
      "elements": [
          {
              "type": "text",
              "label": "Pickup Location",
              "name": "loc_origin"
          },
          {
              "type": "text",
              "label": "Dropoff Location",
              "name": "loc_destination"
          }
      ]
  }
}

See this section on HTTP POST bodies for any needed instruction.

Opening dialogs with URL-encoded JSON

A more complicated way to open dialogs is by sending your carefully crafted JSON as an application/x-www-form-urlencoded query parameter.

Similar to chat.postMessage, chat.unfurl, and chat.update this method also includes a parameter that expects a JSON object encoded with application/x-www-form-urlencoded.

A simple form you might create could be modeled in JSON as:

{
    "callback_id": "ryde-46e2b0",
    "title": "Request a Ride",
    "submit_label": "Request",
    "elements": [
        {
            "type": "text",
            "label": "Pickup Location",
            "name": "loc_origin"
        },
        {
            "type": "text",
            "label": "Dropoff Location",
            "name": "loc_destination"
        }
    ]
}

To prepare that as a HTTP POST to dialog.open, you'd optionally minify and then URL encode that JSON to a single string, displayed below as the value for the dialog POST body parameter.

POST /api/dialog.open?token=xoxb-such-and-such&trigger_id=13345224609.738474920.8088930838d88f008e0
dialog=%7B%22callback_id%22%3A%22ryde-46e2b0%22%2C%22title%22%3A%22Request%20a%20Ride%22%2C%22submit_label%22%3A%22Request%22%2C%22elements%22%3A%5B%7B%22type%22%3A%22text%22%2C%22label%22%3A%22Pickup%20Location%22%2C%22name%22%3A%22loc_origin%22%7D%2C%7B%22type%22%3A%22text%22%2C%22label%22%3A%22Dropoff%20Location%22%2C%22name%22%3A%22loc_destination%22%7D%5D%7D

If all is well, you'll get a clean HTTP 200 OK response with an application/json body declaring:

{
  "ok": true
}

Error handling

If your dialog parameter or other aspects of your dialog are invalid, detailed errors are provided to help aid you in correcting them. See dialog.open for full detail on error conditions.

Top-level dialog attributes

Your dialog is presented to users stylishly with your carefully chosen title and curated form elements.

By default, all form elements are required. Use the optional field to make an element non-mandatory.

Attribute Type Description
title String User-facing title of this entire dialog. 24 characters to work with and it's required.
callback_id String An identifier strictly for you to recognize submissions of this particular instance of a dialog. Use something meaningful to your app. 255 characters maximum. Absolutely required.
elements Array Up to 5 form elements are allowed per dialog. See elements below. Required.
submit_label String User-facing string for whichever button-like thing submits the form, depending on form factor. Defaults to Submit, localized in whichever language the end user prefers. 24 characters maximum, and may contain only a single word.

Dialog form elements

For best practices in designing your dialogs, consult our guide to Creating useful dialogs.

The current list of supported form elements includes:

  • text - Text inputs work well with concise free-form answers and inputs with unestablished bounds, such as names, email addresses, or ticket titles if your form is used for something like a bug tracker.
  • textarea - Text Areas are best when the expected answer is long — over 150 characters or so —. It is best for open-ended and qualitative questions.
  • select - Select menus are for multiple choice questions, and great for close-ended quantitative questions, such as office locations, priority level, meal preference, etc. The select elements may contain static menus or dynamically loaded menus specified with an optional data_source.

Text elements

Text elements are single-line plain text fields.

By default, all fields are required for a user to fill., or the client validation will give the user an error. You can also set each field optional ("optional": "true") and in this case, empty fields will submit as null.

Dialog text element

Example:

{
  "label": "Email Address",
  "name": "email",
  "type": "text",
  "subtype": "email",
  "placeholder": "you@example.com"
}

There is an optional subtype for the type: text. The value of the subtype can be set either email, number, tel, or url, where the default is a plain text. Setting the subtype is especially important for mobile Slack clients where it will trigger special keyboards, for instance, when a form field expects a phone number, you should use the tel so it invokes the numeric keypad.

Dialog text element subtypes

Text element attributes

Element Type Description
label String Label displayed to user. Required. 24 character maximum.
name String Name of form element. Required. No more than 300 characters.
type String The type of form element. For a text input, the type is always text. Required.
max_length Integer Maximum input length allowed for element. Up to 150 characters. Defaults to 150.
min_length Integer Minimum input length allowed for element. Up to 150 characters. Defaults to 0.
optional Boolean Provide true when the form element is not required. By default, form elements are required.
hint String Helpful text provided to assist users in answering a question. Up to 150 characters.
subtype String A subtype for this text input. Accepts email, number, tel, or url. In some form factors, optimized input is provided for this subtype.
value String A default value for this field. Up to 500 characters.
placeholder String A string displayed as needed to help guide users in completing the element. 150 character maximum.

Textarea elements

A textarea is a multi-line plain text editing control. You've likely encountered these on the world wide web. Use this element if you want a relatively long answer from users. The element UI provides a remaining character count to the max_length you have set or the default, 3000.

Dialog textarea element

Like text, this element supports subtype values of email, number, url, and tel.

Simplest example:

{
  "label": "Additional information",
  "name": "comment",
  "type": "textarea",
  "hint": "Provide additional information if needed."
}

Textarea element attributes

Attribute Type Description
type String For a text area, the type is always textarea. It's required.
label String Label displayed to user. Required. No more than 24 characters.
name String Name of form element. Required. No more than 300 characters.
placeholder String A string displayed as needed to help guide users in completing the element. 150 character maximum.
max_length Integer Maximum input length allowed for element. 0-3000 characters. Defaults to 3000.
min_length Integer Minimum input length allowed for element. 1-3000 characters. Defaults to 0.
optional Boolean Provide true when the form element is not required. By default, form elements are required.
hint String Helpful text provided to assist users in answering a question. Up to 150 characters.
subtype String A subtype for this text area, just in case you need a lot of space for them. email, number, tel, or url
value String A default value for this field. Up to 3000 characters.

Select elements

Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu.

Dialog select element

A select element may contain up to 100 selections, provided as an array of simple hashes (see below) in the form element's options field, (or an option_groups array).

To set a default selection, provide a top-level value attribute containing a value presented within the element's collection of options.

Example select form element definition:

{
  "label": "Meal preferences",
  "type": "select",
  "name": "meal_preferences",
  "value": "vegan",
  "options": [
    {
      "label": "Hindu (Indian) vegetarian",
      "value": "hindu"
    },
    {
      "label": "Strict vegan",
      "value": "vegan"
    },
    {
      "label": "Kosher",
      "value": "kosher"
    },
    {
      "label": "Just put it in a burrito",
      "value": "burrito"
    } 
  ]
}

Dialog select element with options

Example select form element with option_groups:

{
  "label": "Choose a meme",
  "name": "animal",
  "type": "select",
  "option_groups": [
    {
      "label": "Cats",
      "options": [
        {
          "label": "Maru",
          "value": "maru"
        },
        {
          "label": "Lil Bub",
          "value": "lilbub"
        },
        {
          "label": "Hamilton the Hipster Cat",
          "value": "hamilton"
        }
      ]
    },
    {
      "label": "Dogs",
      "options": [
        {
          "label": "Boo the Pomeranian",
          "value": "boo"
        }
      ]
    },
  ]
}

Dialog select element with option groups

Populate a select menu dynamically

In addition to the static select menu, you can also generate a data set for a menu on the fly. Make dialog select menus more dynamic by specifying one of these four data_source:

Data source type Description
users The element will be populated with options corresponding to the users available to the current user.
channels The element will be populated with options corresponding to the public channels available to the current user.
conversations The element will be populated with options corresponding to the public channels, private channels, DMs, and MPIMs available to the current user.
external The element will be populated with options or options groups return from an endpoint specified in your app configuration setting.

If no data_source is specified, it defaults to static.

👤 Dynamic user list

Now you can easily populate a select menu with a list of users. For example, when you are creating a bug tracking app, you want to include a field for an assignee. Slack pre-populates the user list in client-side, so your app doesn't need access to a related OAuth scope.

Example select element with users data source:

{
  "label": "Assignee",
  "name": "bug_assignee",
  "type": "select",
  "data_source": "users"
}

Dynamic select from user list

#️⃣ Channels and conversations

You can also provide a select menu with a list of channels. Specify your data_source as channels to limit only to public channels or use conversations to include private channels, direct messages, MPIMs, and whatever else we consider a conversation-like thing.

Example select element with conversations data source:

{
  "label": "Post this message on",
  "name": "channel_notify",
  "type": "select",
  "data_source": "conversations"
}

Dynamic select from channels

📄 Dynamic data from an external source

Once you've configured your load URL in your app's interactive message settings, create a message with an attachment action set with a data_source set to external.

As soon as a user clicks or taps the drop-down menu, a request is sent to your specified URL, expecting a HTTP 200 back with your JSON response.

Example select element with external data source:

{
  "label": "Bug ticket",
  "name": "ticket_list",
  "type": "select",
  "data_source": "external",
  "min_query_length": 2,
}

From the external URL, a list of options (or an option_groups array) will be returned. The options returned here will be shown to the user with a filtering feature. As the user types characters, the select list items automatically get updated.

You can use the min_query_length property to specify the number of characters required to be typed in before dispatching the queries.

Dynamic select from external data

Example options from the external data source:

{
  "options": [
    {
      "label": "[UXD-342] The button color should be artichoke green, not jalapeño",
      "value": "UXD-342"
    },
    {
      "label": "[FE-459] Remove the marquee tag",
      "value": "FE-459"
    },
    {
      "label": "[FE-238] Too many shades of gray in master CSS",
      "value": "FE-238"
    }
  ]
}

Example option_groups from the external data source:

{
  "option_groups": [
    {
      "label": "Visual Design",
      "options": [
        {
          "label": "[UXD-342] The button color should be artichoke green, not jalapeño",
          "value": "UXD-342"
        }
      ]
    },
    {
      "label": "Front-End Engineering",
      "options": [
        {
          "label": "[FE-459] Remove the marquee tag",
          "value": "FE-459"
        },
        {
          "label": "[FE-238] Too many shades of gray in master CSS",
          "value": "FE-238"
        }
      ]
    }
  ]
}

A maximum of 100 options may be included.

Please note that dialog menus uses the more clearly labeled field label instead of text for menu option labels.

Typeahead outgoing requests

Users may use typeahead to filter and find their selections faster. As users type, Slack dispatches data to your app. You can use.

{
  "type": "dialog_suggestion",
  "team": {
    "id": "T24BK35ML",
    "domain": "hooli-hq"
  },
  "user": {
    "id": "U900MV5U7",
    "name": "gbelson"
  },
  "channel": {
    "id": "C012AB3CD",
    "name": "triage-platform"
  },
  "action_ts": "1520635427.671963",
  "token": "W3VDvuzi2nRLsiaDOsmJranO",
  "name": "external_data",
  "value": "fe",
  "callback_id": "bugs"
}

Attributes for static and dynamic select elements

Attribute Type Description
label String Label displayed to user. Required. No more than 24 characters.
name String Name of form element. Required. No more than 300 characters.
type String Set this to select for select elements.
data_source String Set this to either users, channels, conversations, or external. Default value is static.
placeholder String A string displayed as needed to help guide users in completing the element. 150 character maximum.
optional Boolean Provide true when the form element is not required. By default, form elements are required.
value String A default value for static select, also, the data source types, users, channels, and conversations. This option is invalid in external, where you must use selected_options.
selected_options String A default value for external only. (See value).
options Array Provide up to 100 options. Either options or option_groups is required for the static and external. options[].label is a user-facing string for this option. 75 characters maximum. Required.
options[].value is a string value for your app. If an integer is used, it will be parsed as a string. 75 characters maximum. Required.
option_groups Array An array of objects containing a label and a list of options. Provide up to 100 option groups. Either options or option_groups is required for the static and external.

options_groups[].label is a user-facing string for this option. 75 characters maximum. Required.
options_groups[].options is an array that contains a list of options. It is formatted like the options array (see options).

Dialog submission sequence

The typical submission workflow is:

  1. When users submit a form, Slack will validate their responses against the dialog's configuration.
  2. When the form is successfully submitted, Slack will send a request to your Request URL with the callback_id you set at dialog creation and the values submitted by the user.
  3. Your app has the opportunity to validate the user's responses according to your own business logic and suggest additional corrections before final submission.
  4. When the dialog is fully submitted, you app should display some kind of result or feedback to users.

Upon receiving this payload, your server must respond within 3 seconds, whether the form is valid or not. Otherwise, use the response_url to POST a delayed response.

Evaluating submission responses

Your "Request URL" (formerly known as an "Action URL"), configured in your application management settings under Interactive Components, will receive an interactive framework JSON structure in a URL-encoded payload POST parameter.

For example, consider this submission:

POST https://example.com/your-request-url
payload=%7B%22type%22%3A%22dialog_submission%22%2C%22submission%22%3A%7B%22name%22%3A%22Sigourney%20Dreamweaver%22%2C%22email%22%3A%22sigdre%40example.com%22%2C%22phone%22%3A%22%2B1%20800-555-1212%22%2C%22meal%22%3A%22burrito%22%2C%22comment%22%3A%22No%20sour%20cream%20please%22%7D%2C%22callback_id%22%3Aemployee_offsite_1138b%22%2C%22team%22%3A%7B%22id%22%3A%22T1ABCD2E12%22%2C%22domain%22%3A%22coverbands%22%7D%2C%22user%22%3A%7B%22id%22%3A%22W12A3BCDEF%22%2C%22name%22%3A%22dreamweaver%22%7D%2C%22channel%22%3A%7B%22id%22%3A%22C1AB2C3DE%22%2C%22name%22%3A%22coverthon-1999%22%7D%2C%22action_ts%22%3A%22936893340.702759%22%2C%22token%22%3A%22M1AqUUw3FqayAbqNtsGMch72%22%7D%0A

Decode the payload parameter's JSON and you have a hash with a few notable keys:

{
    "type": "dialog_submission",
    "submission": {
        "name": "Sigourney Dreamweaver",
        "email": "sigdre@example.com",
        "phone": "+1 800-555-1212",
        "meal": "burrito",
        "comment": "No sour cream please",
        "team_channel": "C0LFFBKPB",
        "who_should_sing": "U0MJRG1AL"
    },
    "callback_id": "employee_offsite_1138b",
    "team": {
        "id": "T1ABCD2E12",
        "domain": "coverbands"
    },
    "user": {
        "id": "W12A3BCDEF",
        "name": "dreamweaver"
    },
    "channel": {
        "id": "C1AB2C3DE",
        "name": "coverthon-1999"
    },
    "action_ts": "936893340.702759",
    "token": "M1AqUUw3FqayAbqNtsGMch72",
    "response_url": "https://hooks.slack.com/app/T012AB0A1/123456789/JpmK0yzoZDeRiqfeduTBYXWQ"
}

Let's look deeper at those attributes, which you might recognize many of from interactive messages.

  • type - to differentiate from other interactive components, look for the string value dialog_submission.
  • submission - a hash of key/value pairs representing the user's submission. Each key is a name field your app provided when composing the form. Each value is the user's submitted value, or in the case of a static select menu, the value you assigned to a specific response. The selection from a dynamic menu, the value can be a channel ID, user ID, etc.
  • callback_id - this value is the unique callback_id identifier your app gave this instance of the dialog.
  • team - this simple hash contains the id and name of the workspace from which this interaction occurred
  • user - this simple hash contains the id and name of the user who completed the form
  • channel - this simple hash contains the id and name of the channel or conversation where this dialog was completed
  • action_ts - this is a unique identifier for this specific action occurrence generated by Slack. It can be evaluated as a timestamp with milliseconds if that is helpful to you.
  • token - the verification token shared between your app and Slack used to validate an incoming request originates from Slack.
  • response_url - the URL can be used to post responses to dialog submissions.

The token value's verification token is important. Use it to confirm the payload is in fact sent from Slack. If you don't store your verification token yet, find it in the App Credentials section of your app's management console.

Input validation

While Slack will handle some client-side validation of user input upon submission, we encourage your app to do its own round of validation based on its own business logic.

As soon as your user hits the submit button, Slack client will validate the user's inputs against the validation parameters that you have passed to make sure that all the required fields are filled and the formats are correct. And the form won't submit until the user corrects all errors.

Dialog client validation

You should validate the submission values you receive server-side against your own heuristics.

If your app finds any errors with the submission, respond with an application/json payload describing the elements and error messages. The API returns these errors to the user in-app, allowing the user to make corrections and submit again.

Example error message payload:

{
  "errors": [
    {
      "name": "email_address",
      "error": "Sorry, this email domain is not authorized!"
    },
    {
      "name": "username",
      "error": "Uh-oh. This username has been taken!"
    }
  ]
}

name and error are both strings. name correlates to one of your dialog's named fields and the error is a short string describing how you found the response inadequate.

When the submission is without exception, your app must respond with 200 OK with an empty body. This will complete the dialog.

Following up

After you've confirmed the submission is valid and sent a HTTP 200 OK message, you'll want to let the user know everything is "jake."

Use a method like chat.postMessage or chat.postEphemeral, depending on context and desired visibility, to create a message thanking the user and/or providing other feedback. You can also send a delayed response to the response_url if you need more than 3 seconds to respond. with the response_url, your app can continue interacting with users up to 5 times within 30 minutes of the action invocation.

For example, if you are building an app for survey, it is still a good idea to send a follow-up message to make sure your valuable user knows the form submission was successful!

Adapting existing workflows to dialogs

Existing workflows using only conversation, message buttons, or message menus can be enhanced with the focused concentration made possible with dialogs.

Imagine a /helpdesk slash command connected to a company's internal IT helpdesk. With very limited syntax enforcement and the wild possibilities inherent in free-form text, filing a ticket is imprecise and less instantly actionable.

/helpdesk hardware "help my cat chewed on my mouse cable it doesn't work anymore"

Commands like this are noble in purpose but could be made more precise in execution with dialogs.

Your software could easily interpret this as a ticket meant for a hardware category. With some heuristics and analysis, your app might derive that it's about a mouse.

Without asking a series of focused questions to illuminate the entire surface area of the user's inquiry, the user's submission is significantly less actionable than its potential.

By invoking a dialog, you may ask specific guided questions around urgency, platform, location, and other nuances made possible with multiple choice.

Another example, illustrated in the animation below, demonstrates a slash command execution leading to an optimized dialog and useful outcome.

Demo of a slash command leading to a dialog submission

Recent changes

We introduced key new features to Dialogs:


Your dialogs can be short and to the point, a focused way to collect a single piece of information. They can be long, asking questions requiring more thought, research, and careful response. Chain dialogs together with buttons or menus to serialize phased workflow execution. Maintain a pleasant user experience throughout your experimentation by following our best practices.