Create the app from a manifest
At api.slack.com/apps choose Create New App, From a manifest. A manifest keeps configuration in version control:
display_information:
name: Deploy Helper
description: Posts deploy status and runs approved rollbacks
features:
bot_user: { display_name: deploy-helper, always_online: false }
slash_commands:
- command: /deploy
description: Show or trigger deploys
usage_hint: "status | rollback <service>"
should_escape: false
oauth_config:
scopes:
bot: [app_mentions:read, chat:write, commands]
settings:
event_subscriptions:
bot_events: [app_mention]
interactivity: { is_enabled: true }
socket_mode_enabled: true
Install the app to your workspace to get a bot token (xoxb-...). Copy the signing secret from Basic Information.
Scopes: ask for less
Scopes define what the token can do. chat:write posts messages; app_mentions:read receives mentions; commands enables slash commands. Avoid broad scopes such as channels:history or users:read.email unless a feature truly needs them: admins review scopes before approving apps, and every scope is a risk if the token leaks.
Receiving events
Events API over HTTP. Slack POSTs events to your Request URL. When you set the URL, Slack sends a url_verification payload: respond with its challenge. Every request must be acknowledged with 200 within 3 seconds; otherwise Slack retries up to three times with X-Slack-Retry-Num and X-Slack-Retry-Reason headers. Deduplicate on event_id.
Socket Mode. Your app opens a WebSocket to Slack using an app-level token (xapp-..., scope connections:write). No public URL is needed, which is ideal for internal tools behind a firewall or for local development. You still acknowledge each envelope.
Verify every HTTP request
Slack signs requests with your signing secret. Compute v0= + HMAC-SHA256 of v0:{X-Slack-Request-Timestamp}:{raw body} and compare with X-Slack-Signature in constant time. Reject timestamps older than five minutes to block replays. Bolt does this for you; the webhook tester has a Slack mode for debugging.
A Bolt app
import bolt from "@slack/bolt";
const { App } = bolt;
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
appToken: process.env.SLACK_APP_TOKEN, // xapp- token for Socket Mode
socketMode: true,
});
app.command("/deploy", async ({ command, ack, respond }) => {
await ack(); // acknowledge within 3 s
const [action, service] = command.text.trim().split(/\s+/);
if (action === "status") return respond(await deployStatus());
if (action === "rollback" && service) {
return respond({
response_type: "ephemeral",
blocks: [
{ type: "section", text: { type: "mrkdwn", text: `Roll back *${service}* to the previous release?` } },
{ type: "actions", elements: [
{ type: "button", text: { type: "plain_text", text: "Roll back" }, style: "danger", action_id: "rollback", value: service },
{ type: "button", text: { type: "plain_text", text: "Cancel" }, action_id: "cancel" } ] },
],
});
}
return respond("Usage: /deploy status | rollback <service>");
});
app.action("rollback", async ({ ack, body, action, respond }) => {
await ack();
if (!(await isOnCall(body.user.id))) return respond("Only the on-call engineer can roll back.");
await rollback(action.value, { requestedBy: body.user.id });
await respond(`Rollback of ${action.value} started by <@${body.user.id}>.`);
});
await app.start();
Note the authorization check inside the action handler: anyone who can see a button can click it.
Block Kit
Messages, modals and the App Home tab are built from blocks: sections, actions, inputs, context, dividers. Prototype in the Block Kit Builder. Always include a top-level text fallback for notifications and screen readers.
Rate limits
Web API methods are grouped in tiers, from Tier 1 (about one request per minute) to Tier 4 (100+ per minute). chat.postMessage has a special limit of roughly one message per second per channel, with short bursts allowed. On 429 read the Retry-After header. For bulk notifications, queue and spread them.
Distribution
Internal apps are installed in one workspace. To let other workspaces install yours, enable distribution and implement the OAuth flow, storing one token per workspace (encrypted at rest). Listing in the Slack Marketplace requires a security review, a privacy policy and a support contact.