IronWatchDocs
Dashboard
PingWatch · Quickstart
~3 min readsynced 2026-05-29·/api/v1 + ping routes@live

Your first heartbeat

PingWatch is a dead-man's switch: you tell it how often to expect a beat, then have your job curlits ping URL when it finishes. As long as the beats keep arriving on time, PingWatch stays quiet. When one doesn't arrive inside the window, it pages you. Add a single line to the endof your cron or backup job and you're covered.

~/projects/acme · zsh
$# at the very END of your backup script curl -fsS "https://pingwatch.dev/api/ping/d290f1ee-6c54-4b01-90e6-d701748f0851"
200OK· 38ms · 2 bytes
OK

What is PingWatch?

Heartbeats

PingWatch

A heartbeat is missing? You'll know.

PingWatch listens for a periodic "I'm alive" signal from your jobs and pages your on-call when the signal stops arriving.

What it does
  • Receive heartbeats over HTTP / one-line shell snippet
  • Alert through email and webhook
  • Flexible windows: "expect a beat every 5m, ±30s tolerance"
What it does not do
  • ↛ Not usActively probe your URL
    PingWatch is passive. WatchTower probes.
    → try WatchTower
  • ↛ Not usSchedule the work itself
    PingWatch knows you're late, not how to fire on time.
    → try CronPilot
  • ↛ Not usPublic outage page
    PingWatch is internal alerting.
    → try FlipSign

Create a monitor

A monitor is one heartbeat expectation. Create it in the dashboard (New monitor) or over the API. The only required fields are namerequired and schedulerequired — an interval string like 5m, 1h, or 24h. The response carries the monitor's id, which is the heart of its ping URL.

curl
curl -X POST "https://pingwatch.dev/api/v1/monitors" \
  -H "Authorization: Bearer <PINGWATCH_API_KEY>" \
  -H "Content-Type: application/json" \
  -d {
    "name": "Nightly DB backup",
    "schedule": "24h",
    "gracePeriodSeconds": 600
  }'
Response · 201
{
  "id":                 "d290f1ee-6c54-4b01-90e6-d701748f0851",
  "userId":             "8RXc1Pm9-...",
  "name":               "Nightly DB backup",
  "slug":               null,
  "schedule":           "24h",
  "scheduleType":       "interval",
  "gracePeriodSeconds": 600,
  "status":             "new",
  "lastPingAt":         null,
  "lastPingType":       null,
  "nextExpectedAt":     null,
  "alertSentAt":        null,
  "consecutiveMisses":  0,
  "isPaused":           false,
  "createdAt":          "2026-05-29T17:55:00.000Z",
  "updatedAt":          "2026-05-29T17:55:00.000Z"
}

A fresh monitor starts in status newand won't alert until it has received its first ping. The gracePeriodSeconds (default 300, range 6086400) is the slack you allow past the interval before the monitor is considered late.

Ping at the end of the job

Drop the ping at the very end of the work, after it has actually succeeded. A bare GET or POST to the ping URL records a success: the monitor flips to healthy, the stale timer resets, and any active alert is cleared. Use curl -fsS so the ping itself never silently fails.

backup.sh
#!/usr/bin/env bash
set -euo pipefail

PING="https://pingwatch.dev/api/ping/d290f1ee-6c54-4b01-90e6-d701748f0851"

# 1. tell PingWatch the job has started (optional — enables duration timing)
curl -fsS "$PING/start" >/dev/null

# 2. do the work
pg_dump mydb | gzip | aws s3 cp - s3://backups/mydb.sql.gz

# 3. signal success — resets the timer and clears any active alert
curl -fsS "$PING" >/dev/null

Start, success & fail

Three ping endpoints share the same monitor, addressed by {id} (UUID or custom slug):

  • POST /api/ping/{id}/start — fire when the job begins. PingWatch records the start so a later success can be timed (start → success duration), but it does not reset the stale timer on its own.
  • GET|POST /api/ping/{id} — the success beat. This is the one that keeps the monitor healthy.
  • GET|POST /api/ping/{id}/fail — report an explicit failure. The monitor goes down immediately and your on-call is paged, rather than waiting for the window to lapse.
backup.sh — report failures
# wrap the job so a non-zero exit reports a failure immediately
PING="https://pingwatch.dev/api/ping/d290f1ee-6c54-4b01-90e6-d701748f0851"

if pg_dump mydb | gzip | aws s3 cp - s3://backups/mydb.sql.gz; then
  curl -fsS "$PING"
else
  curl -fsS "$PING/fail"   # marks the monitor "down" and pages your on-call
fi

Custom slugs

The UUID works everywhere, but you can give a monitor a readable slug and ping /api/ping/nightly-backup instead. Set it on create or update with the slug field (or from the dashboard). Slugs are lowercase letters, numbers, and single dashes, 2–64 characters.

curl — set a slug
curl -X PATCH "https://pingwatch.dev/api/v1/monitors/d290f1ee-6c54-4b01-90e6-d701748f0851" \
  -H "Authorization: Bearer <PINGWATCH_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{ "slug": "nightly-backup" }'

Embeddable status badge

Every monitor exposes a live SVG badge at /api/badge/{id} (UUID or slug — no auth needed). Drop it in a README or status page; it renders the monitor name and its current state (healthy, late, down, paused, or new).

Markdown
![backup status](https://pingwatch.dev/api/badge/nightly-backup)

Next steps

Cover a job in one line.
Create a monitor, paste its ping URL at the end of your cron or backup script, and let PingWatch page you the moment a beat goes missing.