Skip to content

Heartbeat (cron jobs)

A heartbeat monitor (type push) works the other way round from the other checks: OKStatus doesn’t call your service, your job calls OKStatus each time it runs. If no ping arrives in time, the monitor goes down and you’re alerted.

Use it for anything that runs on a schedule and has no URL to probe: cron jobs, backups, queue workers, nightly imports, certificate renewals.

Terminal window
curl -X POST https://api.okstatus.eu/api/v1/monitors \
-H "Authorization: Bearer $OKSTATUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Nightly backup", "type": "push", "url": "nightly-backup", "interval": 86400, "gracePeriod": 3600 }'
  • url is only a label for a heartbeat: nothing is fetched.
  • interval is how often the job runs, in seconds.
  • gracePeriod is the extra time allowed before the monitor goes down. Without it, it equals interval.

The response carries the monitor’s pushUrl:

{
"data": {
"id": "…",
"type": "push",
"pushToken": "…",
"pushUrl": "https://backend.okstatus.eu/push/…"
}
}

The same pushUrl comes back every time you read the monitor with a Full key. A read-only key, and a member with the viewer role, see the monitor without its token and URL. With Terraform, build the URL from the push_token attribute:

output "backup_ping_url" {
value = "https://backend.okstatus.eu/push/${okstatus_monitor.backup.push_token}"
sensitive = true
}

Call the URL at the end of each successful run. POST, GET and HEAD all count as an up ping:

Terminal window
curl -fsS -m 10 --retry 3 -X POST https://backend.okstatus.eu/push/<token>

A crontab line that pings only when the job succeeds:

Terminal window
0 3 * * * /usr/local/bin/backup.sh && curl -fsS -m 10 --retry 3 -X POST https://backend.okstatus.eu/push/<token> > /dev/null

To report a failure or send a message, use POST with a body (below): a GET is always an up ping. Link previews in chat tools fetch URLs with GET, which is one more reason not to paste the URL in a conversation.

The body is optional. When present, it’s JSON:

FieldTypeDescription
statusstringup (default) or down: report a failure right away, without waiting
msgstringUp to 2,000 characters, kept with the check and shown in the dashboard and alerts
pingintegerHow long the job took, in milliseconds (0 to 60,000)
Terminal window
if ! /usr/local/bin/backup.sh; then
curl -fsS -m 10 -X POST https://backend.okstatus.eu/push/<token> \
-H "Content-Type: application/json" \
-d '{ "status": "down", "msg": "backup.sh exited with an error" }'
fi

An empty body is an up ping. A body that isn’t valid JSON, or whose fields don’t match the table, is refused with 400 and records nothing, so a typo such as "status": "failed" can’t pass for a success.

The monitor is down when no ping has arrived for interval + gracePeriod seconds since the last one. OKStatus checks every 30 seconds.

  • Before the first ping, the monitor has no state and never goes down: send one ping when you set the job up.
  • A ping after a timeout brings the monitor back up and sends the recovery alert.
  • Paused: pings are accepted but recorded nowhere and alert nobody. Resuming starts from the last ping received.
  • Muted or in a maintenance window: pings are recorded, alerts are held back, as for any monitor.

The ping URL has no region: the majority rule of multi-region checks doesn’t apply to heartbeats.

StatusBodyMeaning
200{ "ok": true }Ping recorded
200{ "ok": true, "paused": true }The monitor is paused: nothing recorded
400{ "error": "INVALID_BODY" }The body isn’t JSON, or a field is out of range: nothing recorded
404{ "error": "NOT_FOUND" }Unknown token, or the monitor isn’t a heartbeat
413{ "error": "PAYLOAD_TOO_LARGE" }Body over 8 KiB
429{ "error": "RATE_LIMIT_EXCEEDED" }More than 30 pings a minute for this monitor; see Retry-After

The token in the URL is the only credential: anyone who has it can send pings and hide a failed job. Keep the URL in a secret or an environment variable rather than in a public repository, and don’t paste it in tickets or chats. If it leaks, delete the monitor and create a new one: the token can’t be rotated in place.

The Prometheus export has okstatus_push_last_received_timestamp_seconds, the time of the last ping, for each heartbeat monitor.