Find the PagerDuty alerts nobody acts on, and the ones your own deploys trigger

Every Monday: last week's alerts checked against human reactions in PagerDuty, Slack and GitHub, and the rules to delete or retune.

Runs
Mondays at 10:00
Posts
Report card

From your apps to your board

How the skill works: your agent reads week's alerts from PagerDuty; monitoring from Grafana; monitoring from Datadog; join to slack and github on time and incident id from Slack; put deploys on the same clock from GitHub; where alerts come from, pagerduty services or teams, incident slack channels, repositories from What you tell it, mondays at 10:00, and gives you report card and the report in the chat.

Reads

  • PagerDutyWeek's alerts
  • GrafanaMonitoring
  • DatadogMonitoring
  • SlackJoin to Slack and GitHub on time and incident ID
  • GitHubPut deploys on the same clock
  • What you tell itWhere alerts come from, PagerDuty services or teams, Incident Slack channels, Repositories
reads
Your agentClaude Code, Codex, Cursor or GeminiMondays at 10:00
posts

You get

  • Report cardOn your board, updated in place every run
  • The report in the chatEvery run, with or without Briefkin

What you get

  • Unacted alerts: how many of the week's alerts got no human reaction.
  • Deploy-aligned alerts: how many fired within 10 minutes of a production deploy.
  • By rule: fired, unacted, after deploy, and the verdict (delete, retune, keep).
  • Rules with real reactions, so you know which pages earn their keep.
  • The one rule to change first, for example "Delete Disk 80% on worker-2: 5 pages, nobody looked".

The card on the right is a sample. Yours shows your own numbers and updates in place every run.

What lands on your board

acme-webAlerts nobody acts on: PagerDuty, Slack and deploys · Claude Code

14 of 22 alerts got no human reaction; 5 fired right after a deploy

14 of 22 alerts last week auto-resolved with no acknowledgement and no Slack thread. 5 fired within 10 minutes of a production deploy. Two rules account for 11 of the 14.

Alert ruleFiredNo reactionAfter deployVerdict
High latency p95 > 800 ms (api)764Retune
Disk 80% on worker-2550Delete
5xx rate > 2% (web)411Keep
Queue depth > 500 (jobs)320Retune
Checkout error rate > 1%300Keep
Alerts22
No human reaction14
Within 10 min of a deploy5

Delete "Disk 80% on worker-2": 5 pages, nobody acknowledged or replied, and the disk never passed 84%.

Done
An example with made-up numbers. Your agent posts your real ones.

What it does

PagerDuty counts how often you were paged. It cannot tell you that 14 of last week's 22 alerts resolved on their own while nobody said a word in Slack, or that 5 fired within ten minutes of your own deploys. Those are the alerts that train the on-call person to ignore the pager, and they are invisible unless someone lines up three tools by hand.

This skill is for the on-call lead or engineering manager who owns alert quality. Every Monday the agent takes every alert of the week, looks for a person reacting to it (an acknowledgement, a message in the incident channel within 30 minutes, a linked PR), puts it on the same clock as production deploys, and groups the result by alert rule. The card ends with the rules to delete and the ones to retune.

How it works

  1. 1Pull the week's alerts

    Whichever you page from. PagerDuty: GET /incidents?since=<7 days ago>&until=<now>&time_zone=UTC&limit=100 (page with offset), then GET /incidents/{id}/log_entries?is_overview=false for each. Grafana: GET /api/v1/rules/history?from=<ms>&to=<ms> for state changes into Alerting and back. Datadog: POST /api/v2/events/search with filter.query: "source:alert" and the week as filter.from and filter.to, keeping monitor_id and status.

  2. 2

    2Find a human reaction in the paging tool

    In PagerDuty log entries, an acknowledge_log_entry, annotate_log_entry or resolve_log_entry whose agent.type is user_reference is a person acting. An incident resolved by anything else (the integration, a service, a timeout) with no acknowledgement from a user is auto-resolved. Grafana and Datadog alerts have no acknowledgement, so for them the next two checks decide.

  3. 3Join to Slack and GitHub on time and incident ID

    For each alert, read the incident channel with conversations.history from the fire time to 30 minutes later (oldest, latest) plus conversations.replies on the alerting bot's post. A message from a person (no bot_id) in that thread, or one that mentions the incident number or alert title, counts as a reaction. Then gh search prs --repo <repo> "<incident number or URL>" for a PR within 7 days. An alert with none of the three is unacted.

  4. 4Put deploys on the same clock

    List production deploys of the week with GET /repos/{owner}/{repo}/deployments?environment=production and their success status times (or Vercel deployments that reached READY). An alert that fired 0 to 10 minutes after a deploy finished is deploy-aligned. Everything is compared in UTC.

  5. 5

    5Group by rule and give a verdict

    Group alerts by rule: PagerDuty service plus the incident title with numbers and host names stripped, Grafana ruleUID, Datadog monitor_id. Delete: fired 3 or more times with no human reaction and not deploy-aligned. Retune: most firings deploy-aligned (add a deploy silence or a longer pending period), or a reaction on under a third of them. Keep: the rest.

  6. 6

    6Post the card

    Headline: unacted alerts out of all alerts and the deploy-aligned count. Then the table by rule with fired, unacted, after deploy and the verdict, and one line naming the rule to change first.

  7. Post it to the board

    With BRIEFKIN_TOKEN set, the agent sends the result as one card that updates in place every run. Without it, you get the report in the chat.

Install and run it

Pick your agent, where the data comes from and how to schedule it; the commands below follow.

Agent
Data
Schedule
  1. Install the skill

    One command. It puts the SKILL.md in ~/.claude/skills and in ~/.agents/skills, which Codex, Cursor, Gemini CLI, Copilot CLI and OpenCode read too.

    shellcurl -fsSL https://briefkin.dev/skills/pagerduty-alerts-nobody-acts-on/install | sh
  2. Run it

    Ask Claude Code:

    promptRun the pagerduty-alerts-nobody-acts-on skill

    Or paste the prompt instead of installing:

    promptTake the last 7 days of alerts from PagerDuty (incidents and their log entries), or Grafana alert state history, or Datadog monitor alert events, whichever I use. For each alert, check for a human reaction: acknowledged or resolved by a user in PagerDuty, a message by a person in <#incidents> within 30 minutes of it firing or in the alert's Slack thread, or a PR in <owner/repo> mentioning the incident within 7 days. Also check whether it fired within 10 minutes after a production deploy (GitHub Deployments, UTC). Group alerts by rule and tell me for each: fired, no reaction, after deploy, and a verdict: delete (3+ firings, no reaction, not deploy-related), retune (mostly after deploys, or reacted to under a third of the time) or keep. Start with the rule I should change first.
  3. Put it on a schedule

    Mondays at 10:00, from cron on any machine where Claude Code is signed in:

    crontab0 10 * * 1 claude -p "Run the pagerduty-alerts-nobody-acts-on skill"

    Add it with crontab -e. A laptop asleep at that time runs it when it wakes if you use launchd or Task Scheduler instead; GitHub Actions works for the API route.

  4. Send it to your board

    Connect Briefkin once with curl -fsSL https://briefkin.dev/install | sh and every run lands on your board as a report card. How setup works

View the SKILL.mdRaw file
SKILL.md---
name: pagerduty-alerts-nobody-acts-on
description: "Every Monday: last week's alerts checked against human reactions in PagerDuty, Slack and GitHub, and the rules to delete or retune. Use when the user asks for this, or when it runs on a schedule."
---

# Alerts nobody acts on: PagerDuty, Slack and deploys

Every Monday: last week's alerts checked against human reactions in PagerDuty, Slack and GitHub, and the rules to delete or retune.

PagerDuty counts how often you were paged. It cannot tell you that 14 of last week's 22 alerts resolved on their own while nobody said a word in Slack, or that 5 fired within ten minutes of your own deploys. Those are the alerts that train the on-call person to ignore the pager, and they are invisible unless someone lines up three tools by hand.

This skill is for the on-call lead or engineering manager who owns alert quality. Every Monday the agent takes every alert of the week, looks for a person reacting to it (an acknowledgement, a message in the incident channel within 30 minutes, a linked PR), puts it on the same clock as production deploys, and groups the result by alert rule. The card ends with the rules to delete and the ones to retune.

## Two ways to get the data

Use the API or MCP route when you have an MCP server, a CLI or an API token for the app. Otherwise, if you can drive a browser the user is signed in to (Claude in Chrome, a Playwright or Chrome DevTools MCP server), use the browser route. Say which one you used.

## Route A: API or MCP server

### What you need

- Read access to the alerts: a read-only PagerDuty API key or the PagerDuty MCP server, a Grafana Viewer service account token, or Datadog API and application keys with `events_read` and `monitors_read`.
- A Slack bot invited to the incident channel with `channels:history` (`groups:history` if private), and GitHub read access to Deployments and Pull requests.
- Alert posts in Slack that carry the incident number or URL, and production deploys recorded as GitHub Deployments or Vercel deploys: these are the keys the join depends on.

If something is missing, say what and stop; do not invent numbers.

### Steps

1. **Pull the week's alerts** (PagerDuty): Whichever you page from. PagerDuty: `GET /incidents?since=<7 days ago>&until=<now>&time_zone=UTC&limit=100` (page with `offset`), then `GET /incidents/{id}/log_entries?is_overview=false` for each. Grafana: `GET /api/v1/rules/history?from=<ms>&to=<ms>` for state changes into Alerting and back. Datadog: `POST /api/v2/events/search` with `filter.query: "source:alert"` and the week as `filter.from` and `filter.to`, keeping `monitor_id` and `status`.
2. **Find a human reaction in the paging tool**: In PagerDuty log entries, an `acknowledge_log_entry`, `annotate_log_entry` or `resolve_log_entry` whose `agent.type` is `user_reference` is a person acting. An incident resolved by anything else (the integration, a service, a timeout) with no acknowledgement from a user is auto-resolved. Grafana and Datadog alerts have no acknowledgement, so for them the next two checks decide.
3. **Join to Slack and GitHub on time and incident ID** (Slack): For each alert, read the incident channel with `conversations.history` from the fire time to 30 minutes later (`oldest`, `latest`) plus `conversations.replies` on the alerting bot's post. A message from a person (no `bot_id`) in that thread, or one that mentions the incident number or alert title, counts as a reaction. Then `gh search prs --repo <repo> "<incident number or URL>"` for a PR within 7 days. An alert with none of the three is unacted.
4. **Put deploys on the same clock** (GitHub): List production deploys of the week with `GET /repos/{owner}/{repo}/deployments?environment=production` and their `success` status times (or Vercel deployments that reached `READY`). An alert that fired 0 to 10 minutes after a deploy finished is deploy-aligned. Everything is compared in UTC.
5. **Group by rule and give a verdict**: Group alerts by rule: PagerDuty service plus the incident title with numbers and host names stripped, Grafana `ruleUID`, Datadog `monitor_id`. **Delete**: fired 3 or more times with no human reaction and not deploy-aligned. **Retune**: most firings deploy-aligned (add a deploy silence or a longer pending period), or a reaction on under a third of them. **Keep**: the rest.
6. **Post the card**: Headline: unacted alerts out of all alerts and the deploy-aligned count. Then the table by rule with fired, unacted, after deploy and the verdict, and one line naming the rule to change first.

## Route B: the browser

### What you need

- Signed in to PagerDuty with a role that can see the services, to Slack with access to the incident channel, and to GitHub with read access to the repository.

If something is missing, say what and stop; do not invent numbers.

### Steps

Start at https://app.pagerduty.com/. The user is signed in already; never type passwords or codes, and ask if a sign-in page appears.

1. **List last week's incidents**: Sign-in lands on your account's PagerDuty subdomain. Open **Incidents**, choose the **All** tab and set the date range to the last 7 days. Note each incident's number, title, service and created time.
2. **Read who reacted**: Open each incident and read its timeline. An acknowledgement, note or resolve by a named person is a reaction; resolved by the integration or automatically, with no one acknowledging, is not.
3. **Check Slack for the rest**: In Slack search `in:#incidents after:<date a week ago>` and, for each unacknowledged incident, look for a message by a person within 30 minutes of it firing or a reply in the alert bot's thread.
4. **Line up deploys**: Open `https://github.com/<owner>/<repo>/deployments` and filter to the production environment. Mark every alert that fired within 10 minutes after a deploy; compare in one time zone.
5. **Group and give verdicts**: Group incidents by alert rule (same service and title). Delete: 3 or more firings, no reaction, not after deploys. Retune: mostly after deploys, or reacted to under a third of the time. Keep the rest.

Note: PagerDuty times show in your profile's time zone and Slack in yours, so convert before matching. Grafana and Datadog users read their alert history pages instead of Incidents. Opening every incident is slow past about 40 a week; the API route reads log entries in one pass.

## The report

- **Unacted alerts**: how many of the week's alerts got no human reaction.
- **Deploy-aligned alerts**: how many fired within 10 minutes of a production deploy.
- **By rule**: fired, unacted, after deploy, and the verdict (delete, retune, keep).
- **Rules with real reactions**, so you know which pages earn their keep.
- The one rule to change first, for example "Delete Disk 80% on worker-2: 5 pages, nobody looked".

## Post it to Briefkin

When `BRIEFKIN_TOKEN` is set, or `~/.config/briefkin/env` exists (the installer from https://briefkin.dev/docs writes it), also send the result to the user's Briefkin board as one card. Keep the `item_key` the same on every run so the card updates in place, use a new `event_id` per run, and put today's real numbers in place of the example ones. If the request fails, say so once and carry on.

```bash
[ -n "${BRIEFKIN_TOKEN:-}" ] || . ~/.config/briefkin/env
curl -sS -X POST "${BRIEFKIN_URL:-https://briefkin.dev}/v1/events" \
  -H "authorization: Bearer $BRIEFKIN_TOKEN" \
  -H "content-type: application/json" \
  -d @- <<'JSON'
{
  "v": 1,
  "event_id": "pagerduty-alerts-nobody-acts-on/<date of this run, e.g. 2026-10-16>",
  "item_key": "pagerduty-alerts-nobody-acts-on",
  "project": "acme-web",
  "source": "Alerts nobody acts on: PagerDuty, Slack and deploys skill",
  "kind": "update",
  "title": "14 of 22 alerts got no human reaction; 5 fired right after a deploy",
  "summary": "**14 of 22** alerts last week auto-resolved with no acknowledgement and no Slack thread. **5** fired within 10 minutes of a production deploy. Two rules account for 11 of the 14.",
  "widget": [
    {
      "type": "Table",
      "props": {
        "columns": [
          "Alert rule",
          "Fired",
          "No reaction",
          "After deploy",
          "Verdict"
        ],
        "align": [
          "left",
          "right",
          "right",
          "right",
          "left"
        ],
        "rows": [
          [
            "High latency p95 > 800 ms (api)",
            7,
            6,
            4,
            "Retune"
          ],
          [
            "Disk 80% on worker-2",
            5,
            5,
            0,
            "Delete"
          ],
          [
            "5xx rate > 2% (web)",
            4,
            1,
            1,
            "Keep"
          ],
          [
            "Queue depth > 500 (jobs)",
            3,
            2,
            0,
            "Retune"
          ],
          [
            "Checkout error rate > 1%",
            3,
            0,
            0,
            "Keep"
          ]
        ]
      }
    },
    {
      "type": "Grid",
      "props": {
        "columns": 3
      },
      "children": [
        {
          "type": "Stat",
          "props": {
            "label": "Alerts",
            "value": 22
          }
        },
        {
          "type": "Stat",
          "props": {
            "label": "No human reaction",
            "value": 14,
            "better": "lower",
            "tone": "bad"
          }
        },
        {
          "type": "Stat",
          "props": {
            "label": "Within 10 min of a deploy",
            "value": 5,
            "better": "lower",
            "tone": "warn"
          }
        }
      ]
    },
    {
      "type": "Callout",
      "props": {
        "text": "Delete \"Disk 80% on worker-2\": 5 pages, nobody acknowledged or replied, and the disk never passed 84%.",
        "tone": "warn"
      }
    }
  ]
}
JSON
```

Widgets come from the Briefkin catalog (https://briefkin.dev/v1/catalog). Without a token, just give the user the report.

## Run it on a schedule

Suggested: Mondays at 10:00 (`0 10 * * 1`). From cron, with the agent signed in on that machine:

```bash
0 10 * * 1 claude -p "Run the pagerduty-alerts-nobody-acts-on skill"
# Codex: codex exec, Gemini CLI: gemini -p, Copilot CLI: copilot -p, OpenCode: opencode run
```

Or use the agent's own scheduler (Claude Code routines, Codex automations, Claude or ChatGPT scheduled tasks): https://briefkin.dev/skills/pagerduty-alerts-nobody-acts-on#schedule

Source: https://briefkin.dev/skills/pagerduty-alerts-nobody-acts-on

What your agent needs

  • Read access to the alerts: a read-only PagerDuty API key or the PagerDuty MCP server, a Grafana Viewer service account token, or Datadog API and application keys with events_read and monitors_read.

  • A Slack bot invited to the incident channel with channels:history (groups:history if private), and GitHub read access to Deployments and Pull requests.

  • Alert posts in Slack that carry the incident number or URL, and production deploys recorded as GitHub Deployments or Vercel deploys: these are the keys the join depends on.

Connecting PagerDuty

  1. PagerDuty MCP server, PagerDuty's official hosted server at https://mcp.pagerduty.com/mcp (https://mcp.eu.pagerduty.com/mcp for EU accounts), also in Claude's connector directory. It takes a user API key in the header Authorization: Token token=<key>, or OAuth with static client credentials (it doesn't support dynamic client registration). In Claude Code: PAGERDUTY_API_KEY=<key> claude mcp add-json pagerduty '{"type":"http","url":"https://mcp.pagerduty.com/mcp","headers":{"Authorization":"Token token=${PAGERDUTY_API_KEY}"}}'; in Cursor, add the URL under mcpServers in .cursor/mcp.json (guide). Its browse_* tools read incidents, schedules and on-calls, services, teams and logs; its manage_* tools change them. The older self-hosted server is deprecated.
  2. REST API with a read-only key. An admin creates a general access key under Integrations > Developer Tools > API Access Keys and ticks Read-only API Key. Store it in PAGERDUTY_API_KEY and send Authorization: Token token=<key> with Accept: application/vnd.pagerduty+json;version=2.

The hosted server can't hide its write tools, so the credential decides: a read-only key allows only GET calls, and every manage_* tool fails with it.

Connecting Grafana

  1. Grafana MCP server (mcp-grafana), Grafana Labs' official open-source server. It runs locally with uvx mcp-grafana, the grafana/mcp-grafana Docker image or a release binary, and reads GRAFANA_URL (your instance, or https://myinstance.grafana.net on Grafana Cloud) and GRAFANA_SERVICE_ACCOUNT_TOKEN. In Claude Code: claude mcp add grafana -e GRAFANA_URL=... -e GRAFANA_SERVICE_ACCOUNT_TOKEN=... -- uvx mcp-grafana --disable-write; Cursor and Codex run the same command. It covers dashboards, Prometheus and Loki queries, alert rules and their states, OnCall and incidents (source).
  2. HTTP API with a service account token. Create a service account under Administration > Users and access > Service accounts with the Viewer role, add a token, store it in GRAFANA_SERVICE_ACCOUNT_TOKEN with GRAFANA_URL, and send it as a Bearer token.

--disable-write removes every tool that creates or changes dashboards, alert rules, silences or contact points; with a Viewer service account behind it, the agent can read alert states and run queries and nothing else.

Connecting Datadog

  1. Datadog MCP Server, Datadog's official remote server at https://mcp.datadoghq.com/v1/mcp for US1. Other sites have their own host, such as https://mcp.datadoghq.eu/v1/mcp or https://mcp.us5.datadoghq.com/v1/mcp, and US government sites aren't supported. In Claude Code, install the Datadog plugin with /plugin install datadog@claude-plugins-official and run /ddsetup, or run claude mcp add --transport http datadog-mcp https://mcp.datadoghq.com/v1/mcp. In Codex, add the URL under [mcp_servers.datadog] in ~/.codex/config.toml and run codex mcp login datadog; in Cursor, install the Datadog plugin. Only the core toolset is on by default; add ?toolsets= to the URL for more (setup).
  2. API with an API key and an application key. Store them in DD_API_KEY and DD_APP_KEY with DD_SITE (datadoghq.com, datadoghq.eu and so on) and send them as the DD-API-KEY and DD-APPLICATION-KEY headers. Scope the application key to what the report reads, such as monitors_read, incident_read and slos_read. The MCP server also accepts a key pair from a scoped service account for clients without OAuth.

People need the mcp_read permission to use the server; leave out mcp_write, or keep MCP Write Access off for the organization, and the agent can't create or edit monitors or notebooks.

Connecting Slack

  1. Slack MCP server, Slack's official remote server at https://mcp.slack.com/mcp. It only works through Slack apps that Slack has registered, so use the Slack connector in Claude or another approved client, and your workspace admin may need to approve it (docs). It searches messages and files, reads channels and threads, and can send messages.
  2. Web API with your own app. Create an internal app at api.slack.com/apps, install it to the workspace, and store its token in SLACK_BOT_TOKEN (xoxb-) or SLACK_USER_TOKEN (xoxp-). Read scopes: channels:history, channels:read, groups:history for private channels, users:read, and search:read (user tokens only) for search. A bot reads only the channels it was invited to.

Leave out chat:write unless the skill should post back into Slack; with history and read scopes alone, the app can't post anything.

Connecting GitHub

  1. GitHub MCP Server, GitHub's official server, hosted at https://api.githubcopilot.com/mcp/. Sign in with OAuth or a personal access token; add /readonly to the URL, or use a toolset URL such as https://api.githubcopilot.com/mcp/x/pull_requests/readonly, to expose only read tools. It also runs locally (source).
  2. The gh CLI. Run gh auth login once, or set GH_TOKEN for scheduled runs. gh pr list --search "review-requested:@me" --json number,title,url,createdAt and gh api repos/{owner}/{repo}/dependabot/alerts cover most reports.
  3. REST and GraphQL with a fine-grained token. Store it in GITHUB_TOKEN, limit it to the repositories in the report, and grant Read on Metadata, Contents, Pull requests and Issues, plus Dependabot alerts if you report on them.

With every permission on a fine-grained token set to Read, the agent can't push, merge, comment or close anything.

Questions

What if the team discusses alerts somewhere other than the incident channel?

Then the Slack check undercounts reactions and more alerts look ignored than are. List every channel where alerts get discussed, and the skill reads each one for the same 30 minutes. DMs and calls stay invisible, so a PagerDuty acknowledgement by a user always counts as a reaction on its own.

Why 10 minutes after a deploy, and which clock?

Most deploy-caused alerts (a cold start latency bump, a brief 5xx during rollout) fire within a few minutes of the new version going live; 10 minutes catches them without pulling in unrelated alerts. All timestamps are converted to UTC first: PagerDuty with time_zone=UTC, Slack ts is Unix time, and GitHub returns UTC.

Do I need Briefkin to use the Alerts nobody acts on: PagerDuty, Slack and deploys skill?

No. The skill runs with your own agent and gives you the report in the chat. With a Briefkin token set, each run also posts it to your board as a report card, so you can glance at it instead of asking again.

Which agents can run it?

Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot CLI, OpenCode, Windsurf and Amp all read SKILL.md files. The install command puts it in ~/.claude/skills and ~/.agents/skills, where they look. ChatGPT, the Claude app and Grok take the prompt instead. Pick your agent under "Install and run it" and the commands change to match.

Can it work without an API or MCP server, from my browser?

Yes. Switch the data source to "Your browser" and the steps change to the PagerDuty, Grafana, Datadog, Slack and GitHub pages themselves: the agent opens app.pagerduty.com in a browser you are signed in to and reads the numbers off the page. Claude Code, Codex, Cursor, Claude app and ChatGPT can drive a browser out of the box; Gemini CLI, Copilot CLI and OpenCode need a Playwright or Chrome DevTools MCP server first. It is slower than the API and needs you signed in, so it suits a report you run by hand or from a scheduler on your own computer.

Read this report on your board, not in a chat log

Skills are free to use with any agent. Briefkin is where their results land: one board for every report, number and question your agents send, with a ping when something needs you.

Free during the open beta. No card.