2026-06-17

Automate Slack With OpenClaw (2026)

A practical guide to the official steipete/slack skill on ClawHub: react, send, edit, pin, look up members — and ship a real on-call acknowledgment workflow without writing a custom plugin.

2026-06-17

Automate Slack With OpenClaw (2026)

A practical guide to the official steipete/slack skill on ClawHub: react, send, edit, pin, look up members — and ship a real on-call acknowledgment workflow without writing a custom plugin.

Slack is where most teams talk. OpenClaw is where most teams run the agent. The two already know how to talk: the steipete/slack skill on ClawHub ships with OpenClaw as a first-class tool — 1.4k installs, MIT-0 licensed, security audit passing — and it covers everything a team actually needs from a Slack bot: reactions, messages, pins, member info, custom emoji. You do not need a custom plugin, a Slack app of your own, or a long-lived daemon.

This tutorial shows four real patterns for using the skill from the agent:

  1. React to a message — drop a :white_check_mark: on completed tasks, ack mentions, mark "looking into it".
  2. Send, edit, and delete messages — post a deploy summary, edit it when the build goes red, retract it when the deploy rolls back.
  3. Pin and list pinned items — turn Slack into a low-cost shared scratchpad for decisions, weekly status, on-call handoffs.
  4. Package the common flows as a slack-ops skill — reusable across workers, installable with one command.

We will end with a working "on-call acknowledgment + status updates" example that ties all four patterns together. It replaces a 300-line Slack Bolt app with one TaskFlow.

What you can do with Slack + OpenClaw

The steipete/slack skill exposes five action groups. Every group is a tool the agent can call directly; no glue code, no SDK, no token juggling.

Action group What the agent can do Inputs
reactions Add or list reactions on a message channelId, messageId, emoji
messages Read, send, edit, delete messages channelId or to, content, messageId
pins Pin, unpin, list pinned messages in a channel channelId, messageId
memberInfo Look up a user (display name, presence, timezone) userId
emojiList List custom emoji available in the workspace (none)

A quick note on who built it, because it is the first thing to check when you adopt a skill. The slack skill is maintained by Peter Steinberger (@steipete) under the MIT-0 license, currently on v1.0.0, with a passing security audit on ClawHub. 1.4k installs and 158 stars as of writing. If you have ever shipped a long-form Slack integration by hand, this is the skill you wish you had at the time.

Setup: install the skill and configure the bot token

Two steps. Total time, under five minutes.

1. Install the skill from ClawHub.

openclaw skills install slack
          

This drops the skill into ~/.openclaw/skills/slack/. After install, restart the agent so it picks up the new tool set. The skill shows up in the agent's available tools as slack with the five action groups above.

2. Configure the Slack bot token.

The skill uses a single Slack bot token configured for the agent. Create a Slack app (or reuse an existing one) with the bot token scopes your workflow needs — the minimum useful set is chat:write, reactions:write, pins:write, users:read, emoji:read. Install the app to your workspace, copy the bot user OAuth token (xoxb-…), and store it the same way we store every other secret:

# In the worker's env (or .env.local on a dedicated server)
          export SLACK_BOT_TOKEN=$(cat ~/.secrets/slack-bot-token)
          chmod 600 ~/.secrets/slack-bot-token
          

Never echo the token. Never commit it. Never paste it into a chat with the agent — write it to the file once, then export it from the file.

That is the whole setup. The agent now has a slack tool and can call any of the five action groups in the next reasoning step.

Pattern 1: react to a message

The simplest possible integration. The agent reads an incoming message, decides it is "done", and adds a :white_check_mark: reaction. This is the right entry point for any triage workflow because reactions are zero-cost, idempotent, and visible to everyone in the channel.

The call:

{
          "action": "react",
          "channelId": "C01ABCDEF",
          "messageId": "1712023032.1234",
          "emoji": ":white_check_mark:"
          }
          

The skill translates the emoji name to the right Unicode codepoint and posts the reaction through the Slack Web API. The agent's loop sees the success response and moves on.

Why start with reactions. Reactions are the cheapest Slack action that still produces visible, durable state. The bot does not need to compose text, cannot accidentally spam a channel, and the reaction shows up immediately for every reader. The agent can use this to acknowledge work in progress (:eyes:), mark "handled" (:white_check_mark:), or escalate (:fire:). Once the agent is trusted to react, you can graduate it to sending messages in pattern 2.

A real failure mode. If the bot token is missing the reactions:write scope, Slack returns missing_scope. Make the skill check the error code, surface a clear message ("the bot token needs reactions:write — go to Slack app settings, add it, reinstall"), and stop. The agent's next reasoning step should fix the config, not loop on retries.

Pattern 2: send, edit, and delete messages

The meat of Slack automation. The agent posts an update, edits it when reality changes, and deletes it if the post was wrong. Slack messages are mutable — use that.

Send a message:

{
          "action": "sendMessage",
          "to": "channel:C01ABCDEF",
          "content": ":rocket: Deploy to production started — `golem-editor@a1b2c3d`"
          }
          

The to field accepts either channel:<id> or user:<id>. The skill resolves the channel name to an ID behind the scenes if you pass the bare name, but passing the ID is faster and avoids ambiguity.

Edit a message:

{
          "action": "editMessage",
          "channelId": "C01ABCDEF",
          "messageId": "1712023032.1234",
          "content": ":white_check_mark: Deploy to production succeeded — https://golem-editor-phi.vercel.app"
          }
          

Slack exposes editMessage so the bot can correct itself in place. The right pattern is: post a "started" message, edit it to "succeeded" or "errored" when the result is known. Readers see one slot in the channel, not two, and the "edited" marker tells them it is live state, not stale text.

Delete a message:

{
          "action": "deleteMessage",
          "channelId": "C01ABCDEF",
          "messageId": "1712023032.1234"
          }
          

Delete is for retracted posts: a deploy that succeeded and then immediately rolled back, a draft incident that turned out to be a misfire, a "ping me" that turned out to be the wrong person. Use it sparingly — Slack does not show deleted bot messages by default, so a deleted message leaves a gap. For most cases, an edit is the better answer.

Read recent messages:

{
          "action": "readMessages",
          "channelId": "C01ABCDEF",
          "limit": 20
          }
          

The agent can read the channel history before it posts. This is the right move when the workflow is "post a status update that does not duplicate what someone else already said". Read first, then send.

Pattern 3: pin and list pinned items

Slack pins are an underrated state surface. A pinned message is visible to every reader in the channel, persists until unpinned, and shows up in the channel sidebar under "Pinned". The right way to think about them: pins are the agent's long-term memory in a channel.

Pin a message:

{
          "action": "pinMessage",
          "channelId": "C01ABCDEF",
          "messageId": "1712023032.1234"
          }
          

Unpin a message:

{
          "action": "unpinMessage",
          "channelId": "C01ABCDEF",
          "messageId": "1712023032.1234"
          }
          

List pins:

{
          "action": "listPins",
          "channelId": "C01ABCDEF"
          }
          

The pattern: pin the latest status, unpin the previous one. A weekly status channel that pins "Week of 2026-06-15 — 12 shipped, 3 in review, 1 incident" lets every new joiner read the team's last week without scrolling. A deploy channel that pins the latest green deploy URL gives every on-call a single link to share. An incident channel that pins the incident commander and the runbook URL turns Slack into a low-cost incident dashboard.

A note on limits. Slack caps a channel at 100 pinned items. Stay well below that — the practical ceiling is around 20 — and unpin stale items aggressively. The skill does not enforce the cap; the agent should.

Pattern 4: package it as a slack-ops skill

The four primitives — react, send/edit/delete, pin, member info — are the building blocks. The next step is packaging the most common flows as a reusable skill so other workers on the platform can install slack-ops and have it just work. This is the same pattern we use in the Gmail skill, the Telegram skill, and the Vercel deploy skill.

A minimal slack-ops skill pack contains three files:

slack-ops/
          SKILL.md           # what the skill does, when to load it
          post-deploy.sh     # the canonical "post a deploy status" flow
          triage.sh          # the canonical "react + classify + pin" flow
          README.md          # env vars, secrets, troubleshooting
          

The SKILL.md is the only file the agent reads first; it decides whether the skill is relevant. Keep it short, concrete, and example-driven.

# slack-ops

          ## When to use
          - The user asks to post, edit, react, or pin something in Slack.
          - The agent has just done a deploy, fixed an alert, or resolved an incident and needs to tell a channel.
          - A new joiner asks "what is the latest status in #deploys?" — the agent should pin-listing, not scroll history.

          ## Preconditions
          - SLACK_BOT_TOKEN is set in the worker env.
          - The `slack` skill from ClawHub is installed (`openclaw skills install slack`).
          - The bot has the scopes it needs (chat:write, reactions:write, pins:write, users:read).

          ## Steps
          1. Decide which action group fits: reactions for ack/marks, messages for announcements, pins for state, memberInfo for people context.
          2. Run the matching flow from post-deploy.sh or triage.sh, or call the slack tool directly if neither fits.
          3. For status announcements: post, then edit when the result is known. Never post "started" and "succeeded" as two messages in the same slot.
          4. For weekly status: pin the new one, unpin the previous one. Keep the pinned count under 10.
          5. Return the message ID and the action taken so the agent's reasoning step has evidence.

          ## Failure modes
          - `missing_scope` on any action → tell the user the bot token needs the listed scope, point at Slack app settings.
          - `channel_not_found` → check whether the channel is in the bot's allowed list, or whether the user meant a private channel.
          - `message_not_found` on edit/delete → the original message was already deleted, or the message ID is stale. Read recent messages and retry.
          

The install slack-ops command in OpenClaw drops this folder into ~/.openclaw/skills/slack-ops/, after which any agent on the worker can pick it up on the next turn. The skill is layered on top of the ClawHub slack skill — slack-ops is opinionated flows, slack is the raw tool.

End-to-end: on-call acknowledgment + status updates

A real flow that uses all four patterns. It is the kind of workflow a Slack Bolt app would take 300 lines to express; in OpenClaw it is one TaskFlow.

The story. It is 3 a.m. A PagerDuty alert fires for golem-editor production latency. The on-call engineer is asleep. The agent, which is wired to PagerDuty via the same Webhooks plugin pattern we describe in the Vercel deploy guide, wakes up first. It needs to (a) acknowledge the alert in #on-call so the rest of the team sees someone is on it, (b) react to the original PagerDuty mirror message in #incidents so the alert queue is visually triaged, (c) post a status update in #on-call, (d) edit the status as it investigates, (e) pin the incident commander and runbook URL, (f) on resolution, post the green summary and unpin the stale one.

The shape of the TaskFlow.

# oncall/incident-ack

          ## Goal
          Acknowledge a PagerDuty incident in Slack, post a status thread, and pin the runbook — all before the on-call engineer wakes up.

          ## Inputs
          - incident_id (string)
          - service (string, e.g. "golem-editor")
          - severity (string: SEV1 | SEV2 | SEV3)
          - pd_mirror_message_ts (string, the message ID of the PagerDuty mirror in #incidents)

          ## Steps
          1. Send a message to `#on-call` with `sendMessage`: ":rotating_light: <severity> alert on <service> — incident <incident_id>. Agent is on it." Capture the `messageId` of the posted message.
          2. React to the PagerDuty mirror in `#incidents` with `:eyes:` using `react`. This visually marks the alert as "agent is here".
          3. Look up the on-call engineer's user via `memberInfo` and post a one-line ping: "@<user> heads up: <service> <severity>". Skip if the on-call is the agent itself.
          4. Pin a fresh message in `#on-call` with `pinMessage`: the incident commander, the runbook URL, and a link to the PagerDuty incident.
          5. Every 60 seconds, post a status update with `sendMessage` referencing the original message, and `editMessage` the original post to reflect the latest known state. (Use `readMessages` first to avoid duplicating updates.)
          6. On resolution, post the green summary, edit the original to `:white_check_mark: resolved — <duration>`, unpin the incident pin, and react `:white_check_mark:` to the original PagerDuty mirror.

          ## Outputs
          - message ID of the acknowledgment post
          - message ID of the resolution post
          - list of pinned message IDs touched

          ## Failure modes
          - `missing_scope` on pin → drop the pin step, log it, continue. Status updates still work.
          - `channel_not_found` on `#on-call` → fall back to DMs to the on-call engineer via `sendMessage` with `to: user:<id>`.
          - PagerDuty mirror message ID is stale → `readMessages` on `#incidents`, find the latest alert from the same `incident_id`, retry the reaction.
          

The agent now runs the same workflow every time an alert fires. The slack skill from ClawHub is the toolset; the slack-ops skill is the opinionated flow; the TaskFlow is the procedure. No custom plugin code, no long-lived daemon, no brittle Bolt app. The agent reads the TaskFlow markdown, picks the right tool calls, and Slack becomes a side-effect of the agent loop.

The same pattern works for any "agent drives a Slack channel" workflow: feature request triage in #product-feedback, weekly status in #all-hands, deploy updates in #deploys, customer support escalations in #support-tier2. Same four primitives, different TaskFlow.

FAQ

Does OpenClaw have an official Slack plugin?

There is an official Slack skill on ClawHub — the steipete/slack skill, maintained by Peter Steinberger under MIT-0, with a passing security audit. It is not a Slack app — it is a tool set the agent can call. For everything in this article — reacting, sending, editing, pinning, looking up members — the skill covers it without you writing a Slack app of your own.

How do I post a message to a Slack channel from an AI agent?

Install the slack skill from ClawHub (openclaw skills install slack), set SLACK_BOT_TOKEN in the worker env, then call the skill's sendMessage action with to: channel:<id> and content: "...". The bot must be invited to the channel first (/invite @<bot-name>). See Pattern 2 above.

How do I make the agent react to a Slack message?

Use the react action from the slack skill: channelId, messageId, and an emoji. Reactions are the cheapest possible agent-to-Slack side effect, so they are the right starting point when you are building trust in the bot. See Pattern 1.

Can I edit or delete a message the agent posted?

Yes. Use editMessage to correct the content in place (right move for status updates that go green or red), and deleteMessage to retract a post (right move for misfires). Slack shows an "edited" marker on edited messages, so readers can tell the difference between live state and stale text.

Can I pin a Slack message from the agent?

Yes — pinMessage, unpinMessage, and listPins are all in the skill's pins action group. Pins are the right place for state that needs to persist across the channel: weekly status, on-call assignments, incident commanders, runbook URLs. Slack caps a channel at 100 pins; stay well below that and unpin stale items.

How does the agent know which channel ID to use?

The skill accepts both channel names and IDs in the to field for sendMessage, but channel IDs (C… prefix) are faster and unambiguous. Channel lookups via conversations.list are out of scope for the skill, so the practical pattern is: the agent's TaskFlow declares the channel ID up front, or the user passes it once and the worker caches it.

Can the agent read message history?

Yes — readMessages returns the last N messages from a channel (capped by Slack's own limits). The agent should always read first when the workflow is "post something that does not duplicate what was just said".

How do I look up a Slack user?

Use memberInfo with the user's Slack ID (U… prefix). Returns display name, presence, timezone — useful for routing on-call pings to the right person in the right timezone.

What scopes does the Slack bot token need?

The minimum useful set is chat:write, reactions:write, pins:write, users:read, emoji:read. Add channels:read and groups:read if the agent needs to enumerate channels. Add chat:write.public if you want to post to channels the bot has not been invited to (rarely a good idea).

Can I receive Slack events in OpenClaw (the reverse direction)?

The slack skill is outbound-only — agent to Slack. For inbound Slack events (someone mentioned the bot, a reaction was added, a message was posted in a channel), use the OpenClaw Webhooks plugin with a Slack Events API subscription, the same pattern we describe in the Vercel deploy guide. The two compose cleanly: the slack skill is the toolset, the Webhooks plugin is the event receiver.

What does the slack skill cost?

Free. The skill is MIT-0 licensed and installed from ClawHub. The only cost is the Slack workspace itself (Free, Pro, Business+, or Enterprise Grid). The bot is just another user in your workspace.

How does this compare to a Slack Bolt app?

A Slack Bolt app is the right place for the deterministic webhook receiver, the slash command handler, and any flow that needs to push to Slack without an agent loop. OpenClaw + the slack skill is the right place for the judgment-heavy flows: deciding whether to acknowledge an alert, drafting a status update, choosing which message to pin, looking up the right person to ping. The two pair naturally — Bolt receives the event, OpenClaw runs the workflow.

Common pitfalls

  • Skipping the planning phase — Teams that jump straight into prompt engineering usually spend weeks rebuilding once they hit edge cases. Spend 30 minutes mapping the trigger, the action, and the fallback before writing the first instruction.
  • Treating the first successful run as done — A 90% pass rate still produces 1 wrong answer in 10. Set an explicit acceptance threshold (typically 99% for production) and instrument the agent to log when it falls short.
  • Skipping audit logging — If you can't replay what the agent did three weeks ago, you can't debug it, bill it, or trust it. Persist every tool call with timestamps and inputs from day one.
  • Not pinning dependency versions — A skill that worked in dev but breaks in prod because a transitive dependency bumped a major version is the most common automation outage. Pin everything in package.json / requirements.txt.

Related articles