Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@chat-adapter/slack
Slack adapter for the chat SDK.
Installation
npm install chat @chat-adapter/slack
Usage (Single Workspace)
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
const chat = new Chat({
userName: "mybot",
adapters: {
slack: createSlackAdapter({
botToken: process.env.SLACK_BOT_TOKEN!,
signingSecret: process.env.SLACK_SIGNING_SECRET!,
}),
},
});
// Handle @mentions
chat.onNewMention(async (thread, message) => {
await thread.post("Hello from Slack!");
});
Multi-Workspace Mode
For apps installed across multiple Slack workspaces, omit botToken and let the adapter resolve tokens dynamically from your state adapter (e.g. Redis) using the team_id from incoming webhooks.
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
import { createRedisState } from "@chat-adapter/state-redis";
const slackAdapter = createSlackAdapter({
signingSecret: process.env.SLACK_SIGNING_SECRET!,
clientId: process.env.SLACK_CLIENT_ID!,
clientSecret: process.env.SLACK_CLIENT_SECRET!,
logger: logger,
encryptionKey: process.env.SLACK_ENCRYPTION_KEY, // optional, encrypts tokens at rest
});
const chat = new Chat({
userName: "mybot",
adapters: { slack: slackAdapter },
state: createRedisState({ url: process.env.REDIS_URL! }),
// notice that there is no bot token
});
OAuth callback
The adapter handles the full Slack OAuth V2 exchange. Pass clientId and clientSecret in the config, then point your OAuth redirect URL to a route that calls handleOAuthCallback:
import { slackAdapter } from "@/lib/chat"; // your adapter instance
export async function GET(request: Request) {
const { teamId } = await slackAdapter.handleOAuthCallback(request);
return new Response(`Installed for team ${teamId}!`);
}
Webhook handling
No changes needed — the adapter extracts team_id from incoming webhooks and resolves the token automatically:
export async function POST(request: Request) {
return chat.webhooks.slack(request, { waitUntil });
}
Using the adapter outside a webhook (cron jobs, workflows)
During webhook handling, the adapter resolves the token automatically from team_id. Outside that context (e.g. a cron job), use getInstallation to retrieve the token and withBotToken to scope it:
import { Chat } from "chat";
// In a cron job or background worker:
const install = await slackAdapter.getInstallation(teamId);
if (!install) throw new Error("Workspace not installed");
await slackAdapter.withBotToken(install.botToken, async () => {
// All adapter calls inside this callback use the provided token.
// You can use thread.post(), thread.subscribe(), etc. normally.
const thread = chat.thread("slack:C12345:1234567890.123456");
await thread.post("Hello from a cron job!");
});
withBotToken uses AsyncLocalStorage under the hood, so concurrent calls with different tokens are isolated from each other.
Removing installations
await slackAdapter.deleteInstallation(teamId);
Encryption
Pass a base64-encoded 32-byte key as encryptionKey to encrypt bot tokens at rest using AES-256-GCM. You can generate a key with:
openssl rand -base64 32
When encryptionKey is set, setInstallation() encrypts the token before storing it and getInstallation() decrypts it transparently.
Configuration
| Option | Required | Description |
|---|---|---|
botToken |
No | Slack bot token (xoxb-...). Required for single-workspace mode. Omit for multi-workspace. |
signingSecret |
Yes | Slack signing secret for webhook verification |
clientId |
No | Slack app client ID (required for OAuth / multi-workspace) |
clientSecret |
No | Slack app client secret (required for OAuth / multi-workspace) |
encryptionKey |
No | Base64-encoded 32-byte AES-256-GCM key for encrypting stored tokens |
Environment Variables
SLACK_BOT_TOKEN=xoxb-... # single-workspace only
SLACK_SIGNING_SECRET=...
SLACK_CLIENT_ID=... # required for multi-workspace OAuth
SLACK_CLIENT_SECRET=... # required for multi-workspace OAuth
SLACK_ENCRYPTION_KEY=... # optional, for multi-workspace token encryption
Slack App Setup
1. Create a Slack App
- Go to api.slack.com/apps
- Click Create New App → From scratch
- Enter app name and select workspace
- Click Create App
2. Configure Bot Token Scopes
- Go to OAuth & Permissions in the sidebar
- Under Scopes → Bot Token Scopes, add:
app_mentions:read- Receive @mention eventschannels:history- Read messages in public channelschannels:read- View basic channel infochat:write- Send messagesgroups:history- Read messages in private channelsgroups:read- View basic private channel infoim:history- Read direct messagesim:read- View basic DM inforeactions:read- View emoji reactionsreactions:write- Add/remove emoji reactionsusers:read- View user info (for display names)
3. Install App to Workspace
Single workspace: Install directly from the Slack dashboard.
- Go to OAuth & Permissions
- Click Install to Workspace
- Authorize the app
- Copy the Bot User OAuth Token (starts with
xoxb-) →SLACK_BOT_TOKEN
Multi-workspace: Enable Manage Distribution under Basic Information, then set up an OAuth redirect URL pointing to your callback route. The adapter handles the token exchange via handleOAuthCallback() (see Multi-Workspace Mode above).
4. Get Signing Secret and OAuth Credentials
- Go to Basic Information
- Under App Credentials, copy:
- Signing Secret →
SLACK_SIGNING_SECRET - Client ID →
SLACK_CLIENT_ID(multi-workspace only) - Client Secret →
SLACK_CLIENT_SECRET(multi-workspace only)
- Signing Secret →
5. Configure Event Subscriptions
- Go to Event Subscriptions
- Toggle Enable Events to On
- Set Request URL to:
https://your-domain.com/api/webhooks/slack- Slack will verify the URL immediately
- Under Subscribe to bot events, add:
app_mention- When someone @mentions your botmessage.channels- Messages in public channelsmessage.groups- Messages in private channelsmessage.im- Direct messages
- Click Save Changes
6. (Optional) Enable Interactivity
If you want to use buttons, modals, or other interactive components:
- Go to Interactivity & Shortcuts
- Toggle Interactivity to On
- Set Request URL to:
https://your-domain.com/api/webhooks/slack
Features
- Multi-workspace support with OAuth V2 and encrypted token storage
- Message posting and editing
- Thread subscriptions
- Reaction handling (add/remove/events)
- File attachments
- Rich cards (Block Kit)
- Action callbacks (interactive components)
- Direct messages
Troubleshooting
"Invalid signature" error
- Verify
SLACK_SIGNING_SECRETis correct - Check that the request timestamp is within 5 minutes (clock sync issue)
Bot not responding to messages
- Verify Event Subscriptions are configured
- Check that the bot has been added to the channel
- Ensure the webhook URL is correct and accessible
License
MIT