Better Stack incident notifications on your phone.

Get a Highest-priority push notification when a Better Stack incident starts, and quiet ones when it's acknowledged or resolved, plus incident comments.

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 Better Stack

Add it as a webhook for Incident.

STEP 03

Get a push on your phone

With the text, sound and buttons from the recipe.

Setup

How to set up the Better Stack incident 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 Better Stack Uptime, go to Integrations > Exporting data > Outgoing webhooks and add one.
  3. Paste your webhook URL as the URL and keep the default template.https://••••••••/•••••••• your personal URL, shown after install
  4. Choose which Incident events to send and save.
  5. In Better Stack Uptime, go to **Integrations → Exporting data → Outgoing webhooks**, add one with the URL above, and pick the incident events: started, acknowledged, resolved, reopened and comments. **Keep the default template**; the recipe reads its fields.
  6. Incidents from Better Stack Telemetry (log and metric alerts) come through here too.
  7. The default template doesn't say which event happened, so the recipe works it out: *resolved* when there's a resolved time, *acknowledged* when there's an acknowledged time, otherwise *started* (a reopened incident looks like a new one). Better Stack doesn't sign webhooks; you can add Basic auth or custom headers, but JustPush doesn't check them, 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// Better Stack → incident started, acknowledged, resolved or commented
2// Fires when a Better Stack Uptime outgoing webhook reports an incident change. Covers
3// uptime monitors, heartbeats and Telemetry alerts (they all create incidents).
4// Docs: https://betterstack.com/docs/uptime/webhooks/
5
6// Only link to real http(s) URLs.
7function link(url, cta) {
8 return typeof url === "string" && /^https?:\/\//.test(url) ? [{ cta, url }] : []
9}
10
11// The template fills unset timestamps with "" or null; treat both (and leftover $VARS) as missing.
12function text(value) {
13 const s = value == null ? "" : String(value).trim()
14 return s && s !== "null" && !/^\$[A-Z_]+$/.test(s) ? s : null
15}
16
17// "12m" / "1h 5m" between two timestamps.
18function between(start, end) {
19 const minutes = Math.round((Date.parse(end) - Date.parse(start)) / 60000)
20 if (!Number.isFinite(minutes) || minutes < 0) return null
21 return minutes < 60 ? `${Math.max(minutes, 1)}m` : `${Math.floor(minutes / 60)}h ${minutes % 60}m`
22}
23
24function handleRequest(request) {
25 const body = request.body || {}
26 const incident = body.data?.attributes
27
28 // Guard: only incident payloads (monitor and on-call webhooks look different).
29 if (body.data?.type !== "incident" || !incident) {
30 console.log("Not a Better Stack incident — skipping")
31 return null
32 }
33
34 const name = text(incident.name) ?? "Incident"
35 const url = text(incident.url)
36 const buttons = [...link(text(incident.screenshot_url), "View screenshot"), ...link(url, "Open site")]
37
38 if (body.event === "comment") {
39 const comment = body.comment?.attributes ?? {}
40 return {
41 title: `💬 ${name}`,
42 message: (text(comment.user_email) ? `${comment.user_email}: ` : "") + (text(comment.content) ?? "New comment"),
43 topic: "Better Stack",
44 priority: 0,
45 buttons,
46 }
47 }
48
49 // The default template has no event name, so the timestamps tell the state.
50 const resolved = text(incident.resolved_at)
51 const acknowledged = text(incident.acknowledged_at)
52 const started = text(incident.started_at)
53
54 if (resolved) {
55 const took = started ? between(started, resolved) : null
56 return {
57 title: `✅ ${name} resolved`,
58 message: took ? `Resolved after ${took}` : "Incident resolved",
59 topic: "Better Stack",
60 priority: -1, // Low — it's over
61 buttons: link(url, "Open site"),
62 }
63 }
64
65 if (acknowledged) {
66 return {
67 title: `👀 ${name} acknowledged`,
68 message: text(incident.cause) ?? "Someone is on it",
69 topic: "Better Stack",
70 priority: -1, // Low — someone is already on it
71 buttons,
72 }
73 }
74
75 return {
76 title: `🔴 ${name}`,
77 message: [text(incident.cause), url].filter(Boolean).join(" · ") || "New incident",
78 topic: "Better Stack",
79 priority: 2, // Highest — something is down right now
80 buttons,
81 }
82}
Payload

The Better Stack incident_change webhook

This is what Better Stack sends to your URL for incident. It is a sample, trimmed to the fields recipes use.

incident_change · sample.json
1{
2 "data": {
3 "id": "123456789",
4 "type": "incident",
5 "attributes": {
6 "url": "https://acme.example.com",
7 "name": "Acme website",
8 "cause": "Status 502",
9 "started_at": "2026-09-29T10:51:28.151Z",
10 "http_method": "get",
11 "resolved_at": null,
12 "response_url": null,
13 "screenshot_url": "https://uptime.betterstack.com/screenshots/123456789.png",
14 "acknowledged_at": null,
15 "response_content": null
16 }
17 }
18}
FAQ

Better Stack incident 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 Better Stack 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

Better Stack on your phone in two minutes.

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