Legacy Steps from Apps
We're still building and not all features are available quite yet. Enjoy this peek into the future!
Not ready for the future? Return to the past at api.slack.com.
We don't recommend using or creating Steps from Apps for the legacy workflow builder. Instead, build functions and triggers for modern workflows using our new automation platform. This tutorial is a great place to start. Alternatively, build a Bolt app that you host and customize it with custom steps for Bolt apps. Learn more about the deprecation or read our Survival Guide & FAQ.
Steps from apps provided an ability for Slack apps to create and process custom workflow steps. These Steps from Apps could be shared and used by anyone in Workflow Builder.
Use Steps from Apps to send data to external services, create tasks in project management systems, or update tickets. Follow our guide to learn how to build your own steps, custom tailored to your organization’s needs.
What are Steps from Apps and how do they relate to workflows?
A workflow is a multi-step process, where some of the steps are automated by Slack. Workflows are available for end-users in various locations in Slack.
Workflow Builder offers a set of tools to create workflows. There's no code involved, so anyone can create workflows by creating a flow of workflow steps.
In addition to the built-in steps, Slack apps can create and share their own custom Steps from Apps. Users can add these steps to their workflows in Workflow Builder. The app then handles execution of that step in a workflow. The app will receive appropriate context from the workflow, such as user inputs from earlier steps.
Learn more about how to create a Slack app.
If you hit this limit, you won't be able to publish any new workflows. For more information, read about choosing a trigger for your workflow.
Getting started with Steps from Apps
First, you'll need a Slack app. Let's get started.
Whether you have an existing app or a freshly minted one installed to your development workspace, you'll want to go to your app's settings for the next step.
Configure your app
There are some settings changes required for your app to create and use workflow steps.
The first thing you'll need to do, if you haven't already, is to enable interactivity in your app.
Go to the Interactivity & Shortcuts section and flip the switch to 'on'. You'll need to fill in the Request URL field with a URL capable of handling interaction payloads. You'll start receiving those payloads from use of your app's steps.
The next step involves subscribing to an event and adding a permission scope, and you can add these yourself as explained below. They will, however, also be automatically added to your app's settings when creating your first workflow step later in this guide.
You'll need to subscribe to an Events API type called workflow_step_execute
. This event will notify your app when a step it created has been used. Add it under the Event Subscriptions section of your app's settings.
Subscribing to the workflow_step_execute
event also requires that your app request a permission scope called workflow.steps:execute
. Add it in the OAuth & Permissions section, under Bot Token Scopes.
Organizations with admin-approved apps turned on will need to approve any re-authentication. Otherwise, only the user will be asked to go through the re-authentication flow – there will be no additional work for the administrator.
Create a workflow step
In your app's config, click on the Workflow Steps section. Select the toggle to opt-in — this will automatically add a few settings (such as permission scopes and event subscriptions) so that you can start creating workflow steps.
You'll see a Steps section, listing any steps you've already created in your app. Each app can create up to 10 steps. Click the Add Step button, you'll see a modal appear with fields to configure your app's new step:
- Step name - a descriptive name which is shown to the Workflow Builder user.
- Callback ID - a string that will act as a unique identifier for this particular step. It will be sent back to you with each action involving your step.
Click Save and you've created your app's first workflow step!
Your app's step will now be available to Workflow Builder users in workspaces where:
- Your app is installed.
- An owner or admin has enabled the Steps from Apps feature in the workspace or Enterprise Grid org's settings.
Prepare your app for Workflow Builder
Here is the flow that occurs when a user adds your app's step in Workflow Builder:
- User inserts your app's step to a workflow, your app receives a
workflow_step_edit
payload. - App uses the
trigger_id
from that payload to open a configurationview
modal. - User completes configuration modal and submits. Your app receives a
view_submission
payload. - App creates a
workflow_step
object, and makes a call toworkflows.updateStep
. - Step is successfully added to the user's workflow.
If you're already using one of these frameworks and you've got a step running, you can skip to keeping track of the workflows the use your steps. Otherwise, if you'd like to implement the flow yourself, let's dig in.
Receiving the workflow_step_edit
payload
At this point in the flow, an interaction payload will be sent to the interactive request URL configured above.
Read our guide to handling interaction payloads to understand how to receive and parse them. You can find a reference guide to the fields included in the workflow_step_edit
type payload here.
Opening a configuration modal
When someone building a workflow inserts your app's step, they will need a way to pass along information entered by end-users in earlier steps. We can use what we call a configuration modal to enable this.
Each field in your configuration modal should allow the builder to define the source for all input data that your app requires. The builder will be able to insert Handlebars-like {{variables}}
into any plain-text field to auto-populate output data from previous steps.
The configuration modal can also be used to configure data that doesn't come from end-users, but is defined only by the Workflow Builder user.
This type of modal can be used in much the same way as regular modals. The workflow_step_edit
payload mentioned above will contain a trigger_id
that is needed to open the modal.
Most existing Block Kit components can be used to define the configuration modal view
object.
There are a few differences between the use of regular modals and configuration modals:
- The
type
property in the modalview
will beworkflow_step
instead ofmodal
. - When the
submit_disabled
property is set totrue
the submit button will be disabled until the user has completed one or more inputs. - Apps cannot specify
title
,submit
or theclose
properties. - A configuration modal view can be updated as long as it remains open, but you cannot push new views to the modal view stack.
- Configuring users will see an option to Insert a variable below any plain-text input blocks in your configuration modal. This allows the configuring user to specify that the value for any given field should be equal to the value of something collected from an end-user in an earlier workflow step. See below for more about variables.
Saving the configuration data using Bolt
When a configuration modal is submitted, your app will be sent a view_submission
interaction payload.
If you're using Bolt, you can acknowledge and process this payload by using the save
callback in the WorkflowStep
class:
- Java
- JavaScript
- Python
import java.util.*;
import com.slack.api.model.workflow.*;
import static com.slack.api.model.workflow.WorkflowSteps.*;
static String extract(Map<String, Map<String, ViewState.Value>> stateValues, String blockId, String actionId) {
return stateValues.get(blockId).get(actionId).getValue();
}
WorkflowStep step = WorkflowStep.builder()
.callbackId("copy_review")
.edit((req, ctx) -> { return ctx.ack(); })
.save((req, ctx) -> {
Map<String, Map<String, ViewState.Value>> stateValues = req.getPayload().getView().getState().getValues();
Map<String, WorkflowStepInput> inputs = new HashMap<>();
inputs.put("taskName", stepInput(i -> i.value(extract(stateValues, "task_name_input", "task_name"))));
inputs.put("taskDescription", stepInput(i -> i.value(extract(stateValues, "task_description_input", "task_description"))));
inputs.put("taskAuthorEmail", stepInput(i -> i.value(extract(stateValues, "task_author_input", "task_author"))));
List<WorkflowStepOutput> outputs = asStepOutputs(
stepOutput(o -> o.name("taskName").type("text").label("Task Name")),
stepOutput(o -> o.name("taskDescription").type("text").label("Task Description")),
stepOutput(o -> o.name("taskAuthorEmail").type("text").label("Task Author Email"))
);
ctx.update(inputs, outputs);
return ctx.ack();
})
.execute((req, ctx) -> { return ctx.ack(); })
.build();
app.step(step);
const ws = new WorkflowStep('add_task', {
edit: async ({ ack, step, configure }) => { },
save: async ({ ack, step, view, update }) => {
await ack();
const { values } = view.state;
const taskName = values.task_name_input.name;
const taskDescription = values.task_description_input.description;
const inputs = {
taskName: { value: taskName.value },
taskDescription: { value: taskDescription.value }
};
const outputs = [
{
type: 'text',
name: 'taskName',
label: 'Task name',
},
{
type: 'text',
name: 'taskDescription',
label: 'Task description',
}
];
await update({ inputs, outputs });
},
execute: async ({ step, complete, fail }) => { },
});
def save(ack, view, update):
ack()
values = view["state"]["values"]
task_name = values["task_name_input"]["name"]
task_description = values["task_description_input"]["description"]
inputs = {
"task_name": {"value": task_name["value"]},
"task_description": {"value": task_description["value"]}
}
outputs = [
{
"type": "text",
"name": "task_name",
"label": "Task name",
},
{
"type": "text",
"name": "task_description",
"label": "Task description",
}
]
update(inputs=inputs, outputs=outputs)
ws = WorkflowStep(
callback_id="add_task",
edit=edit,
save=save,
execute=execute,
)
app.step(ws)
The update
function will make a call to the workflows.updateStep
Web API method. This method stores the configuration of your app's step for a specified workflow.
There are a couple of arguments that the update
function requires - input
and output
.
inputs
is a key-value map of objects describing data required by your app's step. Input values can contain Handlebars{{variables}}
, which are used to include values from the outputs of some earlier step in the workflow.outputs
is an array of objects that describe the data your app's step will return when completed. Subsequent steps in the workflow after your app's step will be able to use these outputs as variables.
Read the reference guides for input
objects and output
objects to learn their full structure.
You can’t use any other type of response_action
with configuration modals.
Then use the information collected from the configuration modal to form the arguments needed to call the Web API method workflows.updateStep
. This method will confirm and save the configuration for your app's step within this particular workflow.
The workflow will now be saved with your app's step nestled inside. Let's move on to what happens when someone uses that newly created workflow.
Handle usage of workflows containing your app's steps
When you switched on the Workflow Steps feature in your app, you were automatically subscribed to the workflow_step_execute
event. This event will be sent to your app every time a workflow containing one of the app's steps is used.
Using Bolt, you can listen for and respond to this event by adding an execute
callback to your WorkflowStep
class:
- Java
- JavaScript
- Python
import java.util.*;
import com.slack.api.model.workflow.*;
WorkflowStep step = WorkflowStep.builder()
.callbackId("copy_review")
.edit((req, ctx) -> { return ctx.ack(); })
.save((req, ctx) -> { return ctx.ack(); })
.execute((req, ctx) -> {
WorkflowStepExecution wfStep = req.getPayload().getEvent().getWorkflowStep();
Map<String, Object> outputs = new HashMap<>();
outputs.put("taskName", wfStep.getInputs().get("taskName").getValue());
outputs.put("taskDescription", wfStep.getInputs().get("taskDescription").getValue());
outputs.put("taskAuthorEmail", wfStep.getInputs().get("taskAuthorEmail").getValue());
try {
ctx.complete(outputs);
} catch (Exception e) {
Map<String, Object> error = new HashMap<>();
error.put("message", "Something wrong!");
ctx.fail(error);
}
return ctx.ack();
})
.build();
app.step(step);
const ws = new WorkflowStep('add_task', {
edit: async ({ ack, step, configure }) => { },
save: async ({ ack, step, update }) => { },
execute: async ({ step, complete, fail }) => {
const { inputs } = step;
const outputs = {
taskName: inputs.taskName.value,
taskDescription: inputs.taskDescription.value,
};
// if everything was successful
await complete({ outputs });
// if something went wrong
// fail({ error: { message: "Just testing step failure!" } });
},
});
def execute(step, complete, fail):
inputs = step["inputs"]
# if everything was successful
outputs = {
"task_name": inputs["task_name"]["value"],
"task_description": inputs["task_description"]["value"],
}
complete(outputs=outputs)
# if something went wrong
error = {"message": "Just testing step failure!"}
fail(error=error)
ws = WorkflowStep(
callback_id="add_task",
edit=edit,
save=save,
execute=execute,
)
app.step(ws)
The event payload will include inputs
that contain any values that your step requires. These inputs come from earlier in the workflow, or from something entered in the configuration modal when the workflow step was being inserted.
You can process information from the payload or use any other relevant data to complete the step. Your app can make API calls, publish to a database, switch a lightbulb on, refresh a user's App Home, or do anything else that is relevant to the step.
That said, within the execute
callback your app must either call complete()
to indicate that step execution was successful, or fail()
to indicate that step execution failed.
The complete
function expects an outputs
object, the shape of which is dictated by the outputs
array sent earlier in the WorkflowStep.save
callback. Use the name
from each object in that array as keys, mapped to the calculated value for each output.
If you're not using Bolt, read our guide to the Events API. This guide explains how to receive and handle event notifications, which includes workflow_step_execute
.
Non-Bolt apps also need to manually indicate the outcome of the step execution. Make a call to the Web API method workflows.stepCompleted
to indicate a successful outcome. A call to workflows.stepFailed
should come after a failed attempt to complete the step.
Activity
tab in Workflow Builder to inspect every run of your workflow. Any error messages for failed runs will appear there.Well done - you've now stepped through all the steps needed to step up your workflow step game!
Handling edits
Once your step has been added to a workflow, configuring users will have an option to customize your step through an Edit button in Workflow Builder.
Clicking that Edit button dispatches another workflow_step_edit
payload to your interactive request URL, and the user’s previously configured inputs.
Step tracking
Count your steps by subscribing to four new events: workflow_published
, workflow_unpublished
, workflow_deleted
, and workflow_step_deleted
.
These events notify your app when another workflow including a step powered by your app has been published, unpublished, or deleted, as well as when one of the steps is removed from a workflow. Use these events to keep track of which workflows make use of your steps.
Known issues
- There is a minor, visual issue with validation errors under input blocks.