Skip to main content

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! ๐Ÿ™Œ
Finding your API endpoint

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
Browse Templates

We have multiple Slack templates available:

  • slack-memory-bot - AI bot with conversation memory
  • webhook-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:

Automatic Form Parsing

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โ€‹

  1. Go to api.slack.com/apps
  2. Click Create New App โ†’ From scratch
  3. Enter app name and select workspace

2. Configure OAuth & Permissionsโ€‹

Add these Bot Token Scopes:

  • chat:write - Send messages
  • commands - Handle slash commands
  • app_mentions:read - Detect @mentions
  • channels:history - Read channel messages
  • im:history - Read DMs

3. Set Up Event Subscriptionsโ€‹

  1. Enable Event Subscriptions
  2. Enter Request URL: https://your-app.api.codehooks.io/dev/slack/events
  3. Subscribe to bot events:
    • message.channels
    • message.im
    • app_mention

4. Create Slash Commandsโ€‹

  1. Go to Slash Commands
  2. Click Create New Command
  3. Enter command (e.g., /mycommand)
  4. Request URL: https://your-app.api.codehooks.io/dev/slack/commands

5. Install to Workspaceโ€‹

  1. Go to Install App
  2. Click Install to Workspace
  3. 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?โ€‹

FeatureDIY SetupCodehooks.io
Setup timeHours/Days5 minutes
Server managementConfigure & maintainServerless
Database for memorySet up separatelyBuilt-in
ScalingManual configurationAutomatic
TemplatesBuild from scratchReady-to-use
CostServer costsFree tier available

Slack Bot FAQ

Common questions about building and deploying Slack bots

What is a Slack bot?
A Slack bot is an automated program that interacts with users in a Slack workspace. Bots can send messages, respond to commands, react to events, and integrate with external services. They're used for notifications, automation, customer support, and AI assistants.
How do I create a Slack bot?
1) Go to api.slack.com/apps and create a new app. 2) Configure OAuth permissions (chat:write, commands, etc.). 3) Set up event subscriptions pointing to your webhook URL. 4) Install the app to your workspace. 5) Deploy your bot code to Codehooks.io using our templates.
What's the difference between incoming webhooks and bot events?
Incoming webhooks are for sending messages TO Slack from your app (one-way, notifications). Bot events let Slack send events TO your app when things happen (messages, reactions, mentions). Most bots use both: events to receive input and the API to respond.
How do I make my Slack bot remember conversations?
Store conversation history in a database. With Codehooks.io, use the built-in Datastore: save each message with user ID, channel, and timestamp. When responding, retrieve recent messages for that user to provide context to your bot logic or AI model.
Can I build a Slack bot for free?
Yes! Slack's free tier allows unlimited apps and bots. Codehooks.io offers a free tier for development and small projects. The slack-memory-bot template deploys a full-featured bot at no cost for getting started.
How do I verify Slack webhook requests?
Use the 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?
Common issues: (1) Bot not installed to workspace - reinstall from app settings. (2) Missing OAuth scopes - add required permissions and reinstall. (3) Event subscriptions not configured - verify URL and subscribe to events. (4) Bot responding to itself - check for bot_id in events to prevent loops.
How do I handle slash commands in Slack?
Create a slash command in your Slack app settings pointing to your webhook URL. When users type the command, Slack POSTs to your endpoint with command, text, user_name, etc. Return a JSON response with text to reply. Use response_type: 'in_channel' to make responses visible to everyone.