Steps from Apps are now deprecated!
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.
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.
This feature will only work for apps using our newest granular permission model. Learn more about how to create a Slack app.
There is a limit of 100 shortcut-triggered workflows per channel. 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.
First, you'll need a Slack app. If you don't have one, you may want to read more about how they work. If you just want to create one here and now, 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.
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.
Your app will need to be re-authenticated anywhere it is already installed after you add a new scope. Otherwise, users will not see your steps in the step library.
Please note: 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.
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:
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:
Here is the flow that occurs when a user adds your app's step in Workflow Builder:
workflow_step_edit
payload.trigger_id
from that payload to open a configuration view
modal.view_submission
payload.workflow_step
object, and makes a call to workflows.updateStep
.Good news: the Bolt frameworks for Javascript, Python, and Java handle all these details for you!
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.
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.
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:
type
property in the modal view
will be workflow_step
instead of modal
.submit_disabled
property is set to true
the submit button will be disabled until the user has completed one or more inputs.title
, submit
or the close
properties.When a configuration modal is submitted, your app will be sent a view_submission
interaction payload.
If you're using Bolt, you can easily acknowledge and process this payload by using the save
callback in the WorkflowStep
class:
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.
Saving configuration data manually
If you're not using Bolt, your app can manually acknowledge and close the modal view and show errors as usual. 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.
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:
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.
Handling workflow execution manually
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.
You can check the 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!
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.
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.