Workflows BETA

    A workflow is a set of processing steps that are executed in order. Each step in a workflow is either a custom function that you define or a built-in function that’s part of slack.

    Workflows can be configured to run without any user input, or they can wait for input via form before continuing.


    Defining workflows

    Workflows are defined in the manifest/workflows directory and implemented in your app’s manifest.

    To add a new workflow that sends a greeting to a member of a workspace, create a new file in manifest/workflows called say-hello.js and import DefineWorkflow and Schema at the top of your workflow file:

    1
    
    const { DefineWorkflow } = require('@slack/bolt');
    

    Then, create a workflow definition. This is where we’ll set, at a minimum, the workflow’s title and its unique callback ID:

    1
    2
    3
    4
    
    const SayHelloWorkflow = DefineWorkflow({
        callback_id: "say_hello_workflow",
        title: "Say Hello"
    });
    

    The callback_id is a unique string that identifies this particular component of your app, and the title is the display name of the workflow that shows up in slugs, unfurl cards, and certain end-user modals. These are the only two required fields. Optionally, you can configure one or more of the following:

    Optional property Description
    description An optional string description of this workflow
    input_parameters Optional input parameters, covered in “Defining input parameters”
    output_parameters An object which describes one or more output parameters that will be returned by your function. This object follows the exact same pattern as input_parameters: top-level properties of the object define output parameter names, with the property values being an object that further describes the type and description of individual output parameters.

    Once you’ve defined your workflow, you’ll now have access to it’s addStep method, which is how you can call built-in and custom functions. The addStep method takes two arguments: first, the function you want to call, and second, any inputs you want to pass to that function. We’ll see examples of how to do both in the following sections.

    Using built-in functions in a workflow

    To use a built-in function, like SendMessage:

    1. Ensure that Schema from the SDK is imported in your workflow file:

    1
    
    const { Schema } = require('@slack/bolt');
    

    2. Call the function with your workflow’s addStep method:

    1
    2
    3
    4
    5
    
    // Example: taking the string output from a function and passing it to SendMessage
    SayHelloWorkflow.addStep(Schema.slack.functions.SendMessage, {
      channel_id: SomeWorkflow.inputs.channelId,
      message: SomeWorkflow.inputs.someString,
    });
    

    Here’s an example of adding a step that calls a built-in function:

    1
    2
    3
    4
    
    SayHelloWorkflow.addStep(Schema.slack.functions.SendMessage, {
        channel_id: "C1234567",
        message: "Hello, World!",
    });
    

    Here’s an example of a step that calls a custom function with a callback ID of my_function_callback_id:

    1
    2
    3
    
    SayHelloWorkflow.addStep("#/functions/my_function_callback_id", {
        some_input: 12345
    });
    

    3. Export your workflow at the bottom of the file to be imported into the manifest:

    1
    
    module.exports = { SayHelloWorkflow };
    

    4. Declare your workflow in your app’s manifest definition under the workflow property:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    // manifest/manifest.js
    const { SayHelloWorkflow } = require('./workflows/say-hello-workflow');
    
    module.exports = Manifest({
        name: "sayhello",
        description: "An app with an example workflow",
        workflows: [SayHelloWorkflow], // Add your workflow here
        botScopes: [
            "commands",
            "chat:write",
            "chat:write.public",
        ],
    });
    

    Using OpenForm in a workflow

    The only built-in function that has an additional requirement is OpenForm. When creating a workflow that will have a step to open a form, your workflow needs to include a required interactivity input parameter and the call to OpenForm must be the first step in the workflow.

    Here’s an example of a basic workflow definition using interactivity:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    const SayHelloWorkflow = DefineWorkflow({
        callback_id: "say_hello_workflow",
        title: "Say Hello to a user",
        input_parameters: {
            properties: {
                interactivity: {
                    type: Schema.slack.types.interactivity,
                }
            },
            required: ["interactivity"]
        }
    });
    
    module.exports = { SayHelloWorkflow };
    

    Visit this guide for more details and code examples of using OpenForm in your app.

    Using custom functions in a workflow

    To use a custom function that you define:

    1. Import the function in your workflow file where you are defining the workflow:

    1
    
    import { SomeFunction } from "../functions/some_function";
    

    2. Call your function, storing its output in a variable. Here you may also pass input parameters from the workflow into the function itself:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    import { SomeFunction } from "../functions/some_function";
    
    const SomeWorkflow = DefineWorkflow(
      callback_id: "some_workflow",
      title: "Some Workflow",
      input_parameters: {
        required: [],
        properties: {
          someString: {
            type: Schema.types.string,
            description: "Some string"
          },
          channelId: {
            type: Schema.slack.types.channel_id,
            description: "Target channel",
            default: "C1234567"
          }
        }
      }
    );
    
    const myFunctionResult = SomeWorkflow.addStep(SomeFunction, {
        // ... Pass along workflow inputs via SomeWorkflow.inputs
        // ... For example, SomeWorkflow.inputs.someString
    });
    
    module.exports = { SomeWorkflow };
    

    3. Use your function in follow-on steps. For example:

    1
    2
    3
    4
    5
    
    // Example: taking the string output from a function and passing it to SendMessage
    SomeWorkflow.addStep(Schema.slack.functions.SendMessage, {
      channel_id: SomeWorkflow.inputs.channelId,
      message: myFunctionResult.outputs.exampleOutput, // This comes from your function definition
    });
    
    1
    2
    3
    4
    
    // Example: invoking a custom function as a step in a workflow
    SomeWorkflow.addStep("#/functions/some_function", {
        someInputVariable: SomeWorkflow.inputs.someInput
    });
    

    Working example

    Let’s take a look at a fully functional Say Hello workflow and manifest.js file that contains one workflow definition, its implementation, and a completed manifest definition:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    // manifest/workflows/say-hello.js
    const { DefineWorkflow, Schema } = require('@slack/bolt');
    
    // Define the workflow:
    const SayHelloWorkflow = DefineWorkflow({
        callback_id: "say_hello_workflow",
        title: "Say Hello"
    });
    
    // Implement the workflow:
    SayHelloWorkflow.addStep(Schema.slack.functions.SendDm, {
      user_id: "U1234567890", // Put your user ID here and the app will DM you
      message: "Hello, world!",
    });
    
    module.exports = { SayHelloWorkflow };
    
    

    Below in the manifest, the SayHelloWorkflow is imported and passed in:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    // manifest/manifest.js
    const { Manifest } = require('@slack/bolt');
    const { SayHelloWorkflow } = require('./workflows/say-hello');
    
    module.exports = Manifest({
      name: "say-hello-app",
      description: "A demo of a Hello World workflow.",
      workflows: [SayHelloWorkflow],
      botScopes: ["commands", "chat:write", "chat:write.public"],
    });
    

    The above example uses the built-in function SendDm to send a direct message. There are many other built-in functions available to use, and you can also include custom functions that you define.

    To invoke a workflow, you need to create a trigger.


    Defining input parameters

    Workflows can pass information into both functions and other workflows that are part of its workflow steps. To do this, we define what information we want to bring into the workflow via its input_parameters property.

    A workflow’s input_parameters property has two sub-properties: required, which is how you can ensure that a workflow only executes if specific input parameters are provided, and properties, where you can list the specific parameters that your workflow accounts for. Any built-in type or custom type can be used.

    Adding an input parameter to a workflow

    Input parameters are listed in the properties property. Each input parameter must include a type and a description, and can optionally include a default value.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    
    // Workflow definition
    const SomeWorkflow = DefineWorkflow({
        callback_id: "some_workflow",
        title: "Some Workflow",
        input_parameters: { 
        required: [],
        properties: {
            exampleString: {
                type: Schema.types.string,
                description: "Here's an example string.",
            },
            exampleBoolean: {
                type: Schema.types.boolean,
                description: "An example boolean.",
                default: true,
            },
            exampleInteger: {
                type: Schema.types.integer,
                description: "An example integer.",
            },
            exampleChannelId: {
                type: Schema.slack.types.channel_id,
                description: "Example channel ID.",
            },
            exampleUserId: {
                type: Schema.slack.types.user_id,
                description: "Example user ID.",
            },
            exampleUsergroupId: {
                type: Schema.slack.types.usergroup_id,
                description: "Example usergroup ID.",
            },
            }
        }
    });
    

    Required parameters can be indicated by listing their names as strings in the required property of input_parameters. For example, here’s how we can indicate that a parameter named exampleUserId is required:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    // Workflow definition
    const SomeWorkflow = DefineWorkflow({
        callback_id: "some_workflow",
        title: "Some Workflow",
        input_parameters: { 
        required: ["exampleUserId"],
        properties: {
            exampleUserId: {
                type: Schema.slack.types.user_id,
                description: "Example user ID.",
            },
            }
        }
    });
    

    If a workflow is invoked and the required input parameters are not provided, the workflow will not execute.

    🗣 Got 2 minutes to provide some feedback? Fill out our Developer Survey and let us know what we’re doing well—and what you’d like to see us improve.


    Onward

    Once you have defined a workflow, you’re ready to create a trigger that invokes it.