Skip to main content

Legacy dialogs

Dialogs are designed to help you gather multi-part input from end-users in a structured way; think, for example, of a helpdesk ticket that contains a freeform description of an issue, but also requires some category labels for triage.

They're best used for quick, contextual work: brief tasks that get some benefit from being completed in the context of a channel or conversation.

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.

Anatomy of a dialog

Dialogs appear as a modal window inside Slack. Within that modal, you can add one or many input fields in the form of single line text inputs, text areas, and dropdowns.

Anatomy of a dialog

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.

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": "C123ABC456"
}
]
}
],
"callback_id": "select_simple_1234",
"team": {
"id": "T0123ABC456",
"domain": "pocket-calculator"
},
"channel": {
"id": "C0123ABC456",
"name": "general"
},
"user": {
"id": "U123ABC456",
"name": "musik"
},
"action_ts": "1481579588.685999",
"message_ts": "1481579582.000003",
"attachment_id": "1",
"token": "iUeRJkkRC9RMMvSRTd8gdq2m",
"response_url": "https://hooks.slack.com/actions/T0123ABC456/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=C123ABC456
channel_name=test
user_id=U123ABC456
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 your 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",
"notify_on_cancel": true,
"state": "Limo",
"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 form you might create could be modeled in JSON as:

{
"callback_id": "ryde-46e2b0",
"title": "Request a Ride",
"submit_label": "Request",
"notify_on_cancel": true,
"state": "Limo",
"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
Authorization: Bearer xoxb-such-and-such
Content-type: application/x-www-form-urlencoded

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%22notify_on_cancel%22%3Atrue%2C%22state%22%3A%22Limo%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.

AttributeTypeDescription
titleStringUser-facing title of this entire dialog. 24 characters to work with and it's required.
callback_idStringAn identifier strictly for you to recognize submissions of this particular instance of a dialog. Use something meaningful to your app. 255 characters maximum. Don't use this ID to reference sensitive data; use the more expansive state parameter below for that. Absolutely required.
elementsArrayUp to 10 form elements are allowed per dialog. See elements below. Required.
stateStringAn optional string that will be echoed back to your app when a user interacts with your dialog. Use it as a pointer to reference sensitive data stored elsewhere.
submit_labelStringUser-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. 48 characters maximum, and may contain only a single word.
notify_on_cancelBooleanDefault is false. When set to true, we'll notify your request URL whenever there's a user-induced dialog cancellation.

Dialog form elements

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. Otherwise, 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 to either email, number, tel, or url, where the default is a plain text. Slack will ignore subtypes that don't match one of those options.

Setting the subtype is useful for mobile Slack clients where it will trigger special keyboards. For example, when a form field expects a phone number, you should use tel as a subtype so it invokes the numeric keypad.

Adding a subtype does not enforce any validation on the field it is added to.

Dialog text element subtypes

Text element attributes

ElementTypeDescription
labelStringLabel displayed to user. Required. 48 character maximum.
nameStringName of form element. Required. No more than 300 characters.
typeStringThe type of form element. For a text input, the type is always text. Required.
max_lengthIntegerMaximum input length allowed for element. Up to 150 characters. Defaults to 150.
min_lengthIntegerMinimum input length allowed for element. Up to 150 characters. Defaults to 0.
optionalBooleanProvide true when the form element is not required. By default, form elements are required.
hintStringHelpful text provided to assist users in answering a question. Up to 150 characters.
subtypeStringA subtype for this text input. Accepts email, number, tel, or url. In some form factors, optimized input is provided for this subtype.
valueStringA default value for this field. Up to 150 characters.
placeholderStringA 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.

An example:

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

Textarea element attributes

AttributeTypeDescription
typeStringFor a text area, the type is always textarea. It's required.
labelStringLabel displayed to user. Required. No more than 48 characters.
nameStringName of form element. Required. No more than 300 characters.
placeholderStringA string displayed as needed to help guide users in completing the element. 150 character maximum.
max_lengthIntegerMaximum input length allowed for element. 0-3000 characters. Defaults to 3000.
min_lengthIntegerMinimum input length allowed for element. 1-3000 characters. Defaults to 0.
optionalBooleanProvide true when the form element is not required. By default, form elements are required.
hintStringHelpful text provided to assist users in answering a question. Up to 150 characters.
subtypeStringA subtype for this text area, just in case you need a lot of space for them. email, number, tel, or url
valueStringA 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 hashes (see below) in the form element's options field, (or an option_groups array).

These select elements have a data_source attribute which specifies whether you want a static list (static, which is the default) or a dynamic list (see below), but let's take a look at a static list first:

Example select form element definition:

{
"label": "Meal preferences",
"type": "select",
"name": "meal_preferences",
"options": [
{
"label": "Vegan",
"value": "vegan"
},
{
"label": "Kosher",
"value": "kosher"
},
{
"label": "Just put it in a burrito",
"value": "burrito"
} 
]
}

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 types:

Data source typeDescription
usersThe element will be populated with options corresponding to the users available to the current user.
channelsThe element will be populated with options corresponding to the public channels available to the current user.
conversationsThe element will be populated with options corresponding to the public channels, private channels, DMs, and MPIMs available to the current user.
externalThe 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

A list of options can be loaded from an external URL and used in your dialog menus.

First a little bit of setup is required to configure the URL that the options will be loaded from:

  • Go to the app settings page
  • Pick your app
  • Navigate to the Interactive Components section
  • Insert the Options Load URL in the Message Menus section

If you've already configured an Options Load URL for other uses, you'll be able to use the type and callback_id as below to determine which options data you want to return.

Now you can create a dialog with a select menu where the data_source is set to external:

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

As soon as a user clicks or taps the drop-down menu, a request like the following is sent to your specified URL:

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

From the external URL, a list of options (or an option_groups array) should be returned as a HTTP 200 response: