The new Slack platform and the features described below are in beta and under active development.

Forms

Build seamless data collection into your next-generation Slack app with forms, where information collected from a user can be mapped to the inputs of workflow steps.

If you're new to using forms, keep reading! If you're interested in modifying existing forms, jump ahead to the schema of form field elements.

Collecting input in a workflow

Let's dive into how to add the OpenForm built-in function to a workflow! Familiarizing yourself with our functions and workflows guides before continuing will help you ease into adding interactivity into your workflow.

1. Add interactivity to your workflow

First things first, let's take a look at the "Send a Greeting" workflow that comes with the Hello World sample app, available to you when you create a new Slack app.

Making your app interactive is the key to collecting user data. To accomplish this, an interactivity input parameter must be included as a workflow property in the workflow definition:

import { DefineWorkflow, Schema } from "deno-slack-sdk/mod.ts";

const GreetingWorkflow = DefineWorkflow({
  callback_id: "greeting_workflow",
  title: "Send a greeting."
  description: "Send a greeting to channel. Practice with the <https://api.slack.com/tutorials/tracks/hello-world|Hello World> tutorial.",
  input_parameters: {
    properties: {
      interactivity: {
        type: Schema.slack.types.interactivity,
      },
      channel: {
        type: Schema.slack.types.channel_id,
      },
    },
    required: ["interactivity"],
  },
});

Learn more about the interactivity type in our built-in types guide.

Notice that you can include a link in the description. Linking is only supported in the form description however, not the element description. For more about forms, read on!

2. Add a form to your workflow

Now that you've completed adding the interactivity property into your workflow, it's time to add OpenForm as a step in your workflow.

A workflow is a multi-step process, where some of the steps are automated; adding steps to your workflows are what makes your app so seamless. For example, say you need to collect user data, send it to your information systems, then update a Slack channel with a link to a report. Each task will need to be configured as a step in your workflow, allowing for user interactivity data to be passed to each step sequentially until this process is complete.

While some functions you use within your workflow will be custom functions, Slack has a library of built-in functions that cover some of the most common tasks executed on our platform.

The OpenForm built-in function allows for the collection of user input which can be used when executing follow-on steps in a workflow like sending messages, creating channels, or even pinning a message in a channel.

Add the OpenForm function as a step in your workflow:

const inputForm = GreetingWorkflow.addStep(
  Schema.slack.functions.OpenForm,
  {
    title: "Send a greeting", // Title of the form shown to the user
    description: "Send a greeting to a channel." // Description of the form shown to the user
    interactivity: GreetingWorkflow.inputs.interactivity,
    submit_label: "Send greeting", // Label of the button the user will click to submit the form
    fields: {
      elements: [{
        name: "recipient",
        title: "Recipient",
        type: Schema.slack.types.user_id,
      }, {
        name: "channel",
        title: "Channel to send message to",
        type: Schema.slack.types.channel_id,
        default: GreetingWorkflow.inputs.channel,
      }, {
        name: "message",
        title: "Message to recipient",
        type: Schema.types.string,
        long: true,
      }],
      required: ["recipient", "channel", "message"],
    },
  },
);

const greetingFunctionStep = GreetingWorkflow.addStep(
  GreetingFunctionDefinition,
  {
    recipient: inputForm.outputs.fields.recipient,
    message: inputForm.outputs.fields.message,
  },
);

// Send a message to a channel with the user's input
GreetingWorkflow.addStep(Schema.slack.functions.SendMessage, {
  channel_id: inputForm.outputs.fields.channel,
  message: inputForm.outputs.fields.message,
});

export default GreetingWorkflow;

Take note of the title, description, and submit_label. It is important be descriptive with these fields, as these are the first things the user will see once the workflow is triggered:

form-metadata

The fields of your form are made up of different types of elements that follow a certain schema. The type of each element determines the input type, and includes types such as string, boolean, timestamp, channel_id, and user_id.

Forms haves two output parameters:

  • user_id: User ID of the person who filled out the form
  • fields: The same field names in the inputs that are returned as outputs with the values as those entered by the user

Output parameters store user interactivity data and can be used to pass information to follow-on steps in the workflow.

The example above included an extra step: sending the users message to a specific channel that the user specified. Using the output parameter fields and selecting the desired output element by name i.e channel and message, the user's input data was passed into the second step of the workflow.

When using the OpenForm built-in function, either add it as the first step in your workflow or ensure the preceding step is interactive, as an interactive step will generate a fresh pointer to use for opening the form. For example, for a later step in your workflow, use the interactive button that can be added with the Send a message built-in function immediately before opening the form.

3. Manifest and deploy your workflow

With a workflow defined and steps outlined, it's time to make this an official part of your app! By adding the workflow definition to your manifest and then deploying your app, this new workflow can find a place in your workspace.

A manifest containing your newfound workflow will have the following additions:

import GreetingWorkflow from "./workflows/greeting_workflow.ts"; // First, import the new workflow into the manifest file

export default Manifest({
  name: "Workflow Messages",
  description: "A simple way to share thoughts",
  icon: "assets/icon.png",
  workflows: [GreetingWorkflow], // Then, add the newly imported workflow to your manifest here
  outgoingDomains: [],
  botScopes: ["commands", "chat:write", "chat:write.public"],
});

After updating the manifest, these changes can be deployed to a workspace using slack deploy!

4. Open an entry point with a trigger

Let's add some momentum behind your workflow and create a Link trigger using a trigger file. In the trigger definition, add interactivity as an input value; this value holds context about the user interactivity that triggered the trigger and passes it along to your workflow.

