How to Integrate AI Agents with Slack

Step-by-step guide to connecting AI agents with Slack. Learn how to send messages, read channels, listen to events, and trigger agent workflows from Slack slash commands using LangChain, CrewAI, Lindy AI, and n8n.

Before You Start

Prerequisites: Slack workspace with admin access, Slack App created in your workspace

Works with: LangChain, CrewAI, Lindy AI, n8n

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#

  1. Go to api.slack.com/apps and click Create New App
  2. Choose From scratch
  3. Name your app (e.g., "AI Assistant Agent") and select your workspace
  4. Click Create App

Step 2: Configure Bot Token Scopes#

In your app settings, go to OAuth & PermissionsScopesBot 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 InformationApp 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#

  1. Create a new n8n workflow
  2. Add a Schedule Trigger (or a Webhook node if you want event-driven triggers)
  3. Add your data source nodes (HTTP Request to an API, database query, etc.)
  4. Add an AI Agent node — configure it with your OpenAI API key and a system prompt
  5. Add a Slack node, set action to Post Message, configure with your bot token
  6. 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-briefing via the Slack node

No-Code Setup with Lindy AI#

Lindy AI provides pre-built Slack triggers and actions. To build an alert routing agent:

  1. Create a new Lindy agent
  2. Set trigger to: Slack message received in #alerts
  3. 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
  1. Add Slack as an action (send message, add reaction)
  2. 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 CommandsCreate 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 SubscriptionsEnable 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 bot
  • message.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.postMessage call using Block Kit sections
  • Cache channel IDs: Don't call conversations.list repeatedly — 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 SlackApiError with error code ratelimited and respect the Retry-After header
  • 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: