You must enable javascript in order to use the Slack API Documentation. You can do this in your browser settings.
Go to Slack

Interacting with users through dialogs

Presented to users within a modal window, Dialogs provide a focused workflow to quickly collect information from users.

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. Most importantly, form submissions to your app's Request URL do not include a response_url.

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

Since dialogs require a Slack app and legacy custom integrations are not supported, you'll need to create, configure, and install a Slack app before getting started with dialogs. Maybe you've done this already.

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 takes URL-encoded parameters as arguments. 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 form elements text and textarea may contain a subtype.

For a superb user experience, use the correct element type for each field. For example, use number for numerical input like a phone number. This is especially helpful when your users are on a mobile device.

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. Required. Must be text, textarea, or select.
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.

Dialog textarea element

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-500 characters. Defaults to 150.
min_length Integer Minimum input length allowed for element. 1-500 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 500 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.

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",
    "placeholder": "Select a meal preference",
    "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"
        },
        {
            "label": "Other",
            "value": "other"
        }
    ]
}
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.
placeholder String A string displayed as needed to help guide users in completing the element. 150 character maximum.
options Array Provide up to 100 option element attributes. Required for this type.
optional Boolean Provide true when the form element is not required. By default, form elements are required.
value String Provide a default value for the select element. Must match a value presented in options.

Select option attributes

Each option you provide in a select form element must have both a label and value.

Option attribute Type Description
label String User-facing text for this option. 75 characters maximum. Required.
value String The string your app wants to "know" when this option is selected. 75 characters maximum. Absolutely required.

If you happen to provide an integer value attribute, we'll just parse it as a string and absolutely refuse to do any kind of maths for you.

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. Unlike interactive messages, there is no response_url with which to delay computation.

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"
    },
    "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"
}

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 select menu, the value you assigned to a specific response.
  • 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.

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 passes 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."

However, unlike interactive messages, dialog requests do not contain a response_url as a gateway to sending a new message.

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.

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

Known issues

  • Dynamic menus like those featured in message menus cannot yet be used with dialogs, though it's something we'd like to provide.

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.