In a separate file, define your trigger in the following way:

import { Trigger } from "deno-slack-api/types.ts";
import GreetingWorkflow from "./workflows/greeting_workflow.ts";

const greetingTrigger: Trigger<typeof GreetingWorkflow.definition> = {
  type: "shortcut",
  name: "Send a greeting",
  description: "Send greeting to channel",
  workflow: "#/workflows/greeting_workflow",
  inputs: {
    interactivity: {
      value: "{{data.interactivity}}",
    },
    channel: {
      value: "{{data.channel_id}}",
    },
  },
};

export default greetingTrigger;

Run the following command to create the trigger in your CLI:

# create a trigger with the callback_id of your workflow and the trigger file
$ slack trigger create --trigger-def triggers/greeting_trigger.ts

...

⚡ Trigger created
   Trigger ID:   Ft024ABCD12X
   Trigger Type: shortcut
   Trigger Name: Send a greeting
   URL: https://slack.com/shortcuts/Ft024ABCD12X/c001a02b13c42de35f47b55a89aad33c

Viola! You now have a shortcut URL to share in a channel or save as a bookmark! This URL acts as an entry point into your workflow, kicking things off with the form created back in Step 2 when clicked.

Next, learn about the different field elements of a form below.

Form field element schema

Form elements have several properties you can customize depending on the element type:

Property Type Description
name String (required) The internal name of the element
title String Title of the form shown to the user. Maximum length is 25 characters
type Schema.slack.types.* The type of form element to display
description String (optional) Description of the form shown to the user
default Same type as type (optional) Default value for this field

For example, here is a form that collects a channel_id, a user_id, a number, and a date:

const inputForm = LogRunWorkflow.addStep(
  Schema.slack.functions.OpenForm,
  {
    title: "Log your run",
    interactivity: LogRunWorkflow.inputs.interactivity,
    submit_label: "Submit run",
    fields: {
      elements: [{
        name: "channel",
        title: "Channel to send to",
        type: Schema.slack.types.channel_id,
        default: LogRunWorkflow.inputs.channel,
      }, {
        name: "runner",
        title: "Runner",
        type: Schema.slack.types.user_id,
      }, {
        name: "distance",
        title: "Distance (in miles)",
        type: Schema.types.number,
      }, {
        name: "rundate",
        title: "Run date",
        type: Schema.slack.types.date,
      }],
      required: ["channel", "runner", "distance", "rundate"],
    },
  },
);

Form field element type parameters

The following parameters are available for each type when defining your form. Please note that some element types are prefixed with Schema.types, while some are prefixed with Schema.slack.types:

Type Parameters Notes
Schema.types.string title, description, default, minLength, maxLength, format, enum, choices, long, type If the long parameter is provided and set to true, it will render as a multi-line text box. Otherwise, it renders as a single-line text input field. In addition, basic input validation can be done by setting format to either email or url
Schema.types.boolean title, description, default, type A boolean rendered as a checkbox in the form
Schema.types.integer title, description, default, enum, choices, type, minimum, maximum A whole number, such as -1, 0, or 31415926535
Schema.types.number title, description, default, enum, choices, type, minimum, maximum A number that allows decimal points, such as 13557523.0005
Schema.types.array title, description, default, type, items, maxItems For the items parameter, Schema.types.string, Schema.slack.types.channel_id, and Schema.slack.types.user_id are supported. For Schema.types.string only, you can also specify enum and choices within the items type. maxItems allows you to specify the maximum number of items that can be selected. default must also be an array, or its values will be ignored
Schema.slack.types.date title, description, default, enum, choices, type A string containing a date, displayed in YYYY-MM-DD format
Schema.slack.types.timestamp title, description, default, enum, choices, type A Unix timestamp, rendered as a date picker
Schema.slack.types.user_id title, description, default, enum, choices, type A user picker
Schema.slack.types.channel_id title, description, default, enum, choices, type A channel picker
Schema.slack.types.rich_text title, description, default, type A way to nicely format messages in your app. Note that this type cannot be converted to other message types, such as a string

For each parameter listed above, type is required. For arrays, items is also required.

The following is an example array containing Schema.types.string types:

{
          name: "departments",
          title: "Your department",
          type: Schema.types.array,
          description: "You belong here.",
          items: {
            type: Schema.types.string,
            enum: ["marketing", "design", "sales", "engineering"],
          }
          default: ["sales", "engineering"]
 }

The enum and choices parameters should be provided together. They are used to create a drop-down that allows users to select an item from a list. For example:

const teammate = NewMemberIntake.addStep(
  Schema.slack.functions.OpenForm,
  {
    title: "Welcome to the team!",
    interactivity: NewMemberIntake.inputs.interactivity,
    submit_label: "Join",
    description: "There's a place for you here.",
    fields: {
      elements: [{
        name: "department",
        title: "What department are you in?",
        type: Schema.types.string,
        enum: ["marketing", "design", "sales", "engineering"],
        choices: [
          { value: "marketing", title: "Marketing", description: "Share the excitement"},
          { value: "design", title: "Design", description: "Create wonderful experiences" },
          { value: "sales", title: "Sales", description: "Solve problems with customers" },
          { value: "engineering", title: "Engineering", description: "Build systems for solutions" },
        ],
      }],
      required: ["department"],
    },
  },
);

Keep the curiosity flowing and check out our Block Kit Interactivity guide next, which covers bringing interactivity to custom functions!

Have 2 minutes to provide some feedback?

We'd love to hear about your experience with the new Slack platform. Please complete our short survey so we can use your feedback to improve.