Slack is where most teams spend the majority of their working day — which makes it one of the most effective surfaces for deploying AI agents. An agent embedded in Slack can answer questions in context, route alerts to the right channel, surface CRM data on demand, and run workflows triggered by a simple message or slash command.
This guide covers everything from Slack App creation to production-ready Python code, no-code options, and real-world agent patterns.
What AI Agents Can Do With Slack Access#
Connecting an AI agent to Slack unlocks a broad range of capabilities:
Messaging and notifications
- Post alerts to channels when monitored conditions are met (deal closed, error threshold crossed, new support ticket)
- Send direct messages to specific team members with context-aware notifications
- Format messages using Slack Block Kit for rich, actionable cards with buttons and dropdowns
Reading and understanding channel content
- Read channel history to answer questions like "what did the team decide about X last week?"
- Monitor a channel for keywords and trigger actions when specific topics are mentioned
- Summarize long threads on demand
Interactive workflows
- Respond to slash commands (
/ask,/report,/status) with agent-generated answers - Present approval prompts where humans can click Approve or Reject before an agent takes a high-stakes action
- Guide users through multi-step forms using Slack modals
Event-driven automation
- Trigger full agent pipelines when someone is mentioned, when a specific emoji reaction is added, or when a file is uploaded
- Route incoming alerts from external systems into the right Slack channel with intelligent categorization
For a broader look at what agents can do across business workflows, see AI agent examples in business.
Setting Up Your Slack App#
Step 1: Create the Slack App#
- Go to api.slack.com/apps and click Create New App
- Choose From scratch
- Name your app (e.g., "AI Assistant Agent") and select your workspace
- Click Create App
Step 2: Configure Bot Token Scopes#
In your app settings, go to OAuth & Permissions → Scopes → Bot Token Scopes.
Add the following scopes based on your agent's needs:
| Scope | What it enables |
|---|---|
| chat:write | Post messages to channels |
| channels:history | Read messages in public channels |
| channels:read | List public channels |
| app_mentions:read | Receive events when the bot is @mentioned |
| im:write | Send direct messages |
| users:read | Look up user information by ID |
| commands | Register slash commands |
Step 3: Install the App to Your Workspace#
Under OAuth & Permissions, click Install to Workspace. After authorizing, copy the Bot User OAuth Token (starts with xoxb-). Store this securely in your environment variables.
Step 4: Get the Signing Secret#
Under Basic Information → App Credentials, copy the Signing Secret. This is used to verify that incoming webhook requests are genuinely from Slack.
Option 1: No-Code Integration with n8n#
Best for: Teams who want Slack automation without writing Python
n8n's Slack nodes let you build complete agent workflows visually.
Setting Up a Slack Notification Workflow in n8n#
- Create a new n8n workflow
- Add a Schedule Trigger (or a Webhook node if you want event-driven triggers)
- Add your data source nodes (HTTP Request to an API, database query, etc.)
- Add an AI Agent node — configure it with your OpenAI API key and a system prompt
- Add a Slack node, set action to Post Message, configure with your bot token
- Connect: Trigger → Data Source → AI Agent → Slack
For example, a daily deal summary agent workflow would:
- Trigger at 8:00 AM via Schedule
- Fetch open deals from HubSpot via HTTP Request
- Pass deal data to the AI Agent node with a prompt: "Summarize these deals and flag any at risk"
- Post the summary to
#sales-daily-briefingvia the Slack node
No-Code Setup with Lindy AI#
Lindy AI provides pre-built Slack triggers and actions. To build an alert routing agent:
- Create a new Lindy agent
- Set trigger to: Slack message received in #alerts
- Add your system prompt:
When you receive an alert message:
1. Classify the alert severity: critical, warning, or informational
2. Identify the relevant team: engineering, support, or operations
3. Route critical alerts to the appropriate on-call channel
4. For informational alerts, add a checkmark emoji reaction and summarize in a thread
- Add Slack as an action (send message, add reaction)
- Save and activate
Option 2: LangChain with Python#
Best for: Teams building custom agent logic with full control
Installation#
pip install langchain langchain-openai slack-sdk python-dotenv flask
Create a .env file:
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_SIGNING_SECRET=your-signing-secret
OPENAI_API_KEY=sk-...
Build Slack Tools for Your Agent#
import os
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
from langchain.tools import tool
from dotenv import load_dotenv
load_dotenv()
slack_client = WebClient(token=os.getenv("SLACK_BOT_TOKEN"))
@tool
def send_slack_message(channel: str, message: str) -> str:
"""
Send a message to a Slack channel or user.
Use channel names like '#general' or user IDs like 'U01234567'.
"""
try:
response = slack_client.chat_postMessage(channel=channel, text=message)
return f"Message sent to {channel}. Timestamp: {response['ts']}"
except SlackApiError as e:
return f"Failed to send message: {e.response['error']}"
@tool
def read_channel_history(channel_id: str, message_count: int = 20) -> str:
"""
Read the recent message history of a Slack channel.
Pass the channel ID (e.g., 'C01234567'), not the channel name.
Returns up to message_count recent messages.
"""
try:
result = slack_client.conversations_history(channel=channel_id, limit=message_count)
messages = result["messages"]
if not messages:
return f"No messages found in channel {channel_id}"
formatted = []
for msg in messages:
user = msg.get("user", "bot")
text = msg.get("text", "")
ts = msg.get("ts", "")
formatted.append(f"[{ts}] {user}: {text}")
return "\n".join(formatted[::-1]) # Reverse to show oldest first
except SlackApiError as e:
return f"Failed to read channel: {e.response['error']}"
@tool
def get_channel_id(channel_name: str) -> str:
"""
Look up a Slack channel ID by its name (without the # symbol).
Returns the channel ID needed for other Slack operations.
"""
try:
result = slack_client.conversations_list(types="public_channel,private_channel")
for channel in result["channels"]:
if channel["name"] == channel_name:
return f"Channel ID for #{channel_name}: {channel['id']}"
return f"No channel found with name: {channel_name}"
except SlackApiError as e:
return f"Failed to list channels: {e.response['error']}"
@tool
def add_reaction(channel_id: str, message_timestamp: str, emoji_name: str) -> str:
"""Add an emoji reaction to a Slack message. emoji_name should be without colons, e.g. 'white_check_mark'."""
try:
slack_client.reactions_add(channel=channel_id, timestamp=message_timestamp, name=emoji_name)
return f"Reaction :{emoji_name}: added to message {message_timestamp}"
except SlackApiError as e:
return f"Failed to add reaction: {e.response['error']}"
Build the Question-Answering Agent#
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
llm = ChatOpenAI(model="gpt-4o", temperature=0)
tools = [send_slack_message, read_channel_history, get_channel_id, add_reaction]
prompt = ChatPromptTemplate.from_messages([
("system", """You are a helpful team assistant embedded in Slack.
You have access to Slack tools to:
- Read channel history to answer questions about past discussions
- Send messages to channels or users with relevant information
- Add reactions to acknowledge messages
When asked a question, first check if recent channel history is relevant.
Keep your responses concise and Slack-appropriate — avoid lengthy markdown that renders poorly."""),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True, max_iterations=5)
result = executor.invoke({
"input": "What was discussed in #product-updates recently? Post a 3-bullet summary to #exec-digest."
})
print(result["output"])
This demonstrates tool calling in AI agents in action — the agent decides independently whether to read channel history first or post immediately.
Triggering Agent Workflows from Slack Slash Commands#
Slash commands let your team invoke an agent with a simple /ask-agent [question] in any channel.
Step 1: Register the Slash Command#
In your Slack App settings, go to Slash Commands → Create New Command:
- Command:
/ask - Request URL:
https://your-server.com/slack/ask(must be HTTPS) - Short description: "Ask the AI agent a question"
Step 2: Build the Flask Handler#
from flask import Flask, request, jsonify
import threading
import requests as http_requests
from slack_sdk.signature import SignatureVerifier
app = Flask(__name__)
verifier = SignatureVerifier(os.getenv("SLACK_SIGNING_SECRET"))
def run_agent_async(question: str, response_url: str):
"""Run the agent in a background thread and post result via response_url."""
result = executor.invoke({"input": question})
answer = result["output"]
# Post the response back to Slack asynchronously
http_requests.post(response_url, json={
"response_type": "in_channel",
"text": answer
})
@app.route("/slack/ask", methods=["POST"])
def handle_slash_command():
# Verify the request is from Slack
if not verifier.is_valid_request(request.get_data(), request.headers):
return jsonify({"error": "Invalid request signature"}), 403
question = request.form.get("text", "")
response_url = request.form.get("response_url")
user_name = request.form.get("user_name", "unknown")
if not question:
return jsonify({"text": "Please provide a question after /ask"})
# Acknowledge immediately (Slack requires response within 3 seconds)
# Then run the agent asynchronously
thread = threading.Thread(
target=run_agent_async,
args=(f"{user_name} asks: {question}", response_url)
)
thread.start()
return jsonify({"text": f"Processing your question: _{question}_\nI'll respond shortly..."})
if __name__ == "__main__":
app.run(port=3000)
Real-Time Event Listening with Slack Events API#
For agents that need to react to Slack activity in real time, configure the Events API.
Step 1: Enable Events#
In your Slack App settings, go to Event Subscriptions → Enable Events.
Set the Request URL to your endpoint (e.g., https://your-server.com/slack/events).
Step 2: Subscribe to Events#
Under Subscribe to bot events, add:
app_mention— fires when someone @mentions your botmessage.channels— fires on every message in public channels the bot has joined
Step 3: Build the Event Handler#
@app.route("/slack/events", methods=["POST"])
def handle_events():
data = request.json
# Handle Slack's URL verification challenge
if data.get("type") == "url_verification":
return jsonify({"challenge": data["challenge"]})
# Verify signature
if not verifier.is_valid_request(request.get_data(), request.headers):
return jsonify({"error": "Invalid signature"}), 403
event = data.get("event", {})
# Ignore messages from bots (including this bot itself) to prevent loops
if event.get("bot_id") or event.get("subtype") == "bot_message":
return jsonify({"ok": True})
if event.get("type") == "app_mention":
# Extract the question (remove the bot mention)
text = event.get("text", "")
channel = event.get("channel")
thread_ts = event.get("ts")
# Remove the @mention from the text
import re
question = re.sub(r"<@[A-Z0-9]+>", "", text).strip()
def respond_in_thread():
result = executor.invoke({"input": question})
slack_client.chat_postMessage(
channel=channel,
text=result["output"],
thread_ts=thread_ts # Reply in the same thread
)
threading.Thread(target=respond_in_thread).start()
return jsonify({"ok": True})
Rate Limits and Best Practices#
Slack enforces rate limits per method and per workspace:
| Method tier | Rate limit | Common methods |
|---|---|---|
| Tier 1 | 1 request/minute | channels.list |
| Tier 2 | 20 requests/minute | conversations.history |
| Tier 3 | 50 requests/minute | chat.postMessage |
| Tier 4 | 100+ requests/minute | users.info |
Best practices for production agents:
- Batch messages: If posting multiple updates, combine them into one
chat.postMessagecall using Block Kit sections - Cache channel IDs: Don't call
conversations.listrepeatedly — look up channel IDs once and store them - Use threading: Always reply in a thread for agent responses to avoid cluttering channels
- Handle retries gracefully: Catch
SlackApiErrorwith error coderatelimitedand respect theRetry-Afterheader - Limit concurrent agents: If many users trigger the agent simultaneously, use a task queue (Celery, Redis Queue) to prevent hitting rate limits
Alert Routing Use Case#
A practical pattern: an AI agent that receives alerts from external monitoring systems and routes them intelligently to the right Slack channel:
alert_routing_prompt = ChatPromptTemplate.from_messages([
("system", """You are an alert routing agent.
When you receive an alert:
1. Classify it: 'infrastructure', 'security', 'application', or 'business'
2. Assess severity: 'critical' (page someone), 'warning' (notify channel), 'info' (log only)
3. Route critical infrastructure alerts to #on-call-engineering
4. Route critical security alerts to #security-incidents
5. Route critical business alerts to #exec-alerts
6. Route warnings to the relevant team channel
7. For info-level alerts, do not send a Slack message — just confirm routing decision"""),
("human", "Alert received: {alert_text}"),
("placeholder", "{agent_scratchpad}"),
])
This kind of routing agent is one of the most common patterns described in our AI agent workflow examples.
Next Steps#
With your Slack integration working, explore these related resources:
- LangChain Tutorial — Deep dive into building agents with LangChain
- AI Agents for Customer Service — Customer-facing Slack bot patterns
- AI Agent Examples in Business — Real Slack agent implementations
- Best AI Agent Platforms 2026 — Compare Lindy, n8n, LangChain, and more
- What Are AI Agents? — Foundational concepts if you're new to agents