How to Connect Custom AI Agents with Slack

Connect custom AI agents with Slack

Tired of sitting at your desk waiting for your AI agent to finish their tasks? By integrating your agent with Slack, you can monitor it or request new tasks remotely.

AI agents represent a new paradigm in which LLMs can interact with other services to perform end-to-end tasks. Beyond the usual clients we are all used to, agents also work in the background writing code, processing datasets, or analyzing monitoring data.

In this article, you’ll discover several options to connect your custom AI agent to Slack, as well as some security best practices to keep in mind when deploying your integration to production.

We’ll also briefly cover other options for connecting LLMs to Slack, such as MCP servers or using Slack as an LLM client.

Connecting to Slack

There are multiple ways to connect an AI agent to your corporate Slack. Choosing the right one depends on your specific use case and the security risks you are willing to accept.

While we’ll focus on webhooks and the Slack API, we’ll cover a couple more options at the end of the article.

Slack offers incoming webhooks you can send events to via HTTP POST messages, which are displayed in a preconfigured channel. It’s a simple, convenient way to get status notifications and error alerts into Slack. However, these webhooks are completely unprotected. Anyone who knows your webhook URL can send a message.

On the other hand, the Slack API provides full platform functionality programmatically. You’ll have full flexibility to send messages to multiple channels and users, read conversations, and react to events like user commands. This API provides proper authentication via OAuth; however, it is a potential source of data leaking.

If you just want to integrate your LLM in Slack (like if it was an embedded window), you have the option to use the Slack native apps for LLM (like Slack for ChatGPT and Slack for Claude).

Finally, you have the option to use an MCP Server via official plugins. You’ll need to register a Slack App, like when using the Slack API.

In this table, you can review the four possibilities that we cover in this article:

Connection Security Notes
Incoming webhooks None, the webhook URL is the secret and can be compromised. Pros: Simplicity. Ideal for sending information to a single channel

Cons: It can’t send messages to DMs or multiple channels
Slack Web API API secured with tokens or OAuth.

It can be a source of data leakage, as the API allows reading and writing to channels.
Pros: Can send messages to DMs and multiple channels. Can react to messages and user inputs.
Cons: Slightly more config needed. Admin access is needed.
Slack Native Apps Limited Slack admin settings Pros: Ideal if you just want to access your LLM from Slack. 
Cons: Cannot react to events.
Slack MCP Server API secured with OAuth.

It can be a source of data leaking, as it allows reading and writing in channels.
Pros: Similar functionality to the Web API.

Cons: Extra config than Slack Web API. Admin access is needed.

We’ll cover the security implications later on.

Let’s start by learning how to send notifications to Slack, for example, a message to a Slack channel when Claude Code finishes a task. We’ll use webhooks first, then the Slack Web API. 

How-to: Send AI Agent Notifications via Slack Incoming Webhooks

Let’s start with a practical example: Sending a message to a Slack channel when Claude Code finishes a task.

Creating an Incoming Webhook

First, in Slack, go to the Slack Marketplace.

On the top menu, select Build.

Then, click on Create new App.

You will see a window where you can choose between building an app with a manifesto or from scratch. Select to build an App from scratch.

Select an App name and a Slack workspace (your Slack domain) to install it.

Now a screen appears with the connection data and basic information from your app. In the left sidebar, select Incoming Webhooks.

Activate the incoming webhook by clicking on the toggle at the top right:

Finally, click on Add New Webhook at the bottom of the page.

A new screen appears where you need to enter:

  • Your company workspace
  • The Slack channel that you want to link this incoming webhook to. 

After these steps, your webhook is ready. Let’s do a quick test.

Test Your Webhook

You will see a snippet block on the configuration screen that you can use to test the connection with curl. It will look something like this:

curl -X POST -H 'Content-type: application/json' --data '{"text":"Hello, World!"}' https://hooks.slack.com/services/UIEW98358295/JAS82352/YUnFmPKpwWl...

Run this command in a terminal, and instantly a new “Hello, World!” message will appear in your Slack channel.

Formatting the Text of Your Message

Now that you’re receiving the data, let’s glow it up to improve readability and clarity of purpose.

Slack provides a visual layout system called Block Kit, which lets you structure messages with:

  • Sections
  • Blocks
  • Headers
  • Buttons

On top of that, you can use the official Block Kit Builder to test the formatting and content of your structured message. 

Like the example above, you can try a curl call to preview the message and formatting of the alert:

curl -X POST "https://hooks.slack.com/services/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" \
  -H 'Content-type: application/json' \
  --data '{
    "blocks": [
      {
        "type": "header",
        "text": { "type": "plain_text", "text": "Task Complete" }
      },
      {
        "type": "section",
        "text": { "type": "mrkdwn", "text": "*Job:* Refactor index.py\n*Status:* Success\n*Duration:* 2m 14s" }
      },
      { "type": "divider" },
      {
        "type": "section",
        "text": { "type": "mrkdwn", "text": "```Refactor complete. 7 files changed.```" }
      }
    ]
  }'

Now, check the channel you configured earlier with the incoming webhook, and your formatted message will appear:

Preparing the AI Agent

For this example, we’ll use Claude Code as our agent, along with the claude-agent-sdk-python library for Python 3.

You’ll need:

  • A Claude subscription. Note that 
  • Install Claude code.
  • Install the SDK with: pip install claude-agent-sdk.

Then run claude from the terminal for the first time and follow the wizard to set up your account. It asks a few questions on the terminal, then performs an OAuth authentication step in the browser.

Let’s test if everything is properly installed.

Copy this code to a file named agent.py:

import anyio
from claude_agent_sdk import query, ResultMessage

async def main():
    async for message in query(prompt="What is 2 + 2?"):
        if isinstance(message, ResultMessage):
            print(f"2+2 is: {message.result}")
            print(f"Cost: {next(iter(message.model_usage.values()))['costUSD']}USD")

anyio.run(main)

In this code, we are using query() to ask Claude a question. We’ll receive several messages with Claude’s progress. We’ll process the last one, of type ResultMessage. This one contains the final result and information about the cost of our prompt.

If you run it, you should get a proper response:

$ python agent.py

2+2 is: 4
Cost: 0.0030681USD

¡Congrats! We just built the most inefficient calculator ever, but we confirmed that we have a working custom AI agent.

Connecting With the LLM

Let’s put it all together! Update the agent.py file as follows:

import anyio
import requests
from claude_agent_sdk import query, ResultMessage

WEBHOOK_URL="https://hooks.slack.com/services/…"

# Our Claude Code task. This could be "Refactor a file"
async def run_test_task() -> str:
  task_description = "Test task: 2+2"
  async for message in query(prompt="What is 2 + 2?"):
    if isinstance(message, ResultMessage):
      send_to_slack(task_description, message)
      return message.result

def send_to_slack(task_description, message):
  status = message.subtype
  result = message.result
  model_usage = next(iter(message.model_usage.values()))
  cost = model_usage['costUSD']
  job_description = f"*Job:* {task_description}\n*Status:* {status}\n*Cost:* {cost}USD"
  formatted_message = [
      {
        "type": "header",
        "text": { "type": "plain_text", "text": "Task Complete" }
      },
      {
        "type": "section",
        "text": { "type": "mrkdwn", "text": job_description }
      },
      { "type": "divider" },
      {
        "type": "section",
        "text": { "type": "mrkdwn", "text": f"```{result}```" }
      }
    ]
  requests.post(WEBHOOK_URL,json={"blocks": formatted_message})
  print(f"Sending to Slack: {formatted_message}")

async def main():
  result = await run_test_task()
  print(f"Run Test Task: {result}")

anyio.run(main)

We moved our Claude code into the run_test_task method, then added a send_to_slack function to format the Slack message and send it.

The key code calling the webhook is:

requests.post(WEBHOOK_URL,json={"blocks": formatted_message})

After running the code like:

$ python agent_2.py
Sending to Slack: {'blocks': [{'type': 'header', 'text': {'type': 'plain_text', 'text': 'Task Complete'}}, {'type': 'section', 'text': {'type': 'mrkdwn', 'text': '*Job:* Test task: 2+2\n*Status:* success\n*Cost:* 0.0029330999999999997USD'}}, {'type': 'divider'}, {'type': 'section', 'text': {'type': 'mrkdwn', 'text': '```4```'}}]}
Run Test Task: 4

You should see a notification in Slack:

Now, you can adapt the code we created to add some real AI agent work that’s worth monitoring. 

Incoming Webhooks Security Best Practices

The main security practice to remember is to keep the Webhook URL secret.

Never share your Webhook URL, as it’s considered sensitive information. Anyone who knows that URL can send messages to your Slack.

At best, the malicious actor will spam your Slack channels. As Slack Incoming Webhooks are limited to one message per minute, the damage is limited. However, spam messages can prevent important alerts from being displayed.

At worst, you can suffer a social engineering attack. The malicious actor can craft a message that resembles a GitHub alert but links to a malicious website that steals your team’s credentials.

Avoid sending links in webhook messages to make these kinds of attacks easier to identify.

Also, educate your team on the security implications of webhooks.

How-To: Send AI Agent Notifications via Slack Web API

Let’s adapt our example to use the Slack Web API instead of webhooks.

In this way, we’ll be able to control our agent remotely with commands in the future.

Creating a New Slack App

To keep things clean, we’ll create a new Slack App to access the API.

Start by navigating to https://api.slack.com/apps.

Hit Create New App, then From Scratch, then choose a name and a workspace, just like before.

Now, in the left sidebar, navigate to Oauth & Permissions.

Scroll down to Scopes -> Bot Token Scopes.

There, add at least these permissions:

chat:write — to post messages

im:write — to send DMs

Configuration is ready. Let’s generate our OAuth tokens.

Click Install App in the sidebar, then click Install to Workspace.

Just like before, review the permissions and Allow access to your workspace.

Then, you’ll get the chance to copy the Bot User OAuth Token.

Finally, let’s add our App to the channel where we want to receive the notifications.

For that, open the channel and click the name to reveal the details.

Select Add an App and follow the steps to add your newly created app.

Testing the Slack App

We have all the information we need to send a test message.

Save this code to a file named test_slack_api.py:

import requests
import os

SLACK_TOKEN = os.getenv("SLACK_BOT_TOKEN")

def send_test_message():
  requests.post(
    "https://slack.com/api/chat.postMessage",
    headers={"Authorization": f"Bearer {SLACK_TOKEN}"},
    json={"channel": "test-claude", "text": "Hello World!"},
  )

print(send_test_message())

Notice how we are sending the message Hello World! to the test-claude channel.

After running the code like:

$ export SLACK_BOT_TOKEN=Your Bot OAuth Token
$ python test_slack_api.py

You should see a message on the Slack channel:

Updating Our Agent to Use the Slack Web API

Let’s edit our agent.py to replace the webhook code.

In particular, replace the old requests.post() call with this one:

  requests.post(
    "https://slack.com/api/chat.postMessage",
    headers={"Authorization": f"Bearer {SLACK_TOKEN}"},
    json={"channel": "test-claude", "blocks": formatted_message},
  )

After running our agent like this:

$ python agent_3.py
Sending to Slack: [{'type': 'header', 'text': {'type': 'plain_text', 'text': 'Task Complete'}}, {'type': 'section', 'text': {'type': 'mrkdwn', 'text': '*Job:* Test task: 2+2\n*Status:* success\n*Cost:* 0.0029330999999999997USD'}}, {'type': 'divider'}, {'type': 'section', 'text': {'type': 'mrkdwn', 'text': '```4```'}}]
Run Test Task: 4

You should see a new notification in the channel:

Your agent is now sending messages to Slack using the API.

We’ve covered the basics, and you are ready to start exploring the rest of the Slack Web API to create richer experiences.

Slack API security best practices

The Slack API exposes mostly the same functionality as the desktop client. Treat these accesses with the same care as user accesses to prevent data leaks, service interruption, or social engineering attacks.

Start by limiting your Slack App permissions: Grant only the minimal set of scopes required for your integration, paying special attention to the information you are making available. 

Secure the data on your App’s side: Collect and store the minimum amount of data, while encrypting the data in use, in transit, and at rest. When connecting your app to an LLM, keep in mind that AI agents are external services that collect data. Limit what information is available to the LLMs.

Secure the API credentials: Don’t store OAuth credentials in plain sight, such as in configuration files. Encrypt this information as well.

Even better, implement JIT access: Rather than using Slack’s OAuth credentials in your app, use an intermediary. You’ll be able to:

  • Have centralized visibility of all your connections.
  • Provide more granular policies, such as one-time access.
  • Pull the plug faster in case of a security event.

Other Methods to Access Your LLM from Slack

Using LLMs from Slack Native Apps

If you just want to chat with your LLM like a contact in Slack, there’s a simpler way.

Most LLMs have an official app for Slack. They are simplified versions of their web or Desktop clients, with little to no access to other information in Slack.

They interact with Slack only as a conversation. In other words, you are the one starting the interaction, rather than the LLM sending you information when an event happens.

You can install them from the Slack Marketplace.

From a security standpoint, the risk is low. It’s similar to using the LLM’s desktop app. Educating users is the best way to ensure no private data is leaked.

Connecting Claude Code With the Slack MCP Server

However, if you want to harness all the power of Slack from your LLM client, you can rely on Slack’s MCP server.

If you are using Claude Code or Claude Desktop, check the official plugin that connects with Slack’s MCP server. The installation steps require creating a Slack App, as the Slack API requires an OAuth token. Follow the steps that we described when discussing the Slack Web API.

The security implications of using Slack’s MCP server are the same as using the Slack API. Limit the permissions of the Slack App that backs the connection, and implement JIT access for the OAuth connection.

Next Steps

Here are some improvements that you can add to your Slack and LLM connection to get the most out of both systems.

Use a Framework 

Rather than writing your client code from scratch, you can leverage frameworks that reduce some of the complexity of dealing directly with a messaging API. Have a look at Slack Bolt, a framework for building Slack Apps that includes message sending.

Add Langchain for Scalability 

LangChain is an open-source framework for creating agents and offers several benefits for an integration like this. It includes a built-in agent loop that determines which tool to use for a given request.

Add Redis to Have Persistent Memory

You now have everything in place, and your LLM and Slack are communicating daily. Then, suddenly, a server restart happens, and all your context is lost! That’s why you should start using Redis to store conversation context and preserve it across restarts.

Implement JIT Access for Your OAuth Connections

With so many services, connectors, and applications, it’s easy to lose track of who has access to what. Auditing and blocking these connections is a tedious task that requires reviewing each tool’s admin panel.

JIT access tools centralize these accesses, while providing finer control. You can provide one-time access, audit connections, and pull the plug, all from the same control panel.

Conclusion

The power of Slack and similar messaging tools lies in integrating the tools you use constantly into your instant communication.

LLMs are becoming increasingly prevalent, and there are plenty of options for connecting them to Slack. However, you should be aware of the security implications and follow best practices to avoid incidents.

Each option offers a different level of functionality. Just keep in mind that the more access you grant to your integration, the higher the security risk you take.Luckily, Slack lets you tweak app permissions, and you can use JIT access to gain greater control over who can access the API.

You might also like

Workforce and customer agents may rely on similar identity infrastructure, but the trust models, access patterns, and security risks behind them differ significantly.
What it takes to implement it, and why real-world environments make it hard to finish.
Workload access management isn’t identity management – it enforces access and eliminates credentials. Learn the five core WAM capabilities