---
title: Slackbot Agent Guide
description: Learn how to build an AI Agent in Slack with the AI SDK and Chat SDK.
url: "https://ai-sdk.dev/cookbook/guides/slackbot"
docs_index: /llms.txt
tags:
  - agents
  - chatbot
---

> For an index of all documentation, see [/llms.txt](/llms.txt).

In this guide, you will learn how to build a Slackbot powered by the AI SDK. The bot will respond to mentions in channels and to direct messages, using the full context of the thread, and stream its replies into Slack as they are generated.

The bot uses [Chat SDK](https://chat-sdk.dev) to connect to Slack. Chat SDK verifies Slack's webhook signatures, parses events into threads and messages, and streams replies with Slack's native streaming API, so you can focus on the agent.

## Project Setup

This project uses the following stack:

- [AI SDK by Vercel](/docs)
- [Chat SDK](https://chat-sdk.dev) with the [Slack adapter](https://chat-sdk.dev/adapters/official/slack)
- [Next.js](https://nextjs.org) App Router
- [Redis](https://redis.io) for thread subscriptions and locks
- [Vercel](https://vercel.com)

Create a new Next.js application:

```
pnpm create next-app@latest my-slackbot
```

Navigate to the newly created directory:

```
cd my-slackbot
```

Install the AI SDK, Chat SDK, the Slack adapter, the Redis state adapter, and `zod` for tool inputs:

```bash
pnpm add ai chat @chat-adapter/slack @chat-adapter/state-redis zod
```

## Slack App Setup

Create a Slack app from a manifest:

1. Go to [api.slack.com/apps](https://api.slack.com/apps)
2. Click "Create New App" and choose "From a manifest"
3. Select your workspace and paste the manifest below
4. Install the app to your workspace

```yaml title="manifest.yaml"
display_information:
  name: My Bot
  description: A bot built with the AI SDK and Chat SDK

features:
  agent_view:
    agent_description: A bot built with the AI SDK and Chat SDK
  bot_user:
    display_name: My Bot
    always_online: true

oauth_config:
  scopes:
    bot:
      - app_mentions:read
      - assistant:write
      - channels:history
      - channels:read
      - chat:write
      - groups:history
      - groups:read
      - im:history
      - im:read
      - mpim:history
      - mpim:read
      - reactions:read
      - reactions:write
      - users:read

settings:
  event_subscriptions:
    request_url: https://your-domain.com/api/webhooks/slack
    bot_events:
      - app_mention
      - message.channels
      - message.groups
      - message.im
      - message.mpim
      - member_joined_channel
      - app_home_opened
      - app_context_changed
      - agent_session_stopped
      - agent_session_title_changed
  interactivity:
    is_enabled: true
    request_url: https://your-domain.com/api/webhooks/slack
```

You will replace `your-domain.com` with your deployment URL after deploying.

## Configure Environment Variables

Create a `.env.local` file in your project root:

```dotenv title=".env.local"
SLACK_BOT_TOKEN=your_slack_bot_token
SLACK_SIGNING_SECRET=your_slack_signing_secret
REDIS_URL=your_redis_url
AI_GATEWAY_API_KEY=your_ai_gateway_api_key
```

- `SLACK_BOT_TOKEN`: the Bot User OAuth Token from your Slack app's "OAuth & Permissions" page
- `SLACK_SIGNING_SECRET`: the Signing Secret from your Slack app's "Basic Information" page
- `REDIS_URL`: a Redis connection URL, such as one from a Redis integration on the [Vercel Marketplace](https://vercel.com/marketplace)
- `AI_GATEWAY_API_KEY`: your [Vercel AI Gateway](/providers/ai-sdk-providers/ai-gateway) API key

The Slack adapter and the Redis state adapter read these variables automatically.

## Create the Agent

Create the agent in `lib/agent.ts`. It uses the AI SDK's [`ToolLoopAgent`](/docs/agents/building-agents) with a weather tool, so it can call the tool and then answer with the result:

```ts title="lib/agent.ts"
import { ToolLoopAgent, tool } from 'ai';
import { z } from 'zod';

export const agent = new ToolLoopAgent({
  model: "anthropic/claude-sonnet-5.5",
  instructions: `You are a Slack bot assistant. Keep your responses concise and to the point.
  - Do not tag users.
  - Current date is: ${new Date().toISOString().split('T')[0]}`,
  tools: {
    getWeather: tool({
      description: 'Get the current weather at a location',
      inputSchema: z.object({
        latitude: z.number(),
        longitude: z.number(),
        city: z.string(),
      }),
      execute: async ({ latitude, longitude, city }) => {
        const response = await fetch(
          `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,weathercode,relativehumidity_2m&timezone=auto`,
        );

        const weatherData = await response.json();
        return {
          temperature: weatherData.current.temperature_2m,
          weatherCode: weatherData.current.weathercode,
          humidity: weatherData.current.relativehumidity_2m,
          city,
        };
      },
    }),
  },
});
```

`ToolLoopAgent` sends tool results back to the model automatically and keeps calling tools until the model writes a final answer (up to 20 steps by default). If a user asks "What's the weather in London?", the agent calls `getWeather` with London's coordinates and then answers using the result.

## Create the Bot

Create the bot in `lib/bot.ts`. It connects the agent to Slack:

```ts title="lib/bot.ts"
import { Chat, type Thread } from 'chat';
import { toAiMessages } from 'chat/ai';
import { createSlackAdapter } from '@chat-adapter/slack';
import { createRedisState } from '@chat-adapter/state-redis';
import { agent } from './agent';

export const bot = new Chat({
  userName: 'mybot',
  adapters: {
    // Match the manifest's `agent_view` (Slack's Agent messaging experience).
    slack: createSlackAdapter({ agentView: true }),
  },
  state: createRedisState(),
});

async function respond(thread: Thread) {
  await thread.startTyping();

  // Load recent messages and convert them to AI SDK messages.
  const { messages } = await bot.history.thread.list(thread.id, {
    limit: 20,
  });

  const result = await agent.stream({
    messages: await toAiMessages(messages),
    abortSignal: thread.signal,
  });

  // Stream the reply into the Slack thread.
  await thread.post(result.fullStream);
}

// A mention or a new direct message starts a conversation.
bot.onNewMention(async thread => {
  await thread.subscribe();
  await respond(thread);
});

// Later messages in a subscribed thread continue it.
bot.onSubscribedMessage(async thread => {
  await respond(thread);
});
```

Here's what each part does:

- `agentView: true` tells the Slack adapter that the app uses Slack's Agent messaging experience, which the manifest enables with `agent_view`. In direct messages, each top-level message starts its own thread and the bot replies in that thread. Keep this option in sync with the manifest: without it, the adapter uses the older `assistant_view` thread mapping, and top-level direct messages fail to load their history.
- `onNewMention` runs when someone mentions the bot in a thread it hasn't subscribed to. Direct messages are treated as mentions too. `thread.subscribe()` makes later messages in the thread go to `onSubscribedMessage`.
- `startTyping()` shows Slack's typing indicator while the agent works.
- `bot.history.thread.list()` loads the thread's recent messages, and `toAiMessages` converts them to `ModelMessage[]`, with the bot's own messages as `assistant` messages.
- `thread.post(result.fullStream)` streams the agent's reply into the thread. `fullStream` includes step boundaries, so Chat SDK can add paragraph breaks between the agent's steps. Chat SDK also converts the model's markdown to Slack formatting.
- `thread.signal` is aborted when the user clicks Slack's stop button, which stops generation.

## Create the Webhook Route

Create a route handler that passes Slack's events to the bot:

```ts title="app/api/webhooks/slack/route.ts"
import { after } from 'next/server';
import { bot } from '@/lib/bot';

export async function POST(request: Request): Promise<Response> {
  return bot.webhooks.slack(request, {
    waitUntil: task => after(() => task),
  });
}
```

Slack expects a response within three seconds. The route responds right away, and `waitUntil` keeps the function running until the agent finishes its reply.

## Deploying the App

1. Install the Vercel CLI

```
pnpm install -g vercel
```

2. Deploy the app

```
vercel deploy
```

3. Go to your project's settings on Vercel (Your project -> Settings -> Environment Variables) and add the variables from your `.env.local` file

Make sure to redeploy your app after updating environment variables.

4. Go to your app on [api.slack.com/apps](https://api.slack.com/apps) and open "App Manifest". Replace `your-domain.com` in both `request_url` fields with your deployment's domain, for example:

```bash
https://your-vercel-url.vercel.app/api/webhooks/slack
```

Finally, head to Slack and test the app by mentioning the bot in a channel or sending it a direct message.

## Next Steps

You've built a Slack agent powered by the AI SDK! Here are some ways you could extend it:

1. Give the agent tools to read the channel or react to messages with [Chat SDK's AI SDK tools](/docs/agents/chat-platforms#give-the-agent-chat-tools)
2. Run the same bot on Microsoft Teams, Discord, and other platforms by [adding adapters](/docs/agents/chat-platforms#add-more-platforms)
3. Implement more tools like database queries or knowledge base searches
4. Post rich messages with [cards](https://chat-sdk.dev/docs/cards) and handle button clicks with [actions](https://chat-sdk.dev/docs/actions)

For a framework-managed alternative with durable threaded sessions, typing indicators, approval buttons, and multichannel support, see [eve's Slack channel](https://eve.dev/docs/channels/slack).

---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)