How to Build a Slack Bot with Webhooks: Complete Guide with Examples
Slack bots use webhooks to receive messages, handle slash commands, and send real-time notifications. Whether you need a simple alert bot or an AI-powered assistant with memory, this guide shows you how to build and deploy one in minutes.
What is a Slack bot? A Slack bot is an automated program that interacts with users in a Slack workspace - responding to messages, executing commands, and posting notifications via webhook endpoints.
The problem? Building a Slack bot requires setting up servers, handling OAuth, managing webhook endpoints, and dealing with Slack's API complexity.
The solution? Deploy a production-ready Slack bot with Codehooks.io templates in under 5 minutes.
Prerequisitesโ
Before you begin, sign up for a free Codehooks.io account and install the CLI:
npm install -g codehooks
Quick Deploy with Templateโ
The fastest way to create a Slack bot is using our ready-made template:
coho create myslackbot --template slack-memory-bot
You'll see output like:
Creating project: myslackbot
Project created successfully!
Your API endpoints:
https://myslackbot-a1b2.api.codehooks.io/dev
Then install, deploy, and configure:
cd myslackbot
npm install
coho deploy
coho set-env SLACK_BOT_TOKEN xoxb-your-token --encrypted
coho set-env SLACK_SIGNING_SECRET your-signing-secret --encrypted
Project: myslackbot-a1b2 Space: dev
Deployed Codehook successfully! ๐
Run coho info to see your API endpoints and tokens.
This template includes:
- Slash command handling
- Event subscriptions
- Conversation memory (stores context)
- AI integration ready
- Database storage
We have multiple Slack templates available:
slack-memory-bot- AI bot with conversation memorywebhook-slack-minimal- Simple webhook handler with signature verification
Browse all templates in the templates repository.
Understanding Slack Integration Typesโ
1. Incoming Webhooks (Send messages TO Slack)โ
Send notifications from your app to a Slack channel:
import { app } from 'codehooks-js';
// Your app triggers this to send a Slack notification
app.post('/notify-slack', async (req, res) => {
const { channel, message } = req.body;
// Send to Slack incoming webhook
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: message,
channel: channel
})
});
res.json({ success: true });
});
export default app.init();
2. Slash Commands (Users trigger your bot)โ
Handle /commands that users type in Slack:
Slack sends slash command data as application/x-www-form-urlencoded (not JSON). Codehooks automatically parses this into req.body, so you can access fields like command, text, and user_id directly.
import { app } from 'codehooks-js';
// Slack sends POST request when user types /mycommand
app.post('/slack/commands', async (req, res) => {
const { command, text, user_name, channel_name } = req.body;
// Verify the request is from Slack
if (!verifySlackRequest(req)) {
return res.status(401).json({ error: 'Unauthorized' });
}
switch (command) {
case '/weather':
const weather = await getWeather(text);
return res.json({
response_type: 'in_channel',
text: `Weather for ${text}: ${weather}`
});
case '/help':
return res.json({
response_type: 'ephemeral', // Only visible to user
text: 'Available commands: /weather [city], /help'
});
default:
return res.json({ text: `Unknown command: ${command}` });
}
});
export default app.init();
3. Event Subscriptions (React to Slack events)โ
Respond to messages, reactions, channel events:
import { app } from 'codehooks-js';
import { Datastore } from 'codehooks-js';
// Slack sends events to this endpoint
app.post('/slack/events', async (req, res) => {
const { type, event, challenge } = req.body;
// Handle Slack URL verification
if (type === 'url_verification') {
return res.json({ challenge });
}
// Handle events
if (type === 'event_callback') {
switch (event.type) {
case 'message':
// Don't respond to bot messages (avoid loops)
if (event.bot_id) break;
await handleMessage(event);
break;
case 'app_mention':
// Bot was @mentioned
await handleMention(event);
break;
case 'reaction_added':
await handleReaction(event);
break;
}
}
res.json({ ok: true });
});
async function handleMessage(event) {
const conn = await Datastore.open();
// Store message for context/memory
await conn.insertOne('slack_messages', {
user: event.user,
channel: event.channel,
text: event.text,
ts: event.ts,
createdAt: new Date().toISOString()
});
// Respond if message contains trigger word
if (event.text.toLowerCase().includes('hello bot')) {
await postSlackMessage(event.channel, `Hello <@${event.user}>!`);
}
}
async function postSlackMessage(channel, text) {
await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SLACK_BOT_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ channel, text })
});
}
export default app.init();
Building an AI Slack Bot with Memoryโ
Create a bot that remembers conversation context:
import { app } from 'codehooks-js';
import { Datastore } from 'codehooks-js';
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN;
app.post('/slack/events', async (req, res) => {
const { type, event, challenge } = req.body;
if (type === 'url_verification') {
return res.json({ challenge });
}
if (event?.type === 'app_mention' && !event.bot_id) {
// Process asynchronously to respond quickly
res.json({ ok: true });
await handleAIResponse(event);
return;
}
res.json({ ok: true });
});
async function handleAIResponse(event) {
const conn = await Datastore.open();
const userId = event.user;
const userMessage = event.text.replace(/<@[^>]+>/g, '').trim();
// Get conversation history for this user
const history = await conn.getMany('conversations', { userId }, {
sort: { createdAt: -1 },
limit: 10
}).toArray();
// Build messages array for OpenAI
const messages = [
{ role: 'system', content: 'You are a helpful Slack assistant.' },
...history.reverse().flatMap(h => [
{ role: 'user', content: h.userMessage },
{ role: 'assistant', content: h.botResponse }
]),
{ role: 'user', content: userMessage }
];
// Call OpenAI
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${OPENAI_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gpt-4',
messages,
max_tokens: 500
})
});
const data = await response.json();
const botResponse = data.choices[0].message.content;
// Store conversation for memory
await conn.insertOne('conversations', {
userId,
userMessage,
botResponse,
channel: event.channel,
createdAt: new Date().toISOString()
});
// Post response to Slack
await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
'Authorization': `Bearer ${SLACK_BOT_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
channel: event.channel,
text: botResponse,
thread_ts: event.ts // Reply in thread
})
});
}
export default app.init();
Setting Up Your Slack Appโ
1. Create a Slack Appโ
- Go to api.slack.com/apps
- Click Create New App โ From scratch
- Enter app name and select workspace
2. Configure OAuth & Permissionsโ
Add these Bot Token Scopes:
chat:write- Send messagescommands- Handle slash commandsapp_mentions:read- Detect @mentionschannels:history- Read channel messagesim:history- Read DMs
3. Set Up Event Subscriptionsโ
- Enable Event Subscriptions
- Enter Request URL:
https://your-app.api.codehooks.io/dev/slack/events - Subscribe to bot events:
message.channelsmessage.imapp_mention
4. Create Slash Commandsโ
- Go to Slash Commands
- Click Create New Command
- Enter command (e.g.,
/mycommand) - Request URL:
https://your-app.api.codehooks.io/dev/slack/commands
5. Install to Workspaceโ
- Go to Install App
- Click Install to Workspace
- Copy the Bot User OAuth Token
6. Set Environment Variablesโ
coho set-env SLACK_BOT_TOKEN xoxb-your-token --encrypted
coho set-env SLACK_SIGNING_SECRET your-signing-secret --encrypted
coho set-env OPENAI_API_KEY sk-your-key --encrypted # If using AI
Sending Rich Messages (Block Kit)โ
Create interactive, formatted messages:
async function sendRichMessage(channel, orderData) {
await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SLACK_BOT_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
channel,
blocks: [
{
type: 'header',
text: { type: 'plain_text', text: 'New Order Received!' }
},
{
type: 'section',
fields: [
{ type: 'mrkdwn', text: `*Order ID:*\n${orderData.id}` },
{ type: 'mrkdwn', text: `*Total:*\n$${orderData.total}` },
{ type: 'mrkdwn', text: `*Customer:*\n${orderData.customer}` },
{ type: 'mrkdwn', text: `*Status:*\n${orderData.status}` }
]
},
{
type: 'actions',
elements: [
{
type: 'button',
text: { type: 'plain_text', text: 'View Order' },
url: `https://yourapp.com/orders/${orderData.id}`
},
{
type: 'button',
text: { type: 'plain_text', text: 'Mark Shipped' },
action_id: 'ship_order',
value: orderData.id
}
]
}
]
})
});
}
Verifying Slack Requestsโ
Always verify that requests come from Slack:
import crypto from 'crypto';
function verifySlackRequest(req) {
const signingSecret = process.env.SLACK_SIGNING_SECRET;
const timestamp = req.headers['x-slack-request-timestamp'];
const signature = req.headers['x-slack-signature'];
// Check timestamp is recent (prevent replay attacks)
const fiveMinutesAgo = Math.floor(Date.now() / 1000) - 60 * 5;
if (timestamp < fiveMinutesAgo) {
return false;
}
// Create signature base string
const sigBasestring = `v0:${timestamp}:${req.rawBody}`;
// Calculate expected signature
const mySignature = 'v0=' + crypto
.createHmac('sha256', signingSecret)
.update(sigBasestring)
.digest('hex');
// Compare signatures
return crypto.timingSafeEqual(
Buffer.from(mySignature),
Buffer.from(signature)
);
}
Common Use Casesโ
Notification Botโ
Send alerts from your app to Slack channels:
- Deployment notifications
- Error alerts
- New user signups
- Order notifications
Support Botโ
Handle customer inquiries:
- Ticket creation via slash commands
- Auto-responses to common questions
- Escalation to human agents
DevOps Botโ
Automate development workflows:
- CI/CD status updates
- Pull request notifications
- Incident management
AI Assistantโ
Intelligent conversational bot:
- Answer questions with context
- Summarize documents
- Generate content
Why Use Codehooks.io for Slack Bots?โ
| Feature | DIY Setup | Codehooks.io |
|---|---|---|
| Setup time | Hours/Days | 5 minutes |
| Server management | Configure & maintain | Serverless |
| Database for memory | Set up separately | Built-in |
| Scaling | Manual configuration | Automatic |
| Templates | Build from scratch | Ready-to-use |
| Cost | Server costs | Free tier available |
Related Resourcesโ
- Slack Bot Templates - Ready-to-deploy templates
- Slack API Documentation
- Block Kit Builder - Design rich messages
- Codehooks.io Quick Start
- Webhook Delivery System
Slack Bot FAQ
Common questions about building and deploying Slack bots
What is a Slack bot?
How do I create a Slack bot?
What's the difference between incoming webhooks and bot events?
How do I make my Slack bot remember conversations?
Can I build a Slack bot for free?
slack-memory-bot template deploys a full-featured bot at no cost for getting started.How do I verify Slack webhook requests?
x-slack-signature header and your Signing Secret (found in app settings under Basic Information). Create an HMAC SHA256 hash of v0:timestamp:body and compare it to the signature. This prevents unauthorized requests to your webhook endpoint.Why isn't my Slack bot responding?
bot_id in events to prevent loops.How do I handle slash commands in Slack?
command, text, user_name, etc. Return a JSON response with text to reply. Use response_type: 'in_channel' to make responses visible to everyone.