Integrate Slack with Bolt.new using incoming webhooks (no OAuth, works in the WebContainer preview) or a full Slack App with bot token for reading channels and handling slash commands (requires deployment for OAuth callbacks and event subscriptions). Start with incoming webhooks — create one in the Slack API dashboard and POST JSON messages from an API route. Deploy to Netlify or Bolt Cloud for full app features.
Add Slack Notifications and Bots to Your Bolt.new App
Slack is the default communication platform for most software teams, and connecting your Bolt.new app to Slack lets you deliver real-time notifications, trigger workflows, and build internal bots without leaving the tools your team already uses. Whether you want to alert a channel when a new user signs up, send daily digest reports, or build a custom slash command that queries your app's data, Slack's APIs make it straightforward to implement from Bolt.
There are two distinct approaches to Slack integration, and understanding which one to use saves significant setup time. Incoming webhooks are the simplest option: Slack gives you a unique HTTPS URL, and you send messages by POSTing a JSON payload to it. No OAuth flow, no token management, no complex app configuration — just a URL that works immediately. This pattern works perfectly from Bolt's WebContainer preview because it's a standard outbound HTTP call. The trade-off is that incoming webhooks are one-directional (you can only send, not receive or read Slack data) and limited to a single channel per webhook.
A full Slack App with a Bot Token unlocks the complete Slack Web API: posting to any channel, reading messages, listing workspace members, responding to slash commands, and reacting to events. The setup requires creating an app in the Slack API dashboard, configuring OAuth scopes, and installing it to your workspace. Slash commands and event subscriptions require a public webhook URL that Slack can call — which means you need to deploy before testing these features. However, once deployed to Netlify or Bolt Cloud, the full Slack API is available and your Bolt app can behave like a first-class Slack integration.
Integration method
Slack integrates with Bolt.new through two paths: incoming webhooks (a simple HTTPS POST to a fixed URL that requires no OAuth and works from the WebContainer preview) and a full Slack App using the Web API and Bot Token (which supports reading channels, posting as a bot, and handling slash commands but requires OAuth callbacks and event subscriptions that need a deployed URL). Both patterns use standard HTTP clients — no native modules required. Start with incoming webhooks for notifications; upgrade to a full Slack App after deploying when you need two-way communication.
Prerequisites
- A Slack workspace where you have permission to create apps and install integrations
- A Slack API account at api.slack.com (free, uses your Slack login)
- For incoming webhooks: a Slack app created with Incoming Webhooks feature enabled
- For full Slack App: bot token scopes configured (chat:write, channels:read, users:read) and app installed to workspace
- A deployed URL on Netlify or Bolt Cloud for testing slash commands and event subscriptions
Step-by-step guide
Create a Slack App and Get Your Incoming Webhook URL
Create a Slack App and Get Your Incoming Webhook URL
Before writing any code in Bolt, you need to create a Slack App in the Slack API dashboard and generate an incoming webhook URL. This webhook URL is all you need to start sending messages — no OAuth flow, no complex permissions. Go to api.slack.com/apps and click 'Create New App'. Choose 'From scratch', give your app a name (e.g., 'My Bolt App'), and select your Slack workspace. Once created, navigate to 'Incoming Webhooks' in the left sidebar of your app settings and toggle 'Activate Incoming Webhooks' to On. Click 'Add New Webhook to Workspace' at the bottom. Slack will ask you to authorize and select a channel — choose the channel where you want messages to appear (e.g., #notifications or #general). After authorizing, you'll see a webhook URL like `https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX`. Copy this URL. For a full Slack App with a bot token, also navigate to 'OAuth & Permissions' and add the scopes you need: `chat:write` (to post messages), `channels:read` (to list channels), and `users:read` (to list workspace members). Then click 'Install to Workspace' and copy the Bot User OAuth Token starting with `xoxb-`. Store both credentials in your Bolt project's `.env` file as `VITE_SLACK_WEBHOOK_URL` for the incoming webhook and never expose the bot token on the client side — it belongs server-side only.
1# .env file in your Bolt project root2# Incoming webhook URL (safe to use from API routes)3SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX45# Bot token for full Slack API access (server-side only, never expose to client)6SLACK_BOT_TOKEN=xoxb-your-bot-token-here78# Channel IDs for targeting specific channels9SLACK_NOTIFICATIONS_CHANNEL_ID=C0000000010SLACK_ERRORS_CHANNEL_ID=C00000001Pro tip: Incoming webhook URLs are channel-specific — each webhook can only post to one channel. Create multiple webhooks in your Slack App for different channels. The bot token (xoxb-) can post to any channel the bot has been invited to.
Expected result: You have a Slack App created at api.slack.com, an incoming webhook URL for at least one channel, and optionally a bot token. These credentials are stored in your Bolt project's .env file.
Prompt Bolt to Build the Slack Notification API Route
Prompt Bolt to Build the Slack Notification API Route
With your credentials in the .env file, prompt Bolt to create the server-side API route that handles Slack message sending. For a Next.js project, this will be an API route at `app/api/slack/notify/route.ts`. For a Vite project, you'll use a Supabase Edge Function. The API route pattern is essential here — your Slack bot token and webhook URL must never be exposed in client-side JavaScript bundles where users can find them in browser DevTools. All Slack API calls happen server-side. The generated route should accept a POST request with a message payload, validate the request, and forward it to Slack using either the incoming webhook URL or the `@slack/web-api` SDK. Bolt will generate the correct handler based on your project type. Review the generated code to ensure environment variables are accessed via `process.env.SLACK_BOT_TOKEN` (Next.js) or `import.meta.env` (Vite/client) — the bot token should only appear in server-side code paths. For the incoming webhook approach, no SDK is needed — a simple `fetch()` POST to the webhook URL with a JSON body is sufficient and works perfectly in the WebContainer preview for outbound calls.
Create a Slack notification system for my Next.js app. Add an API route at app/api/slack/notify/route.ts that accepts POST requests with a JSON body containing: message (string), channel (optional string, defaults to #notifications), and severity (optional: 'info' | 'warning' | 'error'). Use the SLACK_WEBHOOK_URL environment variable to post incoming webhook messages. Format messages as Slack Block Kit with a colored header based on severity (green for info, yellow for warning, red for error) and the message text. Return a success response when the message is sent.
Paste this in Bolt.new chat
1// app/api/slack/notify/route.ts2import { NextResponse } from 'next/server';34type Severity = 'info' | 'warning' | 'error';56const SEVERITY_COLORS: Record<Severity, string> = {7 info: '#36a64f',8 warning: '#ffcc00',9 error: '#ff0000',10};1112export async function POST(request: Request) {13 const webhookUrl = process.env.SLACK_WEBHOOK_URL;14 if (!webhookUrl) {15 return NextResponse.json({ error: 'SLACK_WEBHOOK_URL not configured' }, { status: 500 });16 }1718 const { message, channel, severity = 'info' } = await request.json();1920 const payload = {21 blocks: [22 {23 type: 'header',24 text: {25 type: 'plain_text',26 text: severity === 'error' ? '🚨 Error Alert' : severity === 'warning' ? '⚠️ Warning' : 'ℹ️ Notification',27 },28 },29 {30 type: 'section',31 text: {32 type: 'mrkdwn',33 text: message,34 },35 },36 ],37 attachments: [38 {39 color: SEVERITY_COLORS[severity as Severity] ?? SEVERITY_COLORS.info,40 },41 ],42 ...(channel ? { channel } : {}),43 };4445 const response = await fetch(webhookUrl, {46 method: 'POST',47 headers: { 'Content-Type': 'application/json' },48 body: JSON.stringify(payload),49 });5051 if (!response.ok) {52 const errorText = await response.text();53 return NextResponse.json({ error: `Slack error: ${errorText}` }, { status: 500 });54 }5556 return NextResponse.json({ success: true });57}Pro tip: Slack's incoming webhook ignores the 'channel' field in the payload — the channel is fixed when you create the webhook. To send to multiple channels, create separate webhooks for each channel or switch to the full Slack Web API with a bot token.
Expected result: Bolt generates a working /api/slack/notify endpoint. Sending a POST request to it from your app logic delivers a formatted Block Kit message to your Slack channel.
Add the Slack Web API for Bot Features (Optional)
Add the Slack Web API for Bot Features (Optional)
If you need more than one-way notifications — such as reading channel history, listing workspace members, or replying in threads — you'll need to use the Slack Web API with your bot token. Install the official `@slack/web-api` package and create a server-side Slack client. This approach lets you call any Slack API method: `conversations.list` to list channels, `users.list` to get workspace members, `chat.postMessage` to post to any channel the bot has joined, and `reactions.add` to add emoji reactions to messages. All these calls are standard outbound HTTPS requests and work perfectly from the WebContainer during development. The key difference from incoming webhooks is that the bot token has a broader set of permissions and can interact with Slack data rather than just pushing messages. To use a method, ensure the corresponding OAuth scope is added to your Slack App in the OAuth & Permissions settings — `channels:read` for listing channels, `users:read` for user data, `reactions:write` for reactions. After adding new scopes, reinstall the app to your workspace to get an updated token.
Add a Slack bot integration to my app using the @slack/web-api package. Create a utility file lib/slack.ts that exports a configured WebClient using the SLACK_BOT_TOKEN environment variable. Add functions: postMessage(channel: string, text: string, blocks?: any[]) for posting messages, listChannels() for fetching all public channels, and getWorkspaceUsers() for listing all workspace members. All functions should be server-side only. Add an API route at app/api/slack/post/route.ts that uses postMessage to send a notification.
Paste this in Bolt.new chat
1// lib/slack.ts — server-side only, import from API routes and server components2import { WebClient } from '@slack/web-api';34if (!process.env.SLACK_BOT_TOKEN) {5 console.warn('SLACK_BOT_TOKEN is not set — Slack integration disabled');6}78export const slack = new WebClient(process.env.SLACK_BOT_TOKEN);910export async function postMessage(11 channel: string,12 text: string,13 blocks?: object[]14) {15 return slack.chat.postMessage({16 channel,17 text,18 ...(blocks ? { blocks } : {}),19 });20}2122export async function listChannels() {23 const result = await slack.conversations.list({24 types: 'public_channel',25 limit: 100,26 });27 return result.channels ?? [];28}2930export async function getWorkspaceUsers() {31 const result = await slack.users.list({});32 return (result.members ?? []).filter((m) => !m.is_bot && !m.deleted);33}3435export async function addReaction(36 channel: string,37 timestamp: string,38 emoji: string39) {40 return slack.reactions.add({ channel, timestamp, name: emoji });41}Pro tip: The @slack/web-api package is pure JavaScript with no native modules, making it fully compatible with Bolt's WebContainer. Install it with: npm install @slack/web-api
Expected result: Your app can now call Slack API methods server-side — posting to any channel the bot has joined, listing workspace members, and managing reactions.
Set Up Slash Commands and Event Subscriptions After Deploying
Set Up Slash Commands and Event Subscriptions After Deploying
Slash commands and Slack event subscriptions are the features that make your Bolt app feel like a real Slack integration — users can type /mycommand in any Slack channel and your app responds with live data, or Slack notifies your app when specific events happen (new messages, channel joins, reactions). Both features require Slack to send HTTP POST requests to your app, which means they absolutely cannot work in Bolt's WebContainer preview. The WebContainer has no public-facing URL — it's a browser-local environment with a private networking layer managed by Service Workers. Slack's servers cannot reach `localhost` or the dynamic WebContainer preview URLs. The solution is to deploy first. Once you have a deployed URL on Netlify (`https://your-app.netlify.app`) or Bolt Cloud (`https://your-app.bolt.host`), you can configure slash commands and event subscriptions. In the Slack API dashboard, go to 'Slash Commands' and click 'Create New Command'. Enter your command name (e.g., /stats), the request URL pointing to your deployed handler (e.g., `https://your-app.netlify.app/api/slack/commands`), and a description. For event subscriptions, go to 'Event Subscriptions', enable them, enter your deployed URL as the Request URL (Slack will send a challenge to verify), and subscribe to the bot events you need. Bolt can generate the slash command handler — it needs to respond within 3 seconds or return an acknowledgment and process async.
Create a Slack slash command handler at app/api/slack/commands/route.ts that handles POST requests from Slack. Verify the request is from Slack using the SLACK_SIGNING_SECRET. Parse the slash command payload and handle /stats by fetching user count and total orders from Supabase, then return a formatted Slack response with the stats. Handle /help by returning a list of available commands. Return responses in the Slack message format with blocks.
Paste this in Bolt.new chat
1// app/api/slack/commands/route.ts2import { NextRequest, NextResponse } from 'next/server';3import crypto from 'crypto';4import { createClient } from '@supabase/supabase-js';56// Verify request is genuinely from Slack7function verifySlackRequest(body: string, timestamp: string, signature: string): boolean {8 const signingSecret = process.env.SLACK_SIGNING_SECRET!;9 const baseString = `v0:${timestamp}:${body}`;10 const hmac = crypto.createHmac('sha256', signingSecret);11 hmac.update(baseString);12 const computedSig = `v0=${hmac.digest('hex')}`;13 return crypto.timingSafeEqual(Buffer.from(computedSig), Buffer.from(signature));14}1516export async function POST(request: NextRequest) {17 const body = await request.text();18 const timestamp = request.headers.get('x-slack-request-timestamp') ?? '';19 const signature = request.headers.get('x-slack-signature') ?? '';2021 if (!verifySlackRequest(body, timestamp, signature)) {22 return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });23 }2425 const params = new URLSearchParams(body);26 const command = params.get('command');27 const userId = params.get('user_id');2829 if (command === '/stats') {30 const supabase = createClient(31 process.env.NEXT_PUBLIC_SUPABASE_URL!,32 process.env.SUPABASE_SERVICE_ROLE_KEY!33 );34 const { count: userCount } = await supabase35 .from('users')36 .select('*', { count: 'exact', head: true });3738 return NextResponse.json({39 response_type: 'in_channel',40 blocks: [41 {42 type: 'header',43 text: { type: 'plain_text', text: '📊 App Stats' },44 },45 {46 type: 'section',47 fields: [48 { type: 'mrkdwn', text: `*Total Users*\n${userCount ?? 0}` },49 ],50 },51 ],52 });53 }5455 return NextResponse.json({56 response_type: 'ephemeral',57 text: 'Unknown command. Try /stats or /help.',58 });59}Pro tip: Slack requires slash command responses within 3 seconds. For operations that take longer (database queries, external API calls), immediately return a 200 response with a 'thinking' message, then send the actual response to the response_url using a follow-up POST request.
Expected result: After deploying and configuring the slash command in the Slack API dashboard, typing /stats in any channel where your bot is present returns live stats from your database.
Deploy and Set Environment Variables
Deploy and Set Environment Variables
Once your Slack integration code is working in development (outbound webhook notifications can be tested in the preview, but slash commands require deployment), deploy your app to Netlify or Bolt Cloud. When deploying to Netlify, connect your app from Bolt's Settings → Applications → Netlify, or push to GitHub and connect the repo to Netlify. After deploying, add your Slack credentials as environment variables in Netlify: go to Site Configuration → Environment Variables → Add a variable. Add `SLACK_WEBHOOK_URL`, `SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET`, and any channel IDs you use. Environment variables added to Netlify are injected into your serverless functions at runtime — they're never exposed to the browser. Redeploy after adding new variables for them to take effect. For Bolt Cloud deployments, add the same variables in your project's environment settings. Once deployed and environment variables are set, update the Slack API dashboard with your production URLs: the Request URL for slash commands, the event subscription URL, and register any OAuth redirect URIs. Test the full integration by using your slash command in Slack and verifying messages appear in the correct channels.
Add a health check endpoint at app/api/slack/health/route.ts that verifies Slack connectivity by sending a test message to the #notifications channel using the SLACK_WEBHOOK_URL environment variable. It should return { status: 'ok', slack: 'connected' } on success or { status: 'error', message: '...' } on failure. This helps verify the integration works after deploying.
Paste this in Bolt.new chat
1// netlify.toml — if deploying to Netlify2[build]3 command = "npm run build"4 publish = ".next"56[[redirects]]7 from = "/*"8 to = "/.netlify/functions/__nextjs"9 status = 2001011# Add in Netlify Dashboard → Site Configuration → Environment Variables:12# SLACK_WEBHOOK_URL = https://hooks.slack.com/services/...13# SLACK_BOT_TOKEN = xoxb-...14# SLACK_SIGNING_SECRET = your-signing-secret15# SLACK_NOTIFICATIONS_CHANNEL_ID = C00000000Pro tip: After deploying, use the 'Send test event' button in the Slack API dashboard to verify your event subscription URL is reachable. For slash commands, install the app to your workspace and test in a DM with the bot first before rolling out to public channels.
Expected result: Your app is deployed with Slack credentials set as environment variables. Incoming webhook notifications work, and if configured, slash commands and event subscriptions receive payloads from Slack at your production URL.
Common use cases
New User Signup Notifications
Send an automatic Slack message to your team's #signups channel every time a new user creates an account. The notification includes the user's email, signup timestamp, and referral source. Incoming webhooks handle this perfectly — no OAuth needed, works in the preview.
Add a Slack notification to my app that sends a message to our team Slack channel whenever a new user signs up. Use an incoming webhook URL stored in the .env file. The message should include the user's email, the signup date, and which page they signed up from. Format it as a Slack Block Kit message with a header and two fields.
Copy this prompt to try it in Bolt.new
Internal Admin Bot with Slash Commands
Build a Slack bot that responds to slash commands like /stats to return your app's key metrics or /users to list recent signups. The bot fetches live data from your Supabase database and formats it as a Slack message response. Requires a deployed URL for Slack to send slash command payloads to.
Create a Slack bot for my app that handles a /dashboard slash command. When someone uses /dashboard in Slack, fetch the last 7 days of signups and revenue from Supabase and respond with a formatted Slack message showing total users, new users this week, and total revenue. Use the Slack Web API with a bot token stored in the .env file. Set up the POST handler at /api/slack/commands.
Copy this prompt to try it in Bolt.new
Automated Error Alerting
Send Slack alerts to an #errors channel when server errors occur, API calls fail, or important background jobs encounter exceptions. Each alert includes the error message, stack trace summary, and a link to the affected page. Critical for production monitoring when you don't want to check logs manually.
Add Slack error alerting to my app. Create a utility function sendSlackAlert(message, severity, context) that posts a formatted Slack Block Kit message to our #errors channel using an incoming webhook. Severity options: 'critical' (red), 'warning' (yellow), 'info' (blue). Call it from the global error handler and from the API route for failed payment processing.
Copy this prompt to try it in Bolt.new
Troubleshooting
Slack webhook POST returns 'invalid_payload' or messages aren't appearing in the channel
Cause: The JSON payload is malformed, the Content-Type header is missing, or the incoming webhook URL has been deleted or revoked in the Slack App settings.
Solution: Ensure you're sending Content-Type: application/json with your POST request and that the body is valid JSON. Verify the webhook URL in your .env matches the current active webhook in the Slack App dashboard under Incoming Webhooks. Rotating or deleting a webhook in Slack immediately invalidates the URL.
1// Correct fetch call for incoming webhooks2await fetch(process.env.SLACK_WEBHOOK_URL!, {3 method: 'POST',4 headers: { 'Content-Type': 'application/json' },5 body: JSON.stringify({ text: 'Hello from Bolt.new!' }),6});Slash commands or event subscriptions say 'Your URL didn't respond with the value of the challenge parameter'
Cause: Slack sends a verification challenge when you first register an event subscription URL. Your endpoint must respond with the challenge value. This also cannot be tested in Bolt's WebContainer preview — only a deployed URL works.
Solution: Deploy your app first, then register the URL. Ensure your event subscription handler responds to POST requests with the challenge value when Slack sends { type: 'url_verification', challenge: '...' }. The WebContainer preview URL is not accessible by Slack's servers — you must use a deployed Netlify or Bolt Cloud URL.
1// Handle Slack URL verification challenge2export async function POST(request: Request) {3 const body = await request.json();4 // Slack sends this to verify your endpoint5 if (body.type === 'url_verification') {6 return Response.json({ challenge: body.challenge });7 }8 // Handle real events here9}Bot token API calls return 'not_in_channel' when posting messages
Cause: The Slack bot hasn't been invited to the channel you're trying to post in. Bots must be explicitly added to channels before they can post.
Solution: In Slack, open the channel you want the bot to post in, type /invite @YourBotName, or go to the channel settings and add the bot as a member. For public channels, you can also call conversations.join with the channel ID using the bot token.
CORS error when calling Slack API directly from the browser (client-side code)
Cause: Slack's API does not allow direct browser-to-API calls for bot tokens. The Slack Web API must be called server-side because bot tokens cannot be exposed in client-side JavaScript.
Solution: Move all Slack API calls to an API route (app/api/slack/route.ts for Next.js) or a Supabase Edge Function. Never import @slack/web-api or use the bot token in components or client-side hooks. Call your internal API route from the client instead.
Best practices
- Use incoming webhooks for simple one-way notifications — they're simpler to set up, don't need OAuth, and work in Bolt's preview for outbound calls
- Never expose your Slack bot token (xoxb-) in client-side code — always access it through an API route or server action where it stays in process.env
- Store your Slack signing secret and verify request signatures on every incoming request from Slack to prevent spoofed slash commands and events
- Test outbound Slack messages (webhook notifications) in Bolt's WebContainer preview, but deploy before testing slash commands and event subscriptions which require a public URL
- Use Slack Block Kit for message formatting instead of plain text — it renders much better and supports interactive components like buttons and dropdown menus
- Create a dedicated Slack App for each project environment — use separate apps for development and production to avoid mixing notification channels
- Handle rate limits gracefully: Slack limits Tier 1 methods to 1 request/minute and Tier 3 to 50+ requests/minute — add retry logic with exponential backoff for critical notifications
- Use Slack's message thread reply feature for follow-up information rather than posting multiple messages to the channel, reducing channel noise
Alternatives
Microsoft Teams is the better choice for organizations already using Microsoft 365 — it integrates deeply with Outlook, SharePoint, and Azure Active Directory.
Flock is a more affordable Slack alternative popular with smaller teams in South Asia that offers similar bots and channel functionality at lower cost.
Chanty is a simple team chat tool with unlimited message history on the free plan — a good Slack alternative for budget-conscious small teams.
Frequently asked questions
Can I send Slack messages from Bolt.new without deploying first?
Yes, for outbound messages using incoming webhooks. Create an incoming webhook URL in the Slack API dashboard and POST JSON to it from your API route — this is a standard outbound HTTP call that works in Bolt's WebContainer preview. Slash commands and event subscriptions require a deployed public URL because Slack needs to send HTTP requests to your app, which cannot reach the WebContainer's browser-local environment.
Does Bolt.new have a native Slack integration like it does for Stripe?
No, Bolt.new does not have a dedicated native Slack panel in Settings like it does for Stripe. Slack integration is implemented through standard API routes using incoming webhooks or the @slack/web-api SDK. Prompt Bolt to 'Add Slack notifications using incoming webhooks' and it will generate the API route and helper functions based on your .env credentials.
How do I get my Slack bot token and signing secret?
Go to api.slack.com/apps, select your app, and find 'OAuth & Permissions' for the bot token (starts with xoxb-) after installing the app to your workspace. The signing secret is under 'Basic Information' → 'App Credentials' → 'Signing Secret'. You need both for a full Slack App integration — the bot token for making API calls and the signing secret for verifying incoming slash command requests.
Can I read messages from a Slack channel in my Bolt.new app?
Yes, using the Slack Web API with a bot token. Add the `channels:history` scope to your Slack App, install it to your workspace, invite the bot to the channel, and call slack.conversations.history({ channel: channelId }) from your API route. This is a standard HTTPS request that works in both the WebContainer preview and after deployment.
How do I send Slack alerts only in production and not during local development?
Use an environment check in your Slack notification helper: only call the Slack API when process.env.NODE_ENV === 'production' or when a specific SLACK_ENABLED=true environment variable is set. In Bolt's WebContainer, set SLACK_ENABLED to false in your .env file and set it to true only in your Netlify or Bolt Cloud environment variables.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation