# Pingdom check alert notifications on your phone

> Get a Highest-priority push notification when a Pingdom uptime check goes down or a transaction check starts failing, and a quiet one when it recovers.

- Company: Pingdom (https://www.justpush.io/recipes/pingdom)
- Event: `state_change` (State change webhook)
- Tags: Monitoring & observability, Uptime & downtime
- Install: https://studio.justpush.io/recipes/pingdom/check-alert
- Web page: https://www.justpush.io/recipes/pingdom/check-alert

## Setup

1. Click Install in Studio and sign in. The recipe is added to your account and you get a personal webhook URL.
2. In My Pingdom, open Integrations > Integrations and click Add new.
3. Give it a name, paste your webhook URL (shown in Studio after install) as the URL and click Save integration.
4. Open each uptime or transaction check that should notify you and tick the new webhook.
5. In My Pingdom, open **Integrations → Integrations** and click **Add new**. Give the integration a name, paste the URL above into the **URL** field and click **Save integration**.
6. Then open each check under **Monitoring → Uptime** (or your transaction checks) and tick the new webhook. Pingdom sends one JSON POST per state change: **UP ↔ DOWN** for uptime checks (HTTP, TCP, Ping, DNS, UDP, SMTP, POP3, IMAP) and **SUCCESS ↔ FAILING** for transaction checks. Every check type works with this recipe.
7. These are Pingdom's *state change* webhooks, not BeepManager webhooks, which have a different payload and aren't supported here.
8. A test webhook sent from My Pingdom arrives as a quiet "Pingdom connected" push. A down alert is Highest priority, or High when the check's alert importance is set to low (Pingdom's docs only show the `HIGH` value, so the low value is assumed).
9. Pingdom doesn't document signing or retries for webhooks, so keep your endpoint URL private.

## Code

Studio calls `handleRequest(request)` with the incoming webhook (`{ method, headers, body, query, raw }`) and sends the message object it returns. Returning `null` sends nothing.

```js
// Pingdom → state change webhook (uptime and transaction checks)
// Fires when an uptime check goes UP ↔ DOWN, or a transaction check goes SUCCESS ↔ FAILING.
// Docs: https://www.pingdom.com/resources/webhooks/

// Each state, how to word it, and how loud to be.
const STATES = {
    DOWN: { emoji: "🔴", verb: "is down", bad: true },
    FAILING: { emoji: "🔴", verb: "is failing", bad: true },
    UP: { emoji: "✅", verb: "is back up", bad: false },
    SUCCESS: { emoji: "✅", verb: "is passing again", bad: false },
}

// Pingdom's check types, in words. Anything unlisted falls back to "Check".
const TYPES = {
    HTTP: "HTTP check",
    HTTP_CUSTOM: "HTTP custom check",
    PORT_TCP: "TCP check",
    PING: "Ping check",
    DNS: "DNS check",
    UDP: "UDP check",
    SMTP: "SMTP check",
    POP3: "POP3 check",
    IMAP: "IMAP check",
    TRANSACTION: "Transaction check",
}

// Table lookup that ignores inherited keys like "toString".
function pick(table, key, fallback) {
    return typeof key === "string" && Object.prototype.hasOwnProperty.call(table, key) ? table[key] : fallback
}

// Only link to real http(s) URLs.
function link(url, cta) {
    return typeof url === "string" && /^https?:\/\//.test(url) ? [{ cta, url }] : []
}

// A trimmed string, or null when empty or not a string/number.
function text(value) {
    if (typeof value !== "string" && typeof value !== "number") return null
    const s = String(value).trim()
    return s || null
}

function handleRequest(request) {
    const body = request.body && typeof request.body === "object" ? request.body : {}
    const state = pick(STATES, text(body.current_state)?.toUpperCase(), null)

    // Guard: Pingdom sends no event header, so recognise its state change payload by shape.
    if (body.check_id == null || !state) {
        console.log("Not a Pingdom state change webhook — skipping")
        return null
    }

    const params = body.check_params && typeof body.check_params === "object" ? body.check_params : {}
    const type = text(body.check_type)?.toUpperCase()
    const name = text(body.check_name) ?? text(params.hostname) ?? pick(TYPES, type, "Your Pingdom check")
    // Uptime checks carry full_url; transaction checks put the start URL in url.
    const url = [params.full_url, params.url].map(text).find((u) => u && /^https?:\/\//.test(u)) ?? null
    const where = url ?? text(params.hostname)
    const buttons = link(url, "Open site")

    // "Test" in My Pingdom sends description "test" and this long_description.
    const longText = text(body.long_description)
    if (text(body.description) === "test" || /test message triggered by a user/i.test(longText ?? "")) {
        return {
            title: "🔔 Pingdom connected",
            message: `Test webhook for ${name} received`,
            topic: "Pingdom",
            priority: -1, // Low — just confirming the setup works
        }
    }

    if (state.bad) {
        const reason = text(body.description) ?? longText
        const low = text(body.importance_level)?.toUpperCase() === "LOW"
        return {
            title: `${state.emoji} ${name} ${state.verb}`,
            message: [reason, where].filter(Boolean).join(" · ") || `${pick(TYPES, type, "Check")} ${state.verb}`,
            topic: "Pingdom",
            // Highest — the site or user flow is broken right now. High when the check's
            // alert importance is set to low in Pingdom.
            priority: low ? 1 : 2,
            buttons,
        }
    }

    // Recovery: description is "OK" (uptime) or empty (transaction), so show where instead.
    return {
        title: `${state.emoji} ${name} ${state.verb}`,
        message: where ? `Recovered · ${where}` : `${pick(TYPES, type, "Check")} recovered`,
        topic: "Pingdom",
        priority: -1, // Low — good news, nothing to do
        buttons,
    }
}
```

## Pingdom state_change webhook payload (sample)

```json
{
  "tags": [
    "production"
  ],
  "check_id": 8123456,
  "check_name": "Acme website",
  "check_type": "HTTP",
  "description": "HTTP Error 503",
  "first_probe": {
    "ip": "192.0.2.10",
    "ipv6": "2001:db8::10",
    "location": "Stockholm, Sweden"
  },
  "check_params": {
    "url": "/",
    "ipv6": false,
    "port": 443,
    "header": "User-Agent:Pingdom.com_bot",
    "full_url": "https://www.example.com/",
    "hostname": "www.example.com",
    "basic_auth": false,
    "encryption": true
  },
  "second_probe": {
    "ip": "192.0.2.20",
    "ipv6": "",
    "version": 1,
    "location": "Austin, US"
  },
  "current_state": "DOWN",
  "previous_state": "UP",
  "importance_level": "HIGH",
  "long_description": "HTTP Server Error 503 Service Unavailable",
  "state_changed_utc_time": "2026-09-21T14:13:20",
  "state_changed_timestamp": 1790000000
}
```

## FAQ

### 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 Pingdom 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.
