Skip to main content

Build a Slackbot with Windmill Workflows

· 8 min read
Henri Courdent

Slackbots are great tools to save time and reduce the need for manual input. They are also useful if you want operational teams to interact with custom-made workflows.

Building a Slack commands handler requires a little bit of Windmill mastering, but it's also a perfect way to get familiar with Windmill, as some key features are used (scripts, worfklows, branches, approval steps).

Also, know that non-technical users can totally build such tools. I'm myself not a developer.

After this article

You will be able to build a Slackbot from which you can create events on your calendar, send emails and even generate them with Open-AI, all of this from Slack.

We've seen in a previous article how to connect Slack with Windmill and have briefly explored how to build a first slackbot for a single command (using a script).

Long story short, the first article explained how to connect Slack with Windmill and to create a /windmill command on Slack linked to a script hosted on Windmill. Handling several commands on your Slackbot uses just the same logic, but we'll use one single Windmill workflow instead of a script.

Workflows on Windmill are based on scripts. Workflows are the architecture that allows you to chain scripts with branches, loops etc. while connecting scripts' inputs to previous ouputs.

Windmill Workflows

As we will use only one master workflow to handle all Slack commands, branches will be key: one branch for one Slack command. Let's get started.

Connecting a first workflow handler to your workspace

Just like the slack integration tutorial, it all starts from your workspace settings. Once you connected Slack to your Windmill account, you will be offered to connect a script or flow to the /windmill Slack command. This time, pick "Create a flow to handle Slack commands".

Create Flow Handler

You will get to this workflow to handle Slack commands. When linked to the /windmill command from the Windmill workspace settings, it will react to it on Slack.

Before going deeper, let's take a look at this first workflow because its logic will be of use.

First Handler Workflow


There are two workflow inputs that are passed to your flow everytime a command is emitted after you have setup the Slack command integration in Windmill:

  • response_url: this is the url to be used by your flow to respond to the caller using the slack client. It will be automatically completed by the command once you've connected Slack to the Windmill workspace.
  • text: the text following the /windmill command and written on Slack. The following step will make sense of it.

Text parser

This script is used to dissect the text of the command. In the default example, it creates two variables: "command" and "input". You'll see later that we can play with it and create even more parameters.

export async function main(text_input: string): Output {
const tokenized: string[] = text_input?.split(' ') || [];
const command = tokenized[0] || 'help';
const input = tokenized.slice(1,).join(' ');

return { command, input };

interface Output {
command: string;
input: string;


Branches on Windmill are twofold: Branch all and Branch one. Here we'll use Branch one as they allow to execute one single branch based on a condition, with also a default branch executed if error.

Although you can theoretically condition branches with any criterium, on your commands handler we'll link them to the output of the parser that set apart the "command" from the Slack input (e.g. results.c.command === 'echo'). Here, each branch will match a behavior you expect from the Slackbot.

At last there is a default branch that will execute if issue or no condition fulfilled. By default this script publishes a Help message :

export async function main(response_url: string) {
await fetch(response_url, {
method: 'POST',
body: JSON.stringify({ text: getHelp() }),

function getHelp() {
const help = `Supported commands
help - prints this command
echo - prints input
return help;

With this example, you have the keys to build complex Slackbots from Windmill. In the following section, we'll see how to handle more parameters, and use approval steps to go back and forth between Slack and Windmill.

Enhance your Slackbot with more parameters, resources and approval steps

The goal of the current section is to give you more hints on the potentiality of the Slackbot. Below is a flow that was built for more custom needs. We kept it simple, but we can't even think of all the possibilities of slackbots built with Windmill.

Slack commands hander, 2nd example

Fine-tuning the parser to manage longer commands

The default parser of the first example has the advantage of being simple as it parses word by word. Since we want to give more comprehensive parameters (sentences etc.), I chose to manage the parameters following the command separated with " " instead of spaces, as shown with the following code (generated with Open AI, remember I'm not a developer):

export async function main(text_input: string): Output {
let tokenized: string[] = [];
if (text_input) {
let start = 0;
let inString = false;
for (let i = 0; i < text_input.length; i++) {
if (text_input[i] === '"') {
inString = !inString;
if (text_input[i] === ' ' && !inString) {
tokenized.push(text_input.slice(start, i).replace(/"/g, ''));
start = i + 1;
tokenized.push(text_input.slice(start).replace(/"/g, ''));
const command = tokenized[0] || 'help';
const input = tokenized[1];
const parameters1 = tokenized[2];
const parameters2 = tokenized[3];

return { command, input, parameters1, parameters2 };

With the following parser, if from Slack I write /windmill coolcommand "the 1st sentence" "the 2nd sentence" "the 3rd sentence", it will result in:

"input": "the 1st sentence",
"command": "coolcommand",
"parameters1": "the 2nd sentence",
"parameters2": "the 3rd sentence"

Interacting with resources

All commands (except the default one) of this slackbot deal with resources. Adding resources is easy on Windmill: many resource types are already available, or you can create your own resource type.

One specificity of triggering resources from Slack is that you have to let Windmill know Slack can interact with them.

You won't be able to have Slack interact with your resources and variables before adding them to the slack group that was automatically created by Windmill after you set up your Slack workspace on Windmill.

Allow resources to be triggered from Slack

To give the permission, go to "Resources" (and "Variables") menu, click on Share, Group and pick slack.

Share to slack group

One simplier way to handle permissions is to host resources and variables on a folder that is part of the group slack.

Share variable to folder

Share folder to group

Once your resources are added, you can choose to pre-load them (as the gcal auth here) or connect them with previous outputs if you want to make them variable depending on the query inputs.

Parameters flows

Human in the loop with approval steps

The example above chains simple scripts to make them into a powerful Slackbot:

To make it more convenient, we also introduce the command /windmill send to make marginal changes on the Open AI completion and send the email directly to a recipient.

Now you know how to articulate a workflow handler of Slack commands. The potential is unlimited as slackbots can basically behave as a trigger on any of your workflows. Your Slackbot will never be as efficient as the one you built yourself. Start now using the Hub for inspiration, and iterate on our cloud app.

Windmill Logo
Windmill is an open-source and self-hostable serverless runtime and platform combining the power of code with the velocity of low-code. We turn your scripts into internal apps and composable steps of flows that automate repetitive workflows.

You can self-host Windmill using a docker compose up, or go with the cloud app.