How to Build a Slack Workflow Automation System
Automate business processes directly within Slack using workflows.
Jay Banlasan
The AI Systems Guy
Your team already lives in Slack. I built a slack workflow automation system that triggers business processes from Slack messages, commands, and reactions. Submit expenses with a slash command. Approve requests with a button click. No context switching to another tool.
Slack becomes the interface for your operations.
What You Need Before Starting
- Python 3.8+
- Slack bot token with appropriate scopes
- Slack socket mode or HTTP endpoint
- Backend services for each workflow
Step 1: Set Up the Slack Bot
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
app = App(token="xoxb-your-bot-token")
Step 2: Build Slash Command Workflows
@app.command("/expense")
def handle_expense(ack, command, client):
ack()
client.views_open(
trigger_id=command["trigger_id"],
view={
"type": "modal",
"callback_id": "expense_submit",
"title": {"type": "plain_text", "text": "Submit Expense"},
"submit": {"type": "plain_text", "text": "Submit"},
"blocks": [
{
"type": "input", "block_id": "amount",
"element": {"type": "number_input", "is_decimal_allowed": True, "action_id": "amount_input"},
"label": {"type": "plain_text", "text": "Amount ($)"}
},
{
"type": "input", "block_id": "category",
"element": {
"type": "static_select", "action_id": "category_select",
"options": [
{"text": {"type": "plain_text", "text": "Travel"}, "value": "travel"},
{"text": {"type": "plain_text", "text": "Software"}, "value": "software"},
{"text": {"type": "plain_text", "text": "Office"}, "value": "office"}
]
},
"label": {"type": "plain_text", "text": "Category"}
},
{
"type": "input", "block_id": "description",
"element": {"type": "plain_text_input", "action_id": "desc_input"},
"label": {"type": "plain_text", "text": "Description"}
}
]
}
)
@app.view("expense_submit")
def handle_submission(ack, body, client):
ack()
values = body["view"]["state"]["values"]
user = body["user"]["id"]
amount = values["amount"]["amount_input"]["value"]
category = values["category"]["category_select"]["selected_option"]["value"]
description = values["description"]["desc_input"]["value"]
client.chat_postMessage(
channel="#finance-approvals",
text=f"New expense from <@{user}>: ${amount} ({category})",
blocks=[
{"type": "section", "text": {"type": "mrkdwn",
"text": f"*Expense Request*\nFrom: <@{user}>\nAmount: ${amount}\nCategory: {category}\nDescription: {description}"}},
{"type": "actions", "elements": [
{"type": "button", "text": {"type": "plain_text", "text": "Approve"},
"style": "primary", "action_id": "approve_expense", "value": f"{user}|{amount}"},
{"type": "button", "text": {"type": "plain_text", "text": "Reject"},
"style": "danger", "action_id": "reject_expense", "value": f"{user}|{amount}"}
]}
]
)
Step 3: Handle Approval Actions
@app.action("approve_expense")
def handle_approve(ack, body, client):
ack()
approver = body["user"]["id"]
data = body["actions"][0]["value"].split("|")
requester, amount = data[0], data[1]
client.chat_postMessage(
channel=requester,
text=f"Your expense of ${amount} was approved by <@{approver}>."
)
client.chat_update(
channel=body["channel"]["id"],
ts=body["message"]["ts"],
text=f"Expense ${amount} from <@{requester}> - APPROVED by <@{approver}>"
)
@app.action("reject_expense")
def handle_reject(ack, body, client):
ack()
approver = body["user"]["id"]
data = body["actions"][0]["value"].split("|")
requester, amount = data[0], data[1]
client.chat_postMessage(
channel=requester,
text=f"Your expense of ${amount} was rejected by <@{approver}>."
)
Step 4: Add Reaction-Based Triggers
@app.event("reaction_added")
def handle_reaction(event, client):
if event["reaction"] == "white_check_mark":
channel = event["item"]["channel"]
ts = event["item"]["ts"]
result = client.conversations_history(channel=channel, latest=ts, inclusive=True, limit=1)
message = result["messages"][0]
if "action item:" in message.get("text", "").lower():
client.chat_postMessage(
channel=channel,
thread_ts=ts,
text=f"Action item marked complete by <@{event['user']}>."
)
Step 5: Run the Bot
if __name__ == "__main__":
handler = SocketModeHandler(app, "xapp-your-app-token")
handler.start()
Deploy with systemd or a process manager:
# /etc/systemd/system/slack-bot.service
# [Service]
# ExecStart=/usr/bin/python3 /path/to/slack_bot.py
# Restart=always
What to Build Next
Add a /standup command that collects daily standup updates asynchronously. Team members respond on their own time, and the bot compiles a summary at 10am. Async standups save meeting time.
Related Reading
- Building Your First Automation: A Complete Guide - automation fundamentals
- The Automation Decision Tree - which processes to put in Slack
- Why AI Operations, Not AI Tools - systems thinking for Slack automations
Want this system built for your business?
Get a free assessment. We will map every system your business needs and show you the ROI.
Get Your Free Assessment