Reaction-Driven Slack Automations: Turn Emoji Into Ops Actions
Trigger ops workflows from Slack reactions: ack alerts with ✅, escalate with 🚨, file tickets with 📝. AI scaffolds the handlers; you review the guardrails.
- #slack
- #chatops
- #events-api
- #automation
The lightest-weight ops interface I’ve ever shipped wasn’t a slash command or a fancy modal. It was an emoji. An alert lands in the channel, the on-call engineer reacts with ✅, and the bot acknowledges the alert, mutes the re-fires, and updates the incident — no typing, no clicking through a UI, just a reaction they were going to add anyway out of habit. Reacji-driven automation (“reacji” being Slack’s term for reaction-as-trigger) is delightful when it works and quietly dangerous when it doesn’t, because a stray emoji can fire a real action.
I built ours with AI help, and reaction triggers are a clean illustration of AI as a fast junior engineer. The model writes a reaction_added handler in seconds and will happily let any user’s any reaction trigger a production action, with no check on who reacted, which message, or whether it’s already been handled. A human reviews the guardrails before this touches a workspace, because “anyone in the channel can fire this by clicking an emoji” is a sentence that should make you nervous.
Subscribe to reaction events
Add the reactions:read scope and the reaction_added (and optionally reaction_removed) bot events. The payload tells you the emoji, who reacted, and which message:
{
"type": "reaction_added",
"user": "U123",
"reaction": "white_check_mark",
"item": { "type": "message", "channel": "C1", "ts": "1718600000.000100" }
}
The reaction field is the emoji name (white_check_mark), not the glyph. Map names to actions in a small table so the routing is explicit and auditable.
Route reactions to actions
Keep an allowlist of emoji-to-action mappings. Anything not in the map is ignored — never a default action:
const ACTIONS = {
white_check_mark: ackAlert,
rotating_light: escalate,
memo: fileTicket,
};
app.event('reaction_added', async ({ event, client }) => {
const action = ACTIONS[event.reaction];
if (!action) return; // unknown emoji → do nothing
await action({ event, client });
});
The if (!action) return is your first guardrail. An automation that does nothing for unrecognized input is the correct default.
Verify it’s the right message
A ✅ on a random message shouldn’t ack an alert. Confirm the reacted-to message is actually a bot alert before acting. Fetch it and check authorship/markers:
async function ackAlert({ event, client }) {
const { messages } = await client.conversations.history({
channel: event.item.channel, latest: event.item.ts,
inclusive: true, limit: 1,
});
const msg = messages[0];
if (msg?.bot_id !== process.env.ALERT_BOT_ID) return; // not our alert
if (!msg.metadata?.event_payload?.alertId) return; // no alert to ack
await acknowledge(msg.metadata.event_payload.alertId, event.user);
}
Attaching structured metadata to your alert messages when you post them makes this clean — the reaction handler reads the alert ID straight off the message instead of guessing from text.
Pro Tip: Reactions are not unique-per-user-per-action in any way that protects you — ten people can each add ✅. Make every reaction handler idempotent so the second, third, and tenth ✅ are no-ops. Key on the alert ID, not the reaction event.
Authorize who can trigger what
The scariest reacji is the destructive one. 🚨 escalating to a page is fine for anyone; 💥 triggering a rollback should not be. Check the reacting user’s role before any consequential action:
async function escalate({ event, client }) {
if (!(await isOnCallOrLead(event.user))) {
await client.chat.postEphemeral({
channel: event.item.channel, user: event.user,
text: 'Only on-call/leads can escalate by reaction.',
});
return;
}
await page(event.item);
}
The ephemeral nudge tells the unauthorized user privately, without announcing the failed attempt to the channel. For genuinely dangerous actions, a reaction shouldn’t be the whole gate — fall back to an explicit approval flow.
Good reacji patterns for ops
The ones that earned their place on my team:
- ✅ on an alert → acknowledge and suppress re-fires.
- 🚨 on a message → escalate / page on-call.
- 📝 on a postmortem-worthy message → file a tracking ticket with a permalink.
- 👀 on an incident → mark “someone’s looking,” update the incident dashboard.
Each is low-friction precisely because the reaction is a gesture people already make. You’re just giving it teeth — carefully.
Where AI helps, where you decide
The model is great at the routing table, the message-fetch glue, and the idempotency guard. I scaffold with Copilot or Claude and refine prompts in the prompt workspace. What it must not own is the authorization and the destructive-action policy — it doesn’t know that a rollback reaction needs a lead, or that a particular channel is externally shared and shouldn’t honor escalation reactions from outside members. Review every consequential handler, keep destructive actions behind a real gate, never give the model a live token, and verify signatures on the events endpoint that delivers these reactions over the open internet.
Conclusion
Reaction-driven automation is the lowest-friction ops interface there is: an emoji people already add becomes an action. Allowlist emoji-to-action mappings, verify you reacted to the right message, make handlers idempotent, and authorize who can trigger consequential actions. Let AI write the handlers fast while a human owns the guardrails. Explore more in the Slack category, or grab a tested starting point from the prompt packs.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.