Slackbot Agent Guide
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 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
- Chat SDK with the Slack adapter
- Next.js App Router
- Redis for thread subscriptions and locks
- Vercel
Create a new Next.js application:
pnpm create next-app@latest my-slackbotNavigate to the newly created directory:
cd my-slackbotInstall the AI SDK, Chat SDK, the Slack adapter, the Redis state adapter, and zod for tool inputs:
pnpm add ai chat @chat-adapter/slack @chat-adapter/state-redis zodnpm install ai chat @chat-adapter/slack @chat-adapter/state-redis zodyarn add ai chat @chat-adapter/slack @chat-adapter/state-redis zodbun add ai chat @chat-adapter/slack @chat-adapter/state-redis zodSlack App Setup
Create a Slack app from a manifest:
- Go to api.slack.com/apps
- Click "Create New App" and choose "From a manifest"
- Select your workspace and paste the manifest below
- Install the app to your workspace
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/slackYou will replace your-domain.com with your deployment URL after deploying.
Configure Environment Variables
Create a .env.local file in your project root:
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_keySLACK_BOT_TOKEN: the Bot User OAuth Token from your Slack app's "OAuth & Permissions" pageSLACK_SIGNING_SECRET: the Signing Secret from your Slack app's "Basic Information" pageREDIS_URL: a Redis connection URL, such as one from a Redis integration on the Vercel MarketplaceAI_GATEWAY_API_KEY: your Vercel 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 with a weather tool, so it can call the tool and then answer with the result:
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}¤t=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:
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: truetells the Slack adapter that the app uses Slack's Agent messaging experience, which the manifest enables withagent_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 olderassistant_viewthread mapping, and top-level direct messages fail to load their history.onNewMentionruns 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 toonSubscribedMessage.startTyping()shows Slack's typing indicator while the agent works.bot.history.thread.list()loads the thread's recent messages, andtoAiMessagesconverts them toModelMessage[], with the bot's own messages asassistantmessages.thread.post(result.fullStream)streams the agent's reply into the thread.fullStreamincludes 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.signalis 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:
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
- Install the Vercel CLI
pnpm install -g vercel- Deploy the app
vercel deploy- Go to your project's settings on Vercel (Your project -> Settings -> Environment Variables) and add the variables from your
.env.localfile
Note
Make sure to redeploy your app after updating environment variables.
- Go to your app on api.slack.com/apps and open "App Manifest". Replace
your-domain.comin bothrequest_urlfields with your deployment's domain, for example:
https://your-vercel-url.vercel.app/api/webhooks/slackFinally, 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:
- Give the agent tools to read the channel or react to messages with Chat SDK's AI SDK tools
- Run the same bot on Microsoft Teams, Discord, and other platforms by adding adapters
- Implement more tools like database queries or knowledge base searches
- Post rich messages with cards and handle button clicks with actions
For a framework-managed alternative with durable threaded sessions, typing indicators, approval buttons, and multichannel support, see eve's Slack channel.