Guide · scheduled jobs

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_secondsHow often the job should check in — hourly, daily, monthly, anything
grace_period_secondsSlack 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 / resumeFor planned gaps (disabling a cron job over a migration). Paused heartbeats never alert.
Ping logEvery ping is recorded with its timestamp and source IP — GET /heartbeats/{id}/pings.
GroupsFold heartbeats into folders, same as monitor groups.
AvailabilityGET /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.