Skip to content

5.1How heartbeats work

Every other monitor type calls your service. A heartbeat works the other way round: your job calls obsrvly. Each call is a ping, one HTTP request to the monitor's ping URL. The last part of that URL is the token, a secret code that identifies your monitor.

Create a heartbeat monitor#

  1. Select + new monitor, then the heartbeat tab. The page title reads new heartbeat monitor.
  2. Enter a name.
  3. Set the schedule. Leave cron expression empty for a job that runs every few minutes or hours, or enter the job's cron line (5.2).
  4. Set the grace (5.3). The form then shows a summary, for example marked DOWN if no ping arrives within 5m × 2 = 10m.
  5. Choose the alert channels under on failure — notify, then select create monitor →.
  6. On the monitor page, select copy next to the ping URL. The text above it reads POST here from your job every <N>s, or POST here from your job each time it runs for a cron schedule.
  7. Add a ping to your job (5.5 to 5.9), then test it (5.4).

The URL and the setup snippets appear only after you create the monitor. The monitor page also has ready-made snippets under setup snippets: shell, crontab, Laravel, Sidekiq and Celery.

A heartbeat monitor has no check locations, no timeout and no assertions. The location of each result shows as inbound.

This chapter writes the ping URL as https://<your-ping-url>. Replace it with the full URL you copied. It already ends in /heartbeat/<token>.

States#

StateWhen
PendingThe monitor exists, but no ping has arrived. The dashboard list shows PEND.
UpThe latest ping reported success.
DownThe job reported a failure, or no ping arrived by the deadline.
PausedYou paused the monitor. Pings get a normal answer but add no result.

A down result follows your alert policy, like any other monitor (chapter 9, Alerting). The next successful ping brings the monitor back up.

The deadline#

Each heartbeat has a deadline: the time by which the next ping must arrive.

  • Interval schedule: the deadline is the last ping plus expected every × grace periods.
  • Cron schedule: the deadline is the next scheduled run after the last ping, plus grace seconds.
  • Before the first ping, obsrvly counts from the moment you created the monitor.
  • Any finish ping restarts the deadline: success, /fail or an exit code. A /start ping does not.
  • Saving a change to the schedule or the grace restarts the deadline from that moment. A new name or new channels do not.
  • When you resume a paused monitor, a fresh deadline starts from that moment.

Last check-in and the countdown to the deadline show on the monitor.

Messages you may see#

MessageMeaning
job reported failureThe job sent /fail.
job exited with code 3The job sent a non-zero exit code, here 3.
heartbeat never received; first was due by <time>The monitor never got a ping, and the first deadline has passed.
heartbeat overdue: last ping <time>, deadline was <time>A ping was missed.
scheduled run (15 2 * * *) overdue: …A run on a cron schedule was missed. The text names your expression.
…; a run started at <time> and never finishedAdded when the job sent /start but no finish ping.

Times in messages are in UTC, for example 2026-10-27T10:22:00Z.

Worked timeline#

A job runs every 5 minutes. The monitor uses expected every 5 minutes and grace periods 2. So each ping must come within 10 minutes of the last one. All times are UTC.

TimeWhat happensResultNext deadline
10:00You create the monitor.Pending10:10
10:03The job pings.Up10:13
10:08The job sends /fail.Down: job reported failure10:18
10:12The job pings.Up10:22
10:22No ping arrives. Shortly after the deadline, obsrvly adds a result.Down: heartbeat overdue: last ping 2026-10-27T10:12:00Z, deadline was 2026-10-27T10:22:00Z—
10:40The job pings again.Up10:50

Last updated