Slack Bots with Bolt skill for Codex

Ack in three seconds, do the work afterwards. Codex builds Slack apps from a manifest kept in the repo, acknowledges every command, action and event within Slack's three-second window, and pushes slow work to the background. It picks Socket Mode or HTTP based on how you deploy, keeps scopes minimal, respects rate-limit tiers and Retry-After, and only posts to real channels after you confirm.

  • Integrations
  • $slack-bots
  • 180 lines
  • 2 files
  • ~1,900 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- slack-bots

Installs to ~/.agents/skills/slack-bots/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $slack-bots, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Builds Slack apps with Bolt for JavaScript or Python - app manifests, scopes, Socket Mode or HTTP events, slash commands, Block Kit messages, modals, interactive buttons, request signing and rate-limit handling. Use when creating a Slack bot or slash command, posting alerts or reports to channels, opening a modal from a shortcut, debugging dispatch_failed or operation_timeout errors, or moving a Slack app from Socket Mode to a public HTTP endpoint.

Use it when

  • Creating a Slack bot, slash command, shortcut or Home tab
  • Slack shows dispatch_failed or timeout errors on interactions
  • Designing Block Kit messages or modals
  • Moving a Slack app from one workspace to multi-workspace OAuth

Use something else when

How to check the result

Every command, button and modal works in a test workspace, unit tests confirm ack runs before slow work, and the logs show no X-Slack-Retry-Num headers.

The mistake it prevents

Doing database or API work before calling ack(), so Slack times out after three seconds and retries or shows the user an error.

~/.agents/skills/slack-bots/SKILL.md

--- name: slack-bots description: Builds Slack apps with Bolt for JavaScript or Python - app manifests, scopes, Socket Mode or HTTP events, slash commands, Block Kit messages, modals,… license: MIT ---

Done well means: every listener acknowledges within 3 seconds and does slow work afterwards, the app requests the smallest set of scopes, every HTTP request is signature-verified (Bolt does this when given the signing secret), messages include fallback text, and nothing posts to a real workspace channel without the user’s confirmation.

Before writing code

  1. Read AGENTS.md and the package manifest; check for an existing Bolt app, manifest.yml or manifest.json, and how the project is deployed (long-running server, container, Lambda).
  2. Choose the transport:
SituationTransport
Internal tool, behind a firewall, or local developmentSocket Mode (needs an app-level token xapp-... with connections:write)
Distributed to many workspaces, or MarketplaceHTTP Request URLs plus OAuth installation
Serverless (Lambda, Cloud Functions)HTTP with processBeforeResponse: true (Bolt JS)
  1. Secrets: SLACK_BOT_TOKEN (xoxb-...), SLACK_SIGNING_SECRET, SLACK_APP_TOKEN (Socket Mode). Never commit them; never print tokens in logs.
  2. Posting to real channels, DMing users, or installing to a production workspace is an outward-facing action: confirm with the user first. Use a test workspace for development.

Package installs and any Slack API call need network; in a Codex sandbox, ask for approval or unit-test listeners with mocked client objects.

Manifest first

Keep the app definition in the repo so scopes and commands are reviewable:

display_information:
  name: Deploy Bot
features:
  bot_user:
    display_name: deploybot
  slash_commands:
    - command: /deploy
      description: Deploy a service
      usage_hint: "[service] [env]"
settings:
  socket_mode_enabled: true
  interactivity:
    is_enabled: true
  event_subscriptions:
    bot_events: [app_mention]
oauth_config:
  scopes:
    bot: [commands, chat:write, app_mentions:read]

Add scopes only when a method needs them (the API error names the missing scope). Every scope change requires reinstalling the app.

Listener pattern (Bolt JS)

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,
});

app.command("/deploy", async ({ ack, command, client, respond }) => {
  await ack();                                    // within 3 s, before any slow work
  const [service, env = "staging"] = command.text.trim().split(/\s+/);
  if (!service) return respond({ response_type: "ephemeral", text: "Usage: /deploy <service> [env]" });

  await client.views.open({                       // trigger_id expires 3 s after the command
    trigger_id: command.trigger_id,
    view: confirmModal(service, env),
  });
});

app.view("deploy_confirm", async ({ ack, view, body, client }) => {
  const reason = view.state.values.reason.input.value ?? "";
  if (reason.length < 5) {
    return ack({ response_action: "errors", errors: { reason: "Give a short reason" } });
  }
  await ack();
  const { service, env } = JSON.parse(view.private_metadata);
  queueDeploy({ service, env, user: body.user.id, reason });   // slow work off the request path
});

app.event("app_mention", async ({ event, say }) => {
  await say({ text: `Hi <@${event.user}>`, thread_ts: event.ts });
});

await app.start(Number(process.env.PORT) || 3000);

Python Bolt is equivalent: App(token=..., signing_secret=...), @app.command("/deploy") with ack() first, and SocketModeHandler(app, app_token).start(). For async frameworks use AsyncApp.

Block Kit essentials

await client.chat.postMessage({
  channel,
  text: `Deploy of ${service} to ${env} succeeded`,     // fallback for notifications and screen readers
  blocks: [
    { type: "section", text: { type: "mrkdwn", text: `*${service}* deployed to *${env}*` } },
    { type: "context", elements: [{ type: "mrkdwn", text: `by <@${userId}> · ${sha.slice(0, 7)}` }] },
    { type: "actions", elements: [
      { type: "button", action_id: "rollback", text: { type: "plain_text", text: "Roll back" },
        style: "danger", value: JSON.stringify({ service, env, sha }) },
    ] },
  ],
});
  • Limits: 50 blocks per message, 100 per modal or Home tab; button value up to 2,000 chars; private_metadata up to 3,000 chars. Store larger state server-side and pass an ID.
  • Escape user text placed in mrkdwn: replace &, <, > with &amp;, &lt;, &gt;.
  • Prototype layouts in Block Kit Builder, then paste the JSON into code.
  • Update instead of re-posting: chat.update with the message ts from the original post.

Ack rules and timeouts

PayloadMust ack withinIf slow
Slash command, action, shortcut, view submission3 sack() first, then respond() (response_url valid 30 min) or chat.postMessage
Events API event3 s (HTTP 200)Slack retries up to 3 times with X-Slack-Retry-Num; dedupe on event_id
Modal opentrigger_id valid 3 sOpen a loading view, then views.update

On serverless, processBeforeResponse: true keeps the function alive until the handler finishes, so keep handlers short or hand off to a queue.

Rate limits

  • Web API methods have tiers; chat.postMessage allows roughly one message per second per channel with short bursts. On HTTP 429, wait for Retry-After seconds. The Node and Python SDK clients retry 429s by default; do not add a second retry layer that ignores the header.
  • Since mid-2025, conversations.history and conversations.replies are heavily limited for commercially distributed apps that are not Marketplace-approved. Check Slack’s current rate-limit page before designing features that read channel history.
  • Batch alerts: aggregate into one message per interval instead of one per event.

Gotchas

  • dispatch_failed or “operation_timeout”: the listener did not ack in time, threw before acking, or the Request URL is wrong. Check the server logs for the payload type.
  • not_in_channel: the bot must be invited to a channel before chat.postMessage (or use chat:write.public for public channels only).
  • Custom HTTP servers that parse JSON before Bolt break signature checks; let Bolt’s receiver read the raw body, or verify v0: + timestamp + : + raw body with HMAC-SHA256 yourself and reject timestamps older than five minutes.
  • User IDs, not names: mention with <@U123>; display names change.
  • Enterprise Grid: IDs can be org-wide; store team_id and enterprise_id with installations.
  • Multi-workspace apps need an installationStore; a single token only works for one workspace.

Verification

  • Run locally in Socket Mode against a test workspace; invoke each command, button and modal.
  • Unit-test listeners by calling handlers with fake ack, client and respond and asserting ack was called first.
  • Watch logs for X-Slack-Retry-Num headers; their presence means acks are too slow.
  • Before production install, review the manifest scopes with the user.

Report template

**App:** Deploy Bot, Bolt JS 4.x, Socket Mode (internal)
**Scopes:** commands, chat:write, app_mentions:read (manifest.yml in repo)
**Features:** /deploy -> confirm modal -> queued deploy; rollback button; mention reply in thread
**Ack strategy:** ack first, slow work queued; no listener exceeds 3 s in tests
**Tested in:** test workspace only; production install awaiting user approval

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Slack Bots with Bolt"
  short_description: "Ack in three seconds, do the work afterwards"
  default_prompt: "$slack-bots Build a Slack /deploy command that opens a confirmation modal before deploying"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem