Installation
npm install @chat-adapter/slack
Environment Variables
| Variable | Required | Description |
|---|
SLACK_BOT_TOKEN | Single-workspace only | Bot User OAuth Token (xoxb-...) |
SLACK_SIGNING_SECRET | Yes | Webhook verification secret |
SLACK_CLIENT_ID | Multi-workspace only | OAuth Client ID for app installation |
SLACK_CLIENT_SECRET | Multi-workspace only | OAuth Client Secret |
Configuration Options
interface SlackAdapterConfig {
/** Bot token (xoxb-...). Required for single-workspace mode. Omit for multi-workspace. */
botToken?: string;
/** Bot user ID (will be fetched if not provided) */
botUserId?: string;
/** Slack app client ID (required for OAuth / multi-workspace) */
clientId?: string;
/** Slack app client secret (required for OAuth / multi-workspace) */
clientSecret?: string;
/** Base64-encoded 32-byte AES-256-GCM encryption key for encrypting stored tokens */
encryptionKey?: string;
/** Prefix for state keys storing installations (default: 'slack:installation') */
installationKeyPrefix?: string;
/** Logger instance */
logger: Logger;
/** Signing secret for webhook verification */
signingSecret: string;
/** Override bot username (optional) */
userName?: string;
}
Setup
Single-Workspace
Multi-Workspace
For internal bots serving one workspace:import { Chat } from 'chat';
import { createSlackAdapter } from '@chat-adapter/slack';
import { MemoryState } from '@chat-adapter/state-memory';
const chat = new Chat({
userName: 'my-bot',
adapters: {
slack: createSlackAdapter({
botToken: process.env.SLACK_BOT_TOKEN!,
signingSecret: process.env.SLACK_SIGNING_SECRET!,
}),
},
state: new MemoryState(),
});
await chat.initialize();
For public apps that users can install in any workspace:import { Chat } from 'chat';
import { createSlackAdapter } from '@chat-adapter/slack';
import { RedisState } from '@chat-adapter/state-redis';
const slack = createSlackAdapter({
// No botToken - will be resolved per-workspace
clientId: process.env.SLACK_CLIENT_ID!,
clientSecret: process.env.SLACK_CLIENT_SECRET!,
signingSecret: process.env.SLACK_SIGNING_SECRET!,
// Optional: encrypt tokens at rest
encryptionKey: process.env.SLACK_ENCRYPTION_KEY,
});
const chat = new Chat({
userName: 'my-bot',
adapters: { slack },
state: new RedisState({ url: process.env.REDIS_URL }),
});
await chat.initialize();
Multi-workspace mode requires a persistent StateAdapter like Redis.
In-memory state will lose installations on restart.
OAuth Flow (Multi-Workspace)
For multi-workspace apps, handle the OAuth callback:
// Route: /slack/oauth/callback
app.get('/slack/oauth/callback', async (req, res) => {
const { teamId, installation } = await slack.handleOAuthCallback(req);
res.send(`Installed to workspace ${installation.teamName}!`);
});
The adapter automatically:
- Exchanges the authorization code for an access token
- Stores the installation via
StateAdapter.set()
- Encrypts the token if
encryptionKey is provided
Webhook Handler
app.post('/webhooks/slack', async (req, res) => {
const response = await slack.handleWebhook(req, {
waitUntil: (promise) => {/* handle async work */},
});
res.status(response.status).send(await response.text());
});
Features
Supported Events
message - Regular messages in channels/DMs
app_mention - Bot mentions (@bot-name)
reaction_added / reaction_removed - Emoji reactions
assistant_thread_started - Slack AI Assistant thread created
app_home_opened - User opens bot’s Home tab
member_joined_channel - User/bot joins a channel
Slash Commands
chat.onSlashCommand('/deploy', async (event) => {
await event.channel.post(`Deploying ${event.text}...`);
});
Interactive Components
Button clicks are automatically routed to chat.onAction():
chat.onAction('approve_button', async (event) => {
await event.thread.post('Approved!');
});
Modals
Modals are not yet fully implemented in the SDK. Use publishHomeView() for Home tab views.
Home Tab
Publish a custom Home tab view:
await slack.publishHomeView(userId, {
type: 'home',
blocks: [
{
type: 'section',
text: { type: 'mrkdwn', text: 'Welcome to *My Bot*!' },
},
],
});
Slack Assistant API
For Slack AI features:
// Set suggested prompts
await slack.setSuggestedPrompts(
channelId,
threadTs,
[
{ title: 'Summarize', message: '/summarize this thread' },
{ title: 'Help', message: 'What can you do?' },
],
'Quick actions'
);
// Show thinking indicator
await slack.setAssistantStatus(channelId, threadTs, 'is thinking...');
// Set thread title (appears in History)
await slack.setAssistantTitle(channelId, threadTs, 'Q&A about deployment');
Thread IDs
Slack thread IDs encode channel and message timestamp:
slack:{channelId}:{threadTs}
Examples:
- Channel message:
slack:C1234567890:1234567890.123456
- DM (top-level):
slack:D9876543210: (empty threadTs for DM roots)
- DM reply:
slack:D9876543210:1234567890.123456
- Message length: 40,000 characters (Block Kit) or 4,000 characters (plain text)
- Blocks per message: 50 blocks
- Rate limits: Tier-based (1+ requests/second for most methods)
- File size: 1 GB per file
See Slack rate limits for details.
Code Examples
chat.onNewMention(async (event) => {
await event.thread.post(`Hi ${event.message.author.userName}!`);
});
Troubleshooting
Webhook verification fails
- Ensure
SLACK_SIGNING_SECRET matches your app configuration
- Check that the request body is raw (not parsed JSON)
- Verify timestamp is within 5 minutes (check server clock)
Bot doesn't respond to messages
- Add bot to the channel (
/invite @bot-name)
- Verify bot has
chat:write scope
- Check webhook URL is publicly accessible
- Enable
message.channels event subscription
Multi-workspace mode not working
- Use a persistent StateAdapter (Redis, not Memory)
- Ensure OAuth redirect URL is whitelisted in app settings
- Check that installations are being saved (debug logs)
Required Scopes
Add these OAuth scopes to your Slack app:
Bot Token Scopes:
chat:write - Post messages
reactions:write - Add reactions
channels:history - Read channel messages
groups:history - Read private channel messages
im:history - Read DM messages
mpim:history - Read group DM messages
users:read - Look up user info
Event Subscriptions:
message.channels
message.groups
message.im
message.mpim
app_mention
reaction_added
reaction_removed
See Slack permissions for full scope list.
Next Steps
Message Handling
Process messages and build conversation flows
Block Kit Cards
Create interactive UIs with buttons and forms
Multi-Workspace Apps
Build apps for public distribution
State Management
Persist data across requests