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

Workflows

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 and implemented in your app's manifest.

Before we begin, import DefineWorkflow at the top of your manifest file:

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

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

export 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 Optional output parameters, covered in "Defining 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 manifest file:
import { Schema } from "deno-slack-sdk/mod.ts";
  1. Call the function with your workflow's addStep method:
// 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,
});

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

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:

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

Finally, declare your workflow in your app's manifest definition at the bottom of your manifest file:

// manifest.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",
    ],
});

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:

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 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 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 { SomeFunction } from "../functions/some_function.ts";

export 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 = MyWorkflow.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: myFunctionResult.outputs.exampleOutput, // This comes from your function definition
});
// Example: invoking a custom function as a step in a workflow
MyWorkflow.addStep("#/functions/some_function", {
    someInputVariable: MyWorkflow.inputs.someInput
});

Working example

Let's take a look at a fully functional manifest.ts file that contains one workflow definition, its implementation, and a completed manifest definition:

// manifest.ts

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

// Define the workflow:
export const SayHelloWorkflow = DefineWorkflow({
    callback_id: "say_hello_workflow",
    title: "Say Hello"
});

// Implement the workflow:
SayHelloWorkflow.addStep(Schema.slack.functions.SendDm, {
  user_id: "U123ABC456", // Put your user ID here and the app will DM you
  message: "Hello, world!",
});

export default Manifest({
  name: "say-hello-app",
  description: "A demo of a Hello World workflow.",
  icon: "assets/icon.png",
  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 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, 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.

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

// 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.

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.

Onward

Once you have defined a workflow, you're ready to create a trigger that invokes it. You can also learn more about creating a datastore to store and retrieve information, or building custom types for your data.