diff --git a/.craft.yml b/.craft.yml
index 868d5085c8..93fd5ce543 100644
--- a/.craft.yml
+++ b/.craft.yml
@@ -55,6 +55,9 @@ targets:
- name: npm
id: "@sentry/junior-octolens"
includeNames: /^sentry-junior-octolens-\d.*\.tgz$/
+ - name: npm
+ id: "@sentry/junior-pagerduty"
+ includeNames: /^sentry-junior-pagerduty-\d.*\.tgz$/
- name: npm
id: "@sentry/junior-sentry"
includeNames: /^sentry-junior-sentry-\d.*\.tgz$/
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f6a39d14a2..0f26bdea81 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -83,6 +83,7 @@ jobs:
- packages/junior-memory/**
- packages/junior-notion/**
- packages/junior-octolens/**
+ - packages/junior-pagerduty/**
- packages/junior-sentry/**
- packages/junior-vercel/**
repo:
@@ -501,6 +502,7 @@ jobs:
pnpm --filter @sentry/junior-memory pack --pack-destination artifacts
pnpm --filter @sentry/junior-notion pack --pack-destination artifacts
pnpm --filter @sentry/junior-octolens pack --pack-destination artifacts
+ pnpm --filter @sentry/junior-pagerduty pack --pack-destination artifacts
pnpm --filter @sentry/junior-maintenance pack --pack-destination artifacts
pnpm --filter @sentry/junior-sentry pack --pack-destination artifacts
pnpm --filter @sentry/junior-vercel pack --pack-destination artifacts
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b08b933e04..4bccef961b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -139,6 +139,7 @@ This repo uses Craft for manual lockstep npm releases of:
- `@sentry/junior-memory`
- `@sentry/junior-notion`
- `@sentry/junior-octolens`
+- `@sentry/junior-pagerduty`
- `@sentry/junior-sentry`
- `@sentry/junior-vercel`
diff --git a/README.md b/README.md
index af11a45ee3..76b6d49ddb 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,7 @@ Start here:
| `@sentry/junior-memory` | Memory plugin package for long-term Junior memory storage |
| `@sentry/junior-notion` | Notion plugin package for page search workflows |
| `@sentry/junior-octolens` | Octolens plugin package for social-listening workflows |
+| `@sentry/junior-pagerduty` | Read-only PagerDuty on-call, schedule, and incident lookups |
| `@sentry/junior-maintenance` | Maintenance plugin package for updating and improving Junior apps |
| `@sentry/junior-sentry` | Sentry investigations and issue events |
| `@sentry/junior-vercel` | Vercel plugin package for deployment monitoring and log investigations |
diff --git a/package.json b/package.json
index 5a3da217ee..d798d1c086 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
"docs:dev": "pnpm --filter @sentry/junior-docs dev",
"docs:build": "pnpm --filter @sentry/junior-docs build",
"docs:check": "pnpm --filter @sentry/junior-docs check",
- "package:lint": "for pkg in packages/junior packages/junior-plugin-api packages/junior-memory packages/junior-dashboard packages/junior-github packages/junior-google packages/junior-agent-browser packages/junior-amplitude packages/junior-cloudflare packages/junior-datadog packages/junior-tts packages/junior-hex packages/junior-linear packages/junior-maintenance packages/junior-notion packages/junior-octolens packages/junior-sentry packages/junior-vercel; do pnpm exec publint \"$pkg\" || exit $?; done",
+ "package:lint": "for pkg in packages/junior packages/junior-plugin-api packages/junior-memory packages/junior-dashboard packages/junior-github packages/junior-google packages/junior-agent-browser packages/junior-amplitude packages/junior-cloudflare packages/junior-datadog packages/junior-tts packages/junior-hex packages/junior-linear packages/junior-maintenance packages/junior-notion packages/junior-octolens packages/junior-pagerduty packages/junior-sentry packages/junior-vercel; do pnpm exec publint \"$pkg\" || exit $?; done",
"package-types:check": "sh scripts/check-package-types.sh",
"release:check": "node scripts/check-release-config.mjs",
"start": "pnpm --filter @sentry/junior-example dev",
diff --git a/packages/docs/astro.config.mjs b/packages/docs/astro.config.mjs
index 832b786f2c..4d8a759f2d 100644
--- a/packages/docs/astro.config.mjs
+++ b/packages/docs/astro.config.mjs
@@ -19,6 +19,7 @@ export default defineConfig({
"/extend/cloudflare": "/extend/cloudflare-plugin",
"/extend/datadog": "/extend/datadog-plugin",
"/extend/hex": "/extend/hex-plugin",
+ "/extend/pagerduty": "/extend/pagerduty-plugin",
"/plugins/overview": "/extend",
"/plugins/agent-browser": "/extend/agent-browser-plugin",
"/plugins/amplitude": "/extend/amplitude-plugin",
@@ -136,6 +137,10 @@ export default defineConfig({
},
{ label: "Memory Plugin", link: "/extend/memory-plugin/" },
{ label: "Notion Plugin", link: "/extend/notion-plugin/" },
+ {
+ label: "PagerDuty Plugin",
+ link: "/extend/pagerduty-plugin/",
+ },
{ label: "Sentry Plugin", link: "/extend/sentry-plugin/" },
{ label: "Vercel Plugin", link: "/extend/vercel-plugin/" },
],
diff --git a/packages/docs/src/content/docs/contribute/releasing.md b/packages/docs/src/content/docs/contribute/releasing.md
index 5eb4961f50..768071dd98 100644
--- a/packages/docs/src/content/docs/contribute/releasing.md
+++ b/packages/docs/src/content/docs/contribute/releasing.md
@@ -27,6 +27,7 @@ Junior uses lockstep package releases for:
- `@sentry/junior-memory`
- `@sentry/junior-notion`
- `@sentry/junior-octolens`
+- `@sentry/junior-pagerduty`
- `@sentry/junior-sentry`
- `@sentry/junior-vercel`
diff --git a/packages/docs/src/content/docs/extend/index.md b/packages/docs/src/content/docs/extend/index.md
index 1776937be9..a2301961c5 100644
--- a/packages/docs/src/content/docs/extend/index.md
+++ b/packages/docs/src/content/docs/extend/index.md
@@ -32,6 +32,7 @@ fixed behavior that code must control.
| [Maintenance](/extend/maintenance-plugin/) | Repository maintenance workflows |
| [Memory](/extend/memory-plugin/) | Long-term scoped memory |
| [Notion](/extend/notion-plugin/) | Notion content |
+| [PagerDuty](/extend/pagerduty-plugin/) | On-call, schedules, and incidents (read-only) |
| [Scheduler](/extend/scheduler-plugin/) | Durable scheduled automations |
| [Sentry](/extend/sentry-plugin/) | Sentry issues, telemetry, and issue webhooks |
| [Vercel](/extend/vercel-plugin/) | Vercel projects and deployments |
diff --git a/packages/docs/src/content/docs/extend/pagerduty-plugin.md b/packages/docs/src/content/docs/extend/pagerduty-plugin.md
new file mode 100644
index 0000000000..5f3763133b
--- /dev/null
+++ b/packages/docs/src/content/docs/extend/pagerduty-plugin.md
@@ -0,0 +1,85 @@
+---
+title: PagerDuty Plugin
+description: Configure read-only PagerDuty lookups for on-call, handoffs, schedules, escalation policies, services, and incidents.
+type: tutorial
+prerequisites:
+ - /extend/
+related:
+ - /concepts/credentials-and-oauth/
+ - /operate/security-hardening/
+---
+
+The PagerDuty plugin lets Slack users ask Junior who is on call, who rotates in or out next, and what is open in PagerDuty. Junior calls the PagerDuty REST API from the sandbox.
+
+The plugin is read-only. It uses one deployment-level API key, and the bundled skill uses only `GET` requests.
+
+## Install
+
+Install the plugin package alongside `@sentry/junior`:
+
+```bash
+pnpm add @sentry/junior @sentry/junior-pagerduty
+```
+
+## Runtime setup
+
+Add the package name to the plugin set exported from `plugins.ts`:
+
+```ts title="plugins.ts"
+import { defineJuniorPlugins } from "@sentry/junior";
+
+export const plugins = defineJuniorPlugins(["@sentry/junior-pagerduty"]);
+```
+
+## Config
+
+Set deployment variables in the Junior environment, then redeploy.
+
+### Environment variables
+
+
+PAGERDUTY_API_KEY
+
+PagerDuty REST API key for read-only requests.
+
+- **Define:** Set `PAGERDUTY_API_KEY` in the deployment environment
+- **Required:** Yes
+- **Environment override:** `PAGERDUTY_API_KEY`
+
+Use an account-level General Access REST API key with **Read-only API Key** enabled. An admin or account owner creates it in **Integrations > Developer Tools > API Access Keys**.
+
+
+
+## Auth model
+
+- The plugin uses one deployment-level PagerDuty API key, not per-user OAuth.
+- Junior keeps the real `PAGERDUTY_API_KEY` value on the host.
+- Sandbox requests to `api.pagerduty.com` receive a host-managed `Authorization: Token token=` header.
+- The sandbox never receives the key in env, files, or command arguments.
+- Users do not connect or disconnect individual PagerDuty accounts from Junior App Home for this plugin.
+
+## What users can do
+
+- Ask who is on call now for a schedule, escalation policy, or service.
+- Ask who rotates in or out next and when the handoff happens.
+- Inspect schedule members, escalation paths, and team members.
+- List open incidents and read incident details, timelines, and notes.
+
+## Verify
+
+1. Set `PAGERDUTY_API_KEY` and redeploy.
+2. Ask Junior in a channel: `Who is on call for right now, and who is next?`
+3. Confirm the reply names the current and next responders, the handoff time, and a PagerDuty link.
+
+## Failure modes
+
+- `PAGERDUTY_API_KEY` missing: Junior cannot add the credential. Set the env var and redeploy.
+- `401 Unauthorized`: the key is invalid or revoked.
+- `403 Forbidden`: the key cannot read the requested resource.
+- `429 Too Many Requests`: PagerDuty is throttling. Retry later or narrow the request.
+- EU-region account: the manifest covers only `api.pagerduty.com`. EU accounts need a manifest change for `api.eu.pagerduty.com`.
+- Mutation requests (acknowledge, resolve, override): the plugin is read-only, and the skill declines these.
+
+## Next step
+
+Review [Credentials & OAuth](/concepts/credentials-and-oauth/) and [Security Hardening](/operate/security-hardening/).
diff --git a/packages/junior-pagerduty/README.md b/packages/junior-pagerduty/README.md
new file mode 100644
index 0000000000..20d83c36d2
--- /dev/null
+++ b/packages/junior-pagerduty/README.md
@@ -0,0 +1,37 @@
+# @sentry/junior-pagerduty
+
+`@sentry/junior-pagerduty` adds read-only PagerDuty lookups to Junior through the PagerDuty REST API. Use it to answer on-call, handoff, schedule, escalation policy, service, team, and incident questions.
+
+## Install
+
+```bash
+pnpm add @sentry/junior @sentry/junior-pagerduty
+```
+
+## Configure
+
+Add the package name to the plugin set exported from `plugins.ts`:
+
+```ts
+import { defineJuniorPlugins } from "@sentry/junior";
+
+export const plugins = defineJuniorPlugins(["@sentry/junior-pagerduty"]);
+```
+
+Set a PagerDuty REST API key in the Junior deployment environment:
+
+```bash
+PAGERDUTY_API_KEY=...
+```
+
+Use an account-level General Access REST API key with **Read-only API Key** enabled. An admin or account owner creates it in **Integrations > Developer Tools > API Access Keys**.
+
+## Auth model
+
+- The plugin uses one deployment-level PagerDuty API key. Users do not connect individual PagerDuty accounts.
+- The real key stays on the host. Junior adds `Authorization: Token token=` to sandbox requests to `api.pagerduty.com`.
+- The key is the read-only boundary. The bundled skill also uses only `GET` requests.
+
+The manifest covers the US service region only. EU-region accounts (`api.eu.pagerduty.com`) need a manifest change.
+
+Full setup guide: https://junior.sentry.dev/extend/pagerduty-plugin/
diff --git a/packages/junior-pagerduty/package.json b/packages/junior-pagerduty/package.json
new file mode 100644
index 0000000000..746c589788
--- /dev/null
+++ b/packages/junior-pagerduty/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "@sentry/junior-pagerduty",
+ "version": "0.257.0",
+ "private": false,
+ "publishConfig": {
+ "access": "public"
+ },
+ "type": "module",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/getsentry/junior.git",
+ "directory": "packages/junior-pagerduty"
+ },
+ "files": [
+ "plugin.yaml",
+ "skills"
+ ]
+}
diff --git a/packages/junior-pagerduty/plugin.yaml b/packages/junior-pagerduty/plugin.yaml
new file mode 100644
index 0000000000..dabe6fff12
--- /dev/null
+++ b/packages/junior-pagerduty/plugin.yaml
@@ -0,0 +1,14 @@
+name: pagerduty
+display-name: PagerDuty
+description: Read-only PagerDuty on-call, schedule, escalation, service, and incident lookups through the PagerDuty REST API
+
+# Junior injects a deployment-level PagerDuty REST API key on sandbox requests
+# to the US API host. Use a read-only key. The sandbox never sees the real key.
+env-vars:
+ PAGERDUTY_API_KEY:
+
+domains:
+ - api.pagerduty.com
+
+api-headers:
+ Authorization: Token token=${PAGERDUTY_API_KEY}
diff --git a/packages/junior-pagerduty/skills/pagerduty/SKILL.md b/packages/junior-pagerduty/skills/pagerduty/SKILL.md
new file mode 100644
index 0000000000..9416fc92df
--- /dev/null
+++ b/packages/junior-pagerduty/skills/pagerduty/SKILL.md
@@ -0,0 +1,62 @@
+---
+name: pagerduty
+description: Look up PagerDuty on-call state through the read-only PagerDuty REST API. Use when users ask who is on call, who is rotating in or out, when the next on-call handoff is, what a schedule or escalation policy looks like, which team or service owns paging, or for PagerDuty incident status and timelines. Do not use it to acknowledge, resolve, reassign, or create incidents, to create schedule overrides, or for Sentry issues, Datadog telemetry, or Linear tickets.
+---
+
+# PagerDuty
+
+Use this skill for read-only PagerDuty lookups: on-call, handoffs, schedules, escalation policies, services, teams, users, and incidents.
+
+## Call the API
+
+- Call `https://api.pagerduty.com` with `curl` from the sandbox.
+- Do not send an `Authorization` header. Junior adds the PagerDuty credential to requests for this host.
+- Send `-H 'Accept: application/vnd.pagerduty+json;version=2'`.
+- Use only `GET`. The deployment key is read-only.
+- Pipe responses through `jq` and keep only the fields you need.
+- Lists page with `limit` (max `100`) and `offset`. Follow `more: true` only when the answer needs more rows.
+- Array filters use bracket syntax, for example `schedule_ids[]=PABC123`. Pass them with `curl -G --data-urlencode`.
+- Times are ISO 8601. Pass `time_zone=` when the user names a zone.
+
+## Workflow
+
+1. Resolve the target.
+
+- Use IDs or PagerDuty URLs from the request or the thread first. A URL such as `https://.pagerduty.com/schedules/PABC123` contains the ID.
+- Otherwise resolve names with one search: `GET /schedules?query=`, `GET /escalation_policies?query=`, `GET /services?query=`, `GET /teams?query=`, or `GET /users?query=`.
+- If a search returns several plausible matches, list them and ask which one the user means.
+
+2. Pick the most direct endpoint.
+
+| Question | Endpoint |
+| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
+| Who is on call now for a schedule or policy | `GET /oncalls?schedule_ids[]=` or `escalation_policy_ids[]=`; add `earliest=true` |
+| Who is rotating in or out, next handoff | `GET /schedules/?since=&until=`; read `final_schedule.rendered_schedule_entries` |
+| What is a user's on-call coverage | `GET /oncalls?user_ids[]=&since=&until=` |
+| Who is on a schedule | `GET /schedules//users` |
+| What is the escalation path | `GET /escalation_policies/` |
+| Which policy pages for a service | `GET /services/?include[]=escalation_policies` |
+| Team members | `GET /teams//members?include[]=users` |
+| Open incidents | `GET /incidents?statuses[]=triggered&statuses[]=acknowledged`; filter with `service_ids[]` or `team_ids[]` |
+| Incident detail, timeline, or notes | `GET /incidents/`, `GET /incidents//log_entries`, `GET /incidents//notes` |
+
+3. Bound every query.
+
+- Default "now" questions to the current time. Get it with `date -u +%Y-%m-%dT%H:%M:%SZ`.
+- Default handoff questions to the next 7 days. Default incident history to the last 24 hours.
+- In `/oncalls` results, `escalation_level` `1` is the primary responder. Say which level you report.
+
+4. Report the result.
+
+- Lead with the answer: names, levels, and shift start and end times in the user's zone when known.
+- For handoffs, name who is on call now, who is next, and the handoff time.
+- Link to the object with its `html_url` when the response contains one. Do not build links from partial IDs.
+- Do not dump raw JSON.
+
+## Guardrails
+
+- This skill is read-only. Decline requests to acknowledge, resolve, reassign, snooze, or create incidents, to add notes or responders, or to create overrides. Tell the user to do it in PagerDuty.
+- Share only the contact details that the question needs. Do not list phone numbers or email addresses unless the user asks for them.
+- `401 Unauthorized`: report that the PagerDuty credential is missing or invalid for this Junior deployment. Do not retry.
+- `403 Forbidden`: report that the PagerDuty credential cannot read the resource. Do not guess at missing permissions.
+- `429 Too Many Requests`: wait briefly and retry the same request once. If it fails again, report the throttle.
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index bb545f8cf1..4f9c985365 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -675,6 +675,8 @@ importers:
packages/junior-octolens: {}
+ packages/junior-pagerduty: {}
+
packages/junior-plugin-api:
dependencies:
commander:
diff --git a/scripts/bump-release-versions.mjs b/scripts/bump-release-versions.mjs
index 4e5bd8da7c..7fae8b0a65 100644
--- a/scripts/bump-release-versions.mjs
+++ b/scripts/bump-release-versions.mjs
@@ -24,6 +24,7 @@ const files = [
"packages/junior-memory/package.json",
"packages/junior-notion/package.json",
"packages/junior-octolens/package.json",
+ "packages/junior-pagerduty/package.json",
"packages/junior-maintenance/package.json",
"packages/junior-sentry/package.json",
"packages/junior-vercel/package.json",