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

Workflows

Workflows are the combination of functions, executed in order. Remember:

  1. Functions (either built-in or custom) define the actions of your app
  2. Workflows are made up of those functions (⬅️ you are here)
  3. Triggers execute workflows

Depending on your use case, you'll want to acquant yourself with either built-in functions, custom functions, or both. Then continue here to learn how to implement them in a workflow.

Defining workflows

Workflows are defined in their own files within your app's /workflows directory and implemented in your app's manifest.

Before defining your workflow, import DefineWorkflow at the top of your workflow file:

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

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

// say_hello_workflow.ts
export const SayHelloWorkflow = DefineWorkflow({
  callback_id: "say_hello_workflow",
  title: "Say Hello",
});
Definition properties Description
callback_id A unique string that identifies this particular component of your app
title The display name of the workflow that shows up in slugs, unfurl cards, and certain end-user modals.
description (Optional) A string description of this workflow.
input_parameters (Optional) See Defining input parameters.
output_parameters (Optional) See Defining output parameters.

Once you've defined your workflow, you can then add functionality by calling built-in and custom functions. This is done with the addStep method, which takes two arguments:

  • the function you want to call
  • the inputs (if any) you want to pass to that function.

We'll see examples of how to call both types of functions in the following sections.


Using built-in functions in a workflow

Built-in functions are essentially Slack-native actions, like creating a channel or sending a message.

To use a built-in function, like SendMessage:

  1. Ensure that Schema from the SDK is imported in your workflow source file:
import { DefineWorkflow, Schema } from "deno-slack-sdk/mod.ts";
  1. Call the function with your workflow's addStep method:
export const SomeWorkflow = DefineWorkflow({
  callback_id: "some-workflow",
  title: "Some workflow",
  input_parameters: {
    properties: {
      channelId: { type: Schema.slack.types.channel_id },
      someString: { type: Schema.types.string },
    },
    required: [],
  },
});

// 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: SomeWorkflow.inputs.someString,
});
  1. Declare your workflow in your app's manifest definition at the bottom of your manifest file:
// manifest.ts
import { Manifest } from "deno-slack-sdk/mod.ts";
import { SayHelloWorkflow } from "./workflows/say_hello_workflow.ts";

export default Manifest({
  name: "sayhello",
  description: "A deno app with an example workflow",
  icon: "assets/icon.png",
  workflows: [SayHelloWorkflow], // Add your workflow here
  botScopes: ["commands", "chat:write", "chat:write.public"],
});

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

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 the interactivity input parameter
  • have the call to OpenForm be its first step

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

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

export 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"],
  },
});

✨ Visit the forms section for more details and code examples of using OpenForm in your app.


Using custom functions in a workflow

Custom functions are reusuable building blocks of automation of your own design.

To use a custom function that you have already defined:

  1. Import the function in your manifest, where you define the workflow:
import { SomeFunction } from "../functions/some_function.ts";
  1. Call your function, storing its output in a variable. Here you may also pass input parameters from the workflow into the function itself:
import { DefineWorkflow, Schema } from "deno-slack-sdk/mod.ts";
import { SomeFunction } from "../functions/some_function.ts";

export const SomeWorkflow = DefineWorkflow({
  callback_id: "some_workflow",
  title: "Some Workflow",
  input_parameters: {
    properties: {
      someString: {
        type: Schema.types.string,
        description: "Some string",
      },
      channelId: {
        type: Schema.slack.types.channel_id,
        description: "Target channel",
        default: "C1234567",
      },
    },
    required: [],
  },
});

const myFunctionResult = SomeWorkflow.addStep(SomeFunction, {
  // ... Pass along workflow inputs via SomeWorkflow.inputs
  // ... For example, SomeWorkflow.inputs.someString
});
  1. Use your function in follow-on steps. For example:
// 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: SomeFunction.outputs.exampleOutput, // This comes from your function definition
});

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 in to 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
  • 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 sub-property. Each input parameter must include a type and a description, and can optionally include a default value.

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

// Workflow definition
export 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:

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

// Workflow definition
export 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.

Defining output parameters

Because workflows are comprised of functions, a workflow's output_parameters can be the same types as a function's output_parameters. They can be any built-in type or a custom type.

Adding an output parameter to a workflow

Output parameters are listed in the properties sub-property of output_parameters.

An output parameter with a built-in type must include a type. An output parameter with a custom type must also include name. We also recommend you include a description for every parameter.

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

// Workflow definiton
export const SantaWorkflow = DefineWorkflow({
  callback_id: "santa_workflow",
  title: "Santa Workflow",
  input_parameters: {
    properties: {
      elfId: {
        type: Schema.slack.types.user_id,
        description: "Unique Id for each elf.",
      },
    },
    required: ["elfId"],
  },
  output_parameters: {
    properties: {
      elfStatus: {
        type: Schema.types.string,
        description: "The elf's toymaking status.",
      },
    },
    required: [],
  },
});

Please note that input_parameters and output_parameters are used when defining a function or workflow, whereas retrieving values will use inputs and outputs, respectively. inputs and outputs are also used when implementing the logic of a custom function.

Onward

➡️ To keep building your app, head to the triggers section to learn how to create a trigger that invokes a defined workflow.

You can also learn about creating a datastore to store and retrieve information, or building custom types for your data.

Have 2 minutes to provide some feedback?

We'd love to hear about your experience building modular Slack apps. Please complete our short survey so we can use your feedback to improve.