Heartbeat Monitors
A heartbeat monitor watches a job that runs somewhere else: a crontab line, a GitHub Actions workflow, a Vercel cron route, a database job. Chronos never runs the job. The job calls the monitor’s ping URL when it finishes, and Chronos alerts you when a ping reports a failure or doesn’t arrive in time.
curl -fsS -m 10 --retry 5 -o /dev/null https://ping.chronos.sh/k3Jx9Qm2TnV7bRw4YcZ8dHfLEvery example on these pages uses that placeholder ping URL. Replace it with your monitor’s ping_url, which the dashboard shows when you create the monitor. The token at the end of the URL is the only credential, so keep the URL out of public repositories and logs.
Set up a monitor
Section titled “Set up a monitor”- Create a monitor in the dashboard, or with
POST /v1/monitors. Give it the cadence your job runs on. - Copy the
ping_url. - Add a ping to the end of your job. Pick your platform:
- Run the job once. The monitor stays
newand sends no alert until its first success or fail ping arrives.
What to ping
Section titled “What to ping”| Path | Means |
|---|---|
/<token> |
The job succeeded. |
/<token>/fail |
The job failed. |
/<token>/<exit-code> |
The job exited with this code, from 0 to 255. 0 is a success and any other code is a failure. |
/<token>/start |
The job started. Optional. |
A start ping never changes the monitor’s status or deadline. It gives the next success or fail ping a duration, so the ping history shows how long each run took.
GET, HEAD, and POST all work. Chronos stores up to 10 KB of a POST body with the ping. See the Ping API for responses and rate limits.
In application code, ping at the end of the job and on the failure path:
const pingUrl = process.env.CHRONOS_PING_URL;
async function ping(path, body) { try { const res = await fetch(pingUrl + path, { method: 'POST', body, signal: AbortSignal.timeout(10_000) }); if (!res.ok) console.error('chronos ping failed', res.status, await res.text()); } catch (err) { console.error('chronos ping failed', err); }}
await ping('/start');try { await runJob();} catch (err) { await ping('/fail', String(err)); throw err;}await ping('');import osimport requests
PING_URL = os.environ["CHRONOS_PING_URL"]
def ping(path="", body=None): try: requests.post(PING_URL + path, data=body, timeout=10).raise_for_status() except requests.RequestException as err: print("chronos ping failed:", type(err).__name__, getattr(err.response, "status_code", None))
ping("/start")try: run_job()except Exception as err: ping("/fail", str(err).encode()) raiseping()The ping helper logs a lost ping and returns, so a network error or a non-200 response, such as 404 for a rotated URL, never fails the job. On those errors the log line leaves out the URL, because the token in the URL is the monitor’s credential. If the success ping is lost, the monitor goes down after the grace period.
Cadence and grace period
Section titled “Cadence and grace period”A monitor has a cadence, either a cron expression or an interval, and a grace period.
- Interval: the next ping is due one interval after the last success or fail ping.
- Cron: the next ping is due at the next cron occurrence, in the monitor’s
timezone(your account’s timezone unless you set one). A ping that arrives a little early counts for the upcoming occurrence. Early means within the smaller of the grace period and half the gap between occurrences. - Grace period: how long Chronos waits after a ping is due before it alerts. The grace period defaults to 5 minutes and can be set from 60 seconds to 7 days. Set it to cover how long the job runs plus any scheduling delay on its platform.
The cadence can’t be more frequent than every 60 seconds, on every plan. An interval can be up to 365 days, and a cron expression must fire at least twice in the next 2 years.
The API returns the current deadlines on every monitor: next_ping_due_at is when the next success or fail ping is expected, and alert_at is that time plus the grace period.
Statuses
Section titled “Statuses”| Status | Means |
|---|---|
new |
Created or resumed, and no success or fail ping since. Never alerts. |
up |
The last ping was a success, and the next one isn’t due yet. |
late |
up, but next_ping_due_at has passed. Still inside the grace period, so no alert yet. |
down |
The last ping was a failure, or no ping arrived by alert_at. |
paused |
Sends no alerts and has no deadline. |
Chronos checks deadlines once a minute, so a missed-ping alert can arrive up to a minute after alert_at.
Alerts
Section titled “Alerts”A monitor sends one down alert when it goes down, and one up alert when it recovers. It goes down in two ways:
- A fail ping arrives, from
/failor a non-zero exit code. The alert includes the exit code and the first 500 characters of the ping’s body. - No success or fail ping arrives by
alert_at. The alert includes when the ping was due, the last ping, and the grace period.
While a monitor is down, more fail pings or lost pings send no further alerts. The next success ping brings it back up and sends the up alert.
Monitor alerts go to every channel enabled in the dashboard’s alert settings: email, Slack, and a signed webhook. The job alert triggers and the missed-run threshold don’t apply to monitors. No setting turns monitor alerts off. To stop alerts from one monitor, pause it.
Pause, resume, and delete
Section titled “Pause, resume, and delete”- Pause stops alerts and clears the deadlines. Pings to a paused monitor still return
200and updatelast_ping, but they aren’t kept in the ping history and change nothing else. Pausing a down monitor sends no up alert. - Resume puts the monitor back to
new, so the next success or fail ping sets its deadline. To cover planned downtime, pause the monitor and resume it afterwards. The skipped runs then send no alert. - Delete removes the monitor and its ping history. The ping URL stops working.
Rotate the ping URL
Section titled “Rotate the ping URL”If a ping URL leaks, rotate it from the dashboard or with POST /v1/monitors/:id/ping-url/rotate. The old URL returns 404 from that moment, so update every job that uses it. A job still on the old URL stops pinging, and the monitor goes down after the grace period.
Plan limits
Section titled “Plan limits”Each plan caps active monitors. See Pricing & Limits.