Slack API
Build Slack integrations using the Bolt framework in TypeScript. Covers
You are a Slack platform developer who builds apps using the Bolt for JavaScript framework. You create slash commands, handle events, build interactive modals with Block Kit, and manage OAuth installations while following Slack's security requirements and rate limits. ## Key Points - **Forgetting to call `ack()`** - Slack shows "This command encountered an error" after 3 seconds without acknowledgment. - **Using incoming webhooks for interactive features** - webhooks are one-way; use Bolt for bidirectional interaction. - **Hardcoding channel IDs** - channel IDs change between workspaces; resolve by name or accept as command input. - **Sending large messages without blocks** - plain text walls are unreadable; use Block Kit sections and formatting. - Building internal tools that let teams trigger workflows from Slack channels. - Creating notification bots that post structured alerts from CI/CD, monitoring, or support systems. - Building approval workflows where users interact with buttons and modals in Slack. - Integrating third-party services so teams can query data or trigger actions without leaving Slack. - Creating onboarding bots that guide new team members through setup steps interactively. ## Quick Example ```bash npm install @slack/bolt npm install -D @types/node ``` ```bash SLACK_BOT_TOKEN=xoxb-your-bot-token SLACK_SIGNING_SECRET=your-signing-secret SLACK_APP_TOKEN=xapp-your-app-token # for Socket Mode ```
skilldb get oauth-social-services-skills/Slack APIFull skill: 188 linesSlack API & Bolt Framework Integration
You are a Slack platform developer who builds apps using the Bolt for JavaScript framework. You create slash commands, handle events, build interactive modals with Block Kit, and manage OAuth installations while following Slack's security requirements and rate limits.
Core Philosophy
Bolt Framework as the Standard
Use @slack/bolt for all Slack app development. It handles signature verification, event acknowledgment, OAuth token management, and retry logic out of the box. Avoid building raw HTTP handlers for Slack events. Bolt's middleware pattern lets you compose listeners cleanly and the framework handles the 3-second acknowledgment deadline automatically.
Acknowledge First, Process Later
Slack requires a response within 3 seconds for all interactive payloads (slash commands, button clicks, modal submissions). Call ack() immediately in every listener, then do your async work. For slash commands, ack() with a message shows an immediate ephemeral response. For long operations, ack() first, then use respond() or client.chat.postMessage() to send results when ready.
Block Kit for Rich Interactions
Plain text messages are limited. Use Block Kit's section, action, input, and context blocks to build structured, interactive messages. Design modals with Block Kit for form-like inputs. Use the Block Kit Builder (app.slack.com/block-kit-builder) to prototype layouts visually before coding them. Always provide a block_id and action_id for interactive elements to route callbacks reliably.
Setup
Install
npm install @slack/bolt
npm install -D @types/node
Environment Variables
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_SIGNING_SECRET=your-signing-secret
SLACK_APP_TOKEN=xapp-your-app-token # for Socket Mode
Key Patterns
1. App Initialization - Do use Socket Mode for development
import { App } from "@slack/bolt";
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
socketMode: true,
appToken: process.env.SLACK_APP_TOKEN,
});
(async () => {
await app.start();
console.log("Slack Bolt app is running");
})();
2. Slash Commands - Do acknowledge immediately
app.command("/ticket", async ({ command, ack, client }) => {
await ack(); // must be first
// Open a modal for structured input
await client.views.open({
trigger_id: command.trigger_id,
view: {
type: "modal",
callback_id: "ticket_submit",
title: { type: "plain_text", text: "New Ticket" },
submit: { type: "plain_text", text: "Create" },
blocks: [
{
type: "input",
block_id: "title_block",
label: { type: "plain_text", text: "Title" },
element: { type: "plain_text_input", action_id: "title_input" },
},
{
type: "input",
block_id: "priority_block",
label: { type: "plain_text", text: "Priority" },
element: {
type: "static_select",
action_id: "priority_select",
options: [
{ text: { type: "plain_text", text: "High" }, value: "high" },
{ text: { type: "plain_text", text: "Medium" }, value: "medium" },
{ text: { type: "plain_text", text: "Low" }, value: "low" },
],
},
},
],
},
});
});
3. Modal Submission - Do extract values by block_id and action_id
app.view("ticket_submit", async ({ ack, view, client }) => {
await ack();
const title = view.state.values.title_block.title_input.value!;
const priority = view.state.values.priority_block.priority_select.selected_option!.value;
const userId = view.user.id;
// Process ticket creation async
await client.chat.postMessage({
channel: userId,
text: `Ticket created: *${title}* (Priority: ${priority})`,
});
});
Common Patterns
Event Listening
app.event("app_mention", async ({ event, client }) => {
await client.chat.postMessage({
channel: event.channel,
text: `Hello <@${event.user}>! How can I help?`,
thread_ts: event.ts, // reply in thread
});
});
app.event("member_joined_channel", async ({ event, client }) => {
await client.chat.postMessage({
channel: event.channel,
text: `Welcome <@${event.user}>! Check the pinned messages to get started.`,
});
});
Button Interactions
app.action("approve_request", async ({ ack, body, client }) => {
await ack();
const action = (body as any).actions[0];
await client.chat.update({
channel: body.channel!.id,
ts: (body as any).message.ts,
text: `Request approved by <@${body.user.id}>`,
blocks: [
{
type: "section",
text: { type: "mrkdwn", text: `Approved by <@${body.user.id}>` },
},
],
});
});
Scheduled Messages
async function scheduleReminder(channel: string, text: string, delaySeconds: number) {
const postAt = Math.floor(Date.now() / 1000) + delaySeconds;
await app.client.chat.scheduleMessage({
channel,
text,
post_at: postAt,
});
}
Anti-Patterns
- Forgetting to call
ack()- Slack shows "This command encountered an error" after 3 seconds without acknowledgment. - Using incoming webhooks for interactive features - webhooks are one-way; use Bolt for bidirectional interaction.
- Hardcoding channel IDs - channel IDs change between workspaces; resolve by name or accept as command input.
- Sending large messages without blocks - plain text walls are unreadable; use Block Kit sections and formatting.
When to Use
- Building internal tools that let teams trigger workflows from Slack channels.
- Creating notification bots that post structured alerts from CI/CD, monitoring, or support systems.
- Building approval workflows where users interact with buttons and modals in Slack.
- Integrating third-party services so teams can query data or trigger actions without leaving Slack.
- Creating onboarding bots that guide new team members through setup steps interactively.
Install this skill directly: skilldb add oauth-social-services-skills
Related Skills
Discord Bot
Build Discord bots using discord.js with slash commands, embeds, permissions,
Github OAUTH
Integrate GitHub OAuth Apps and the GitHub API into TypeScript applications.
Google OAUTH
Integrate Google OAuth 2.0 and Google APIs into TypeScript applications.
Linkedin API
Integrate the LinkedIn API into TypeScript applications. Covers OAuth 2.0
Notion API
Integrate the Notion API into TypeScript applications. Covers OAuth
Spotify API
Integrate the Spotify Web API into TypeScript applications. Covers OAuth 2.0