Automating Incident Channels in Slack: From Page to Postmortem
Spin up a dedicated Slack incident channel automatically, seed it with context, manage roles, and capture the timeline for a clean postmortem.
- #slack
- #incident-response
- #chatops
- #automation
- #sre
- #postmortem
The worst time to organize an incident is during the incident. Yet I’ve sat in too many outages where the first ten minutes were spent arguing about where to talk, who’s running it, and whether someone pinged the database team. After a couple of decades of running ChatOps, I’ve landed on one firm belief: every real incident should get its own automatically-created Slack channel, seeded with context and roles, the moment it’s declared.
Here’s how I build that.
Why a dedicated channel per incident
Threading an incident inside a busy ops channel is chaos. A dedicated channel gives you a clean, contained, time-ordered record of exactly what happened. It becomes the postmortem timeline for free. And it gives you a natural place to set membership — the right people, none of the wrong noise.
The cost is one channel per incident, which feels like clutter until your first messy multi-team outage, at which point you never go back.
The declare command
Everything starts with one slash command: /incident declare <severity> <short title>. When fired, the bot does the following in sequence:
- Creates a channel,
#inc-2026-06-11-checkout-latency, using a predictable naming convention so it sorts and searches cleanly. - Sets the channel topic to the severity and current status.
- Invites the on-call engineer, the incident commander rotation, and any owning-team members for the affected service.
- Posts a pinned “incident card” with the roles, the status, and the links.
app.command('/incident', async ({ command, ack, client }) => {
await ack();
const [verb, sev, ...rest] = command.text.split(' ');
if (verb !== 'declare') return;
const title = rest.join(' ');
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 30);
const date = new Date().toISOString().slice(0, 10);
const { channel } = await client.conversations.create({
name: `inc-${date}-${slug}`,
});
await client.conversations.setTopic({
channel: channel.id,
topic: `${sev.toUpperCase()} | INVESTIGATING | ${title}`,
});
// invite responders, post the pinned incident card...
});
Seed the channel with context, not a blank room
An empty incident channel makes everyone hunt for the same links. The bot should immediately post the things every responder needs:
- The relevant dashboards for the affected service.
- The runbook, if the triggering alert carried one.
- Recent deploys and config changes for that service.
- A status-page draft button.
I think of it as setting the table. Responders arrive and the context is already laid out, so they spend their attention on the problem instead of on logistics.
Manage roles in-channel
Confusion about who’s running this costs real minutes. I use lightweight commands:
/incident role commander @alice— sets the IC, updates the pinned card./incident role comms @bob— assigns whoever owns customer updates./incident status mitigating— updates the topic so anyone glancing at the channel sees the current state.
The pinned card always reflects the truth, so a VP who drops in at minute 40 gets the whole picture from one message without asking “what’s the status?” and breaking the responders’ focus.
Capture the timeline automatically
The channel is the timeline, but I make it explicit. A /incident note <text> command tags a message as a timeline event with a timestamp. Status changes, role assignments, and notes all get stamped. When the incident resolves, the bot can export those tagged events in order — that’s your postmortem skeleton, written while everyone’s memory is fresh instead of three days later from blurry recollection.
Resolve and hand off to the postmortem
/incident resolve flips the topic to RESOLVED, posts a wrap-up, and kicks off the postmortem flow. This is the highest-value moment for automation because it happens when everyone wants to walk away and the details are about to evaporate.
Where AI fits
I layer AI on top of the deterministic plumbing, never under it:
- Live summary on demand. A latecomer types
/incident catchme-upand the bot sends the channel scrollback through an LLM that returns a five-bullet “here’s where we are.” This single feature has saved more interruptions than anything else I’ve built. - Postmortem draft. On resolve, the tagged timeline plus the scrollback go to the model, which returns a blameless draft: summary, timeline, impact, root cause, what went well, action items. A human edits a draft instead of staring at a blank doc.
- Comms drafting. The bot offers a status-page update in customer-safe language for the comms lead to approve.
The constraint never moves: AI reads, summarizes, and drafts. It does not change channel state, declare or resolve incidents, or run remediation. Those are deterministic commands a human triggers.
A rollout that won’t bite you
Ship this in layers:
- Auto-create and seed the channel. Pure upside, no risk.
- Role and status commands. Still no risk, big clarity win.
- Timeline capture and postmortem export. Deterministic.
- AI catch-up and postmortem draft last, once the rest is reliable.
Each layer is useful on its own, so you get value before you’ve built the whole thing.
The payoff
The first time a sev-1 spins up its own channel, invites the right people, and lays out the dashboards before anyone’s typed a word, you’ll wonder how you ran incidents without it. The timeline writes itself, the postmortem starts half-done, and nobody loses ten minutes to logistics.
For the AI prompts I use for catch-up summaries and postmortem drafts, see our Slack incident prompts and the prompt library.
Channel creation, roles, and status are deterministic commands. Keep AI in the summarize-and-draft lane so the system of record stays trustworthy.