showcase/shell-docs/src/content/docs/channels/platforms/slack.mdx
Connect Slack yourself with the Slack adapter. It runs over Socket Mode by default, so no public URL is needed for local development.
npm install @copilotkit/channels @copilotkit/runtime @tanstack/ai @tanstack/ai-openai
From your Slack app:
SLACK_BOT_TOKEN=xoxb-... # Bot User OAuth token
SLACK_APP_TOKEN=xapp-... # App-level token, for Socket Mode
# The runtime runs every channel, so a CopilotKit Intelligence key is required
# even for a direct adapter (free tier available).
COPILOTKIT_API_KEY=cpk-...
import { createChannel } from "@copilotkit/channels";
import { slack, defaultSlackContext } from "@copilotkit/channels/slack";
import { CopilotRuntime, CopilotKitIntelligence } from "@copilotkit/runtime/v2";
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";
import { agent } from "./agent"; // your TanStack AI agent, see the Quickstart
const channel = createChannel({
name: "support-bot",
agent,
// Slack output-formatting guidance so replies render well in Slack.
context: [...defaultSlackContext],
adapters: [
slack({
botToken: process.env.SLACK_BOT_TOKEN!,
appToken: process.env.SLACK_APP_TOKEN!,
respondTo: {
directMessages: true,
appMentions: { reply: "thread" },
threadReplies: "mentionsOnly",
},
}),
],
});
channel.onMessage(async ({ thread, message }) => {
await thread.runAgent({ prompt: message.text });
});
// The runtime drives the channel — it starts the Slack adapter, you don't call
// channel.start() yourself. A CopilotKit Intelligence key runs every channel,
// direct adapters included (free tier available); `ready()` activates it.
const runtime = new CopilotRuntime({
agents: {},
intelligence: new CopilotKitIntelligence({
// apiUrl and wsUrl default to the managed Intelligence platform. Override
// both together only for a self-hosted deployment.
apiKey: process.env.COPILOTKIT_API_KEY!,
}),
identifyUser: () => ({ id: "demo-user", name: "Demo User" }),
channels: [channel],
});
const listener = createCopilotNodeListener({ runtime });
await listener.channels.ready();
respondTo controls when the bot answers: DMs, mentions, and whether it keeps
listening in a thread it is part of.
The Slack adapter streams replies as they are produced. The streaming option
picks the transport:
"native" (default) uses Slack's chat.startStream in threads, and falls
back automatically in flat DMs or workspaces where the streaming API is not
available."legacy" edits a single message with repeated chat.update calls.slack({
botToken: process.env.SLACK_BOT_TOKEN!,
appToken: process.env.SLACK_APP_TOKEN!,
streaming: "native",
});
The Slack adapter ships helpers you can spread into your channel: a set of tools (including a Slack user-lookup tool) and context that guides Slack formatting and tagging.
import { slack, defaultSlackTools, defaultSlackContext } from "@copilotkit/channels/slack";
createChannel({
name: "support-bot",
agent,
tools: [...defaultSlackTools],
context: [...defaultSlackContext],
adapters: [slack({ /* ...credentials */ })],
});