PagerDuty incident acknowledged or resolved notifications on your phone.

Get a quiet push notification when a PagerDuty incident is acknowledged or resolved, and a louder one when an acknowledgement runs out.

How it works

Three steps to your first push

STEP 01

Install the recipe

One click in Studio gives you a personal webhook URL.

STEP 02

Paste your URL into PagerDuty

Add it as a webhook for incident.acknowledged, incident.unacknowledged and incident.resolved.

STEP 03

Get a push on your phone

With the text, sound and buttons from the recipe.

Setup

How to set up the PagerDuty incident.acknowledged, incident.unacknowledged and incident.resolved webhook

  1. Click Install in Studio and sign in. The recipe is added to your account and you get a personal webhook URL.
  2. In PagerDuty, go to Integrations > Generic Webhooks (v3) and click New Webhook (or add one on a service's Integrations tab).
  3. Paste your webhook URL as the Webhook URL.https://••••••••/•••••••• your personal URL, shown after install
  4. Under Event Subscription, tick incident.acknowledged, incident.unacknowledged and incident.resolved and click Add Webhook.
  5. Create a v3 webhook under **Integrations → Generic Webhooks (v3) → New Webhook** (Managers and Admins), or on a single service under **Services → Service Directory → your service → Integrations → Webhooks**. Paste the URL above and tick **incident.acknowledged**, **incident.unacknowledged** and **incident.resolved**. The recipe ignores every other event type, so new incidents are left to the **Incident triggered** recipe.
  6. To only hear about resolved incidents, use this URL instead:
  7. ```
  8. your webhook URL?resolved=1
  9. ```
  10. *Unacknowledged* means the incident is back to triggered (the acknowledgement timed out or someone undid it), so that push is High priority. The others are Low.
  11. To check the connection, open the webhook, click **Send Test Event** under **Test**, and you'll get a quiet "PagerDuty connected" push.
  12. PagerDuty signs every v3 webhook with an `X-PagerDuty-Signature` header (HMAC-SHA256 with the subscription's secret), but JustPush doesn't check that signature yet, so keep your endpoint URL private.
The code

What runs when the webhook arrives

Studio calls handleRequest(request) with the incoming webhook and sends the message it returns. It's yours after install; change anything.

transform.js
1// PagerDuty → incident.acknowledged, incident.unacknowledged, incident.resolved
2// Fires when someone acknowledges a PagerDuty incident, the acknowledgement runs out (or is
3// undone), or the incident is resolved.
4// Docs: https://docs.pagerduty.com/developer/webhooks-overview
5
6// Each event, how to label it, and how loud to be.
7const EVENTS = {
8 // Low: someone is already on it.
9 "incident.acknowledged": { emoji: "👀", verb: "Acknowledged", priority: -1 },
10 // High: the incident is back to triggered and nobody is on it any more.
11 "incident.unacknowledged": { emoji: "⚠️", verb: "Unacknowledged", priority: 1 },
12 // Low: it's over.
13 "incident.resolved": { emoji: "✅", verb: "Resolved", priority: -1 },
14}
15
16// Look a key up without tripping over inherited names like "toString".
17function pick(table, key, fallback) {
18 return typeof key === "string" && Object.prototype.hasOwnProperty.call(table, key) ? table[key] : fallback
19}
20
21// Only link to real http(s) URLs.
22function link(url, cta) {
23 return typeof url === "string" && /^https?:\/\//.test(url) ? [{ cta, url }] : []
24}
25
26// A trimmed string, or null for anything else.
27function text(value) {
28 return typeof value === "string" && value.trim() ? value.trim() : null
29}
30
31// "12m" / "1h 5m" / "2d 3h" between two timestamps.
32function between(start, end) {
33 const minutes = Math.round((Date.parse(end) - Date.parse(start)) / 60000)
34 if (!Number.isFinite(minutes) || minutes < 0) return null
35 if (minutes < 60) return `${Math.max(minutes, 1)}m`
36 if (minutes < 1440) return `${Math.floor(minutes / 60)}h ${minutes % 60}m`
37 return `${Math.floor(minutes / 1440)}d ${Math.floor((minutes % 1440) / 60)}h`
38}
39
40function handleRequest(request) {
41 const body = request.body && typeof request.body === "object" ? request.body : {}
42 const event = body.event && typeof body.event === "object" ? body.event : {}
43 const type = event.event_type
44
45 // "Send Test Event" on the subscription sends a pagey.ping; confirm the connection quietly.
46 if (type === "pagey.ping") {
47 return {
48 title: "🔔 PagerDuty connected",
49 message: text(event.data?.message) ?? "Your webhook subscription is working",
50 topic: "PagerDuty",
51 priority: -1,
52 }
53 }
54
55 // Guard: only acknowledge / unacknowledge / resolve. v3 webhooks have no event header,
56 // so the payload's event_type decides.
57 const state = pick(EVENTS, type, null)
58 const incident = event.data
59 if (!state || !incident || typeof incident !== "object") {
60 console.log(`Ignoring PagerDuty "${type ?? "unknown"}" event`)
61 return null
62 }
63
64 // ?resolved=1 on the webhook URL only pushes when incidents are resolved.
65 const onlyResolved = ["1", "true", "yes"].includes(String(request.query?.resolved ?? "").toLowerCase())
66 if (onlyResolved && type !== "incident.resolved") {
67 console.log(`Skipping "${type}" because of ?resolved=1`)
68 return null
69 }
70
71 const title = text(incident.title) ?? text(incident.summary) ?? "An incident"
72 // agent is null when PagerDuty or an integration did it (e.g. an auto-resolve).
73 const who = text(event.agent?.summary)
74 const took = type === "incident.resolved" ? between(incident.created_at, event.occurred_at) : null
75 const what = [state.verb, who ? `by ${who}` : null, took ? `after ${took}` : null].filter(Boolean).join(" ")
76 const number = Number.isFinite(incident.number) ? `#${incident.number}` : null
77
78 return {
79 title: `${state.emoji} ${title}`,
80 message: [what, number, text(incident.service?.summary)].filter(Boolean).join(" · "),
81 topic: "PagerDuty",
82 priority: state.priority, // see EVENTS above
83 buttons: link(incident.html_url, "Open incident"),
84 }
85}
Payload

The PagerDuty incident.resolved webhook

This is what PagerDuty sends to your URL for incident.acknowledged, incident.unacknowledged and incident.resolved. It is a sample, trimmed to the fields recipes use.

incident.resolved · sample.json
1{
2 "event": {
3 "id": "01D8XQ5T8MB2V6C1Y3F7K0EFGH",
4 "data": {
5 "id": "PGR0VU2",
6 "self": "https://api.pagerduty.com/incidents/PGR0VU2",
7 "type": "incident",
8 "teams": [
9 {
10 "id": "PFCVPS0",
11 "self": "https://api.pagerduty.com/teams/PFCVPS0",
12 "type": "team_reference",
13 "summary": "Payments",
14 "html_url": "https://acme.pagerduty.com/teams/PFCVPS0"
15 }
16 ],
17 "title": "Checkout API error rate above 5%",
18 "number": 1234,
19 "status": "resolved",
20 "service": {
21 "id": "PF9KMXH",
22 "self": "https://api.pagerduty.com/services/PF9KMXH",
23 "type": "service_reference",
24 "summary": "Checkout API",
25 "html_url": "https://acme.pagerduty.com/services/PF9KMXH"
26 },
27 "urgency": "high",
28 "html_url": "https://acme.pagerduty.com/incidents/PGR0VU2",
29 "priority": {
30 "id": "PSO75BM",
31 "self": "https://api.pagerduty.com/priorities/PSO75BM",
32 "type": "priority_reference",
33 "summary": "P1",
34 "html_url": "https://acme.pagerduty.com/account/incident_priorities"
35 },
36 "assignees": [
37 {
38 "id": "PTUXL6G",
39 "self": "https://api.pagerduty.com/users/PTUXL6G",
40 "type": "user_reference",
41 "summary": "Sam Lee",
42 "html_url": "https://acme.pagerduty.com/users/PTUXL6G"
43 }
44 ],
45 "created_at": "2026-09-29T10:51:28Z",
46 "incident_key": "d3640fbd41094207a1c11e58e46b1662",
47 "resolve_reason": null,
48 "conference_bridge": null,
49 "escalation_policy": {
50 "id": "PUS0KTE",
51 "self": "https://api.pagerduty.com/escalation_policies/PUS0KTE",
52 "type": "escalation_policy_reference",
53 "summary": "Checkout on-call",
54 "html_url": "https://acme.pagerduty.com/escalation_policies/PUS0KTE"
55 }
56 },
57 "agent": {
58 "id": "PTUXL6G",
59 "self": "https://api.pagerduty.com/users/PTUXL6G",
60 "type": "user_reference",
61 "summary": "Sam Lee",
62 "html_url": "https://acme.pagerduty.com/users/PTUXL6G"
63 },
64 "client": {
65 "name": "PagerDuty"
66 },
67 "event_type": "incident.resolved",
68 "occurred_at": "2026-09-29T11:33:52.402Z",
69 "resource_type": "incident"
70 }
71}
FAQ

PagerDuty incident acknowledged or resolved notifications: questions

Does this work on iPhone and Android?

Yes. Install the JustPush app from the App Store or Google Play and sign in. Every phone signed in to your account gets the notification.

Do I need to write code?

No. Install the recipe in Studio, paste your webhook URL into PagerDuty and you are done. The code is there if you want to change the text, the sound or the buttons.

Can I change what the notification says?

Yes. After install the recipe's code is yours. Edit it in Studio and test it against the sample payload before you save.

What does it cost?

JustPush is free for 30 days. After that it's $19.99 a year, or $39.99 once. There is no extra charge for recipes.

Ready when you are

PagerDuty on your phone in two minutes.

Install the recipe, paste one URL, done. Free for 30 days, no credit card required.