WorkflowsMCP

Incident response war room

PagerDuty fires, a dedicated Slack channel spins up, the postmortem writes its own first draft.

@workflowsmcpApr 8, 2026Verified Aug 2026incident-responseon-callpostmortemreliabilityautomation

During an incident nobody has spare attention for logistics. PagerDuty's own Slack integration already turns a trigger into a numbered channel with the responders invited, so this workflow configures that rather than rebuilding it, and spends its custom code where nothing ships out of the box: capturing the timeline from what people actually type, and creating a Notion postmortem on resolve pre-filled with detection time, duration, the timeline and the responder list — so the retro starts from evidence instead of memory.

How it flows

  1. 01

    Monitor triggers a PagerDuty incident

    Events API v2 with a dedup_key, so a flapping monitor does not open five incidents.

  2. 02

    Severity gate decides on a war room

    Priority and service conditions on the channel rule: SEV1 and SEV2 only; SEV3 stays a thread in #ops.

  3. 03

    Channel created and responders invited

    PagerDuty's Slack integration opens inc-{{incident_number}} and pulls the responders in. None of your code runs here.

  4. 04

    Header pinned with roles and status

    The integration bookmarks the incident; your app pins one message naming IC, comms and scribe.

  5. 05

    Timeline captured from !log messages

    Responders curate as they go; nobody reconstructs it afterwards.

  6. 06

    Resolve creates the postmortem page

    Times, duration, services, responders and the timeline pre-filled in Notion.

  7. 07

    Root cause left blank on purpose

    The review owner fills it in. A machine-suggested cause anchors the discussion before anyone has looked.

Set up each app

Work through these in order — later apps usually need a token or an id from an earlier one.

PagerDuty

The trigger and the source of severity

  1. 01

    Add an Events API v2 integration

    Services → Service Directory → your service → Integrations → Add another integration → Events API V2. Copy what the UI labels the Integration Key; that is the routing_key your monitors post to. One integration per service keeps it rotatable without a global outage of alerting.

    POST https://events.pagerduty.com/v2/enqueue
    {
      "routing_key": "R02A9F4C7E1B84D3F",
      "event_action": "trigger",
      "dedup_key": "checkout-api-5xx",
      "payload": {
        "summary": "checkout-api 5xx rate 14% over 5m (threshold 2%)",
        "severity": "critical",
        "source": "checkout-api.prod.us-east-1",
        "component": "payments",
        "group": "checkout",
        "class": "http-error-rate"
      },
      "links": [{ "href": "https://grafana.acme.com/d/checkout", "text": "Checkout dashboard" }]
    }
  2. 02

    Subscribe to incident webhooks

    Integrations → Generic Webhooks (v3) → New webhook → scope it to the service → events: incident.triggered, incident.acknowledged, incident.resolved. Verify the X-PagerDuty-Signature header on every delivery.

  3. 03

    Only spin up a room for SEV1 and SEV2

    Map PagerDuty severity to your SEV scale, then express the gate as conditions on the incident-channel rule below (by priority, service or team) rather than as an if-statement in your code. A war room per warning alert leaves you with 200 dead channels and a workspace nobody can search.

    Severity gate
    critical -> SEV1   channel rule fires, page secondary, notify #general
    error    -> SEV2   channel rule fires, page secondary
    warning  -> SEV3   thread in #ops only, no channel
    info     ->        ignore

Slack

The war room itself and the timeline capture

  1. 01

    Let PagerDuty open the war room

    Do not write this part. PagerDuty → Integrations → Extensions → Slack → your workspace → Incident Channel tab → turn on "Automatically create incident channels". Set the name template (inc-{{incident.incident_number}} — Slack names are lowercase, no spaces, 80 characters max, so keep the template fixed and search still works six months later), scope it with the priority/service/team conditions from the severity gate, and let it handle the invites, the incident bookmark and the status updates. Then still post one message naming IC, comms owner and scribe and pins.add it: every incident review that goes badly starts with "who was actually running this?".

  2. 02

    Add your own app for the timeline

    The native integration owns the room; your app only needs to read and write in it. Add the scopes below, subscribe to the message.channels event, and get the app into each incident channel — a message subscription only delivers channels the app is a member of. If you cannot use the PagerDuty Slack integration, you can still create the room yourself with conversations.create, conversations.invite and conversations.setPurpose (add channels:manage), taking the channel id from the create response and never hardcoding it.

    Bot Token Scopes
    channels:history   # read the !log messages (with the message.channels event)
    channels:read      # resolve the incident channel id
    chat:write         # post the roles header and the postmortem link
    pins:write         # pin that header
    users:read         # turn member ids into the responder list
  3. 03

    Capture the timeline with a keyword

    Ask responders to prefix decisive messages with "!log". Collect those from the message events; you get a human-curated timeline for free instead of parsing 400 messages afterwards.

    What lands in the postmortem timeline
    21:47Z  !log alert fired, 5xx at 14%
    21:49Z  !log rolled back deploy 4f21ac
    21:52Z  !log error rate back under 1%, watching
    22:05Z  !log status page updated, incident resolved

Notion

Postmortem, pre-filled from the facts

  1. 01

    Create the "Incidents" database

    Properties: Name (Title), Incident ID (Text), Severity (Select: SEV1/SEV2/SEV3), Detected At (Date with time), Resolved At (Date with time), Duration mins (Formula), Services (Multi-select), Responders (Person), Root Cause (Text), Action Items (Text), Status (Select: Draft / In review / Complete).

  2. 02

    Let Notion compute the duration

    Add the formula below to Duration mins. Deriving it beats storing it — a corrected Resolved At then fixes the number everywhere at once.

    Notion formula — Duration mins
    if(
      empty(prop("Resolved At")),
      toNumber(""),
      dateBetween(prop("Resolved At"), prop("Detected At"), "minutes")
    )
  3. 03

    Create the page on resolve, not on trigger

    On incident.resolved, POST /v1/pages with Notion-Version: 2026-03-11 — retrieve the Incidents database first and parent the page on its data_source_id, not the database id. Body is the timeline, Detected/Resolved come from the PagerDuty timestamps. Responders is a Person property and takes Notion user ids: Slack member ids will not resolve into it, so keep a slack-to-notion map (the same shape as the reviewer map in the PR pipeline) or fall back to a Text property rather than shipping a column that is always empty. Leave Root Cause empty on purpose — that section is the human's job, and a pre-filled guess anchors the whole review.

  4. 04

    Archive the channel with a link

    Post the postmortem URL into the war room, then archive the channel after 48 hours. The channel history stays searchable; the sidebar stays usable.