Heartbeat monitors
The push URL, the grace period, and what a missed ping produces.
Heartbeat monitors
A heartbeat monitor is passive: instead of upzero probing a target, your own cron job, scheduler or worker pushes to upzero on a schedule you define, and upzero watches for pushes stopping.
Creating one
Heartbeat monitors take no url. heartbeat_period is how often you expect
to push, in seconds (60-86400); grace_period is extra slack before a missed
push counts against you (60-3600). Both default to 300 and 60 if omitted.
curl -X POST https://api.up0.io/api/v1/me/monitors \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "Nightly backup job",
"type": "heartbeat",
"interval_seconds": 300,
"config": { "heartbeat_period": 3600, "grace_period": 300 }
}'The response includes config.heartbeat_token, a UUID generated once at
creation time. That token, not the monitor's own id, is the credential your
job pushes with. There is no way to view it again through the API after
creation, so save it when you create the monitor.
The push URL
curl -X POST https://api.up0.io/api/v1/heartbeat/<HEARTBEAT_TOKEN>No Authorization header is required, since the token in the URL is the
credential. If you do send an X-API-Key header, it must be a valid key
belonging to the monitor's own organization and scoped heartbeats:write; a
bad or under-scoped key is rejected rather than silently ignored.
A successful push returns {"status": "ok"} and nothing else. There is no
payload beyond the token, so a heartbeat monitor cannot carry a job's exit
code or output today, only that it ran.
Add the push to your job
At the end of whatever the job does, whether or not it succeeded on its own
terms, call the push URL. A common pattern is curl -fsS --retry 3 <url> || true at the very end of the script, so a transient network blip on the push
itself doesn't fail the job.
Watch it go up
A brand-new heartbeat monitor starts pending. It does not go down
immediately just because no push has arrived yet. See "what missed produces"
below for exactly when that changes.
Confirm a missed push is caught
Stop pushing (or wait past heartbeat_period + grace_period) and check that
the monitor's status turns down. This is the behaviour that matters: a
heartbeat monitor only proves anything once you have seen it detect an
absence, not just a presence.
What "missed" produces
Every 60 seconds, the same worker that writes probe checks also looks at every heartbeat monitor:
- If a push has been seen, it compares the age of the most recent push
against
heartbeat_period + grace_period. Within that window, the check isup; past it,down. - If no push has ever been seen at all, the monitor is left alone until it is
older than
heartbeat_period + grace_periodsince creation. A brand-new monitor is not markeddownbefore its own job has had a reasonable chance to run once. Once it crosses that age with still no push, it is markeddown.
Either way, the transition writes a normal check row (status: "down", with
no response_time_ms or status_code, since those only apply to probed
types). A monitor recovers to up on the very next push that arrives, no
matter how long it was down.
Walking this live: a heartbeat monitor created with heartbeat_period: 120, grace_period: 60 went down when its 180-second window closed with no push
registered yet, then recovered to up within one worker tick of the next
push arriving, and went down again once pushes stopped and that same
180-second window elapsed again.
Where the push actually goes
A push does not update anything directly. It writes a metric to the platform's internal Pushgateway, the regional Prometheus scrapes that gateway on its own cycle, and the 60-second worker above reads the scraped value the same way it reads probe results. None of that plumbing is something you configure or need to know to use heartbeat monitors. It only explains why a push can take a few seconds to be reflected in the monitor's status rather than being instantaneous.