Monitoring for things without a URL.
A heartbeat inverts the model: instead of us checking you, your job checks in with us. If the ping misses its window, that silence becomes an incident — with the same alert plans, playbooks and status pages as everything else.
The mechanics
Create a heartbeat and you get a unique ping URL. Both verbs work, no auth needed — the slug is the credential:
curl -fsS --max-time 10 https://app.upfour.io/v1/heartbeat/<your-slug>Two fields define “on time”:
expected_interval_seconds | How often the job should check in — hourly, daily, monthly, anything |
grace_period_seconds | Slack on top before the silence counts — a nightly backup that takes 20–40 minutes gets a grace that covers the spread |
Ping arrives in time: all quiet. Interval plus grace passes without one: incident, alerts, the works. When the next ping lands, the heartbeat recovers and the recovery notice goes out.
Put the ping at the end
Order matters — the ping must depend on the job's success, so chain it with
&&, never ;:
#!/bin/sh
pg_dump mydb | gzip > /backups/nightly.sql.gz \
&& curl -fsS --max-time 10 https://app.upfour.io/v1/heartbeat/<your-slug>The backup recipe has the full crontab treatment, including sizing the grace window.
Operations
| Pause / resume | For planned gaps (disabling a cron job over a migration). Paused heartbeats never alert. |
| Ping log | Every ping is recorded with its timestamp and source IP — GET /heartbeats/{id}/pings. |
| Groups | Fold heartbeats into folders, same as monitor groups. |
| Availability | GET /heartbeats/{id}/availability reports on-time performance over a range. |
Via API
Full CRUD plus pause/resume/pings in the
heartbeats reference — create
heartbeats from your deployment tooling and the ping URL comes back in the response. The events
heartbeat.missed and
heartbeat.recovered are available as
signed webhooks.