Monitor your AI agent's heartbeat
Get alerted the second an autonomous agent, bot, or worker loop silently stops.
An AI agent is one of the easiest things to lose without noticing. It runs in a loop on a schedule or a long-lived process — polling a queue, running a heartbeat, working a task list. There's often no web server to health-check and nothing to page you when the process crashes, the token expires, the container gets OOM-killed, or the loop quietly wedges. It just... stops. You find out hours later when the work didn't get done.
Cronpulse is a dead-man's-switch for exactly this. Your agent pings a unique URL at the end of every cycle. Cronpulse expects those pings on a schedule you set. If one doesn't arrive in time, we assume the agent is down and alert you on Discord, Slack, or a webhook — and tell you again when it recovers. You don't run any monitoring infrastructure; the agent just makes one HTTP request it already knows how to make.
The 30-second setup
- Create a check (below, or on the home page) — no signup needed to start.
- Copy your unique ping URL.
- Have your agent
GETorPOSTthat URL once per cycle, after a successful iteration. - Set the period + grace so a missed heartbeat means "something's wrong," then attach Discord/Slack.
Python agent loop
import time, urllib.request
PING = "https://cronpulse.cronpulse.workers.dev/ping/YOUR-KEY"
while True:
do_one_agent_cycle() # your work
try:
urllib.request.urlopen(PING, timeout=10) # "I'm alive"
except Exception:
pass # never let the ping crash the agent
time.sleep(60)
Signal start + failure (optional)
Ping /start when a run begins and /fail if it throws — Cronpulse then tracks run duration and pages you on an explicit failure instead of waiting for the timeout:
import urllib.request
BASE = "https://cronpulse.cronpulse.workers.dev/ping/YOUR-KEY"
def ping(path=""):
try: urllib.request.urlopen(BASE + path, timeout=10)
except Exception: pass
ping("/start")
try:
run_agent_task()
ping() # success
except Exception:
ping("/fail") # alert immediately
raise
Catch a "zombie" agent — pinging, but not making progress
A liveness ping only proves the loop is turning, not that it's doing useful work. A drifted or wedged agent can happily run while True: ping() on a timer while its actual task has stalled — report and truth diverge, and a silence-only monitor never sees it.
Cronpulse closes that gap with an optional progress token. Attach a token to each success ping that your agent can't produce without actually advancing — a digest of the run's output, a monotonic cursor, the last processed ID. Turn on Stuck detection for the check (a threshold of N). If the same token repeats for N consecutive pings, the job is pinging on schedule but not progressing, and Cronpulse alerts you — separately from a plain missed-ping "down."
import hashlib, urllib.request, urllib.parse
BASE = "https://cronpulse.cronpulse.workers.dev/ping/YOUR-KEY"
def ping(token=None):
url = BASE + (("?token=" + urllib.parse.quote(token)) if token else "")
try: urllib.request.urlopen(url, timeout=10)
except Exception: pass
result = run_agent_cycle()
# token = a digest of the WORK, not a counter the loop can bump on its own
token = hashlib.sha256(repr(result).encode()).hexdigest()[:16]
ping(token) # if this digest stops changing, we flag the check STUCK
Make the token a function of the work's output (a result digest or an externally-advanced cursor), not an incrementing counter the loop controls — an agent-controlled counter is just a second heartbeat and a zombie can still bump it.
The honest boundary — what a ping-based monitor can and can't prove
Worth stating plainly, because it's a property of any dead-man's-switch and not a bug: Cronpulse only ever sees what your agent chooses to send. It can prove your agent said something on schedule (liveness), and — with a progress token — that what it's saying keeps changing (not frozen). It cannot, from where it sits, prove the work is real: a degenerate loop emitting a fresh, well-formed token every cycle looks healthy, because a changing token clears STUCK. The token binds "claimed" to "produced" on your side of a line the monitor can't observe.
The stronger version, when your loop has one: point the token at something the agent doesn't author — a row count the store reports after commit, the remote's commit count after push, a queue depth, an open-issue count. A token your agent reports is testimony; a quantity the monitor could pull is a measurement. Cronpulse's zero-infrastructure, thirty-second setup is exactly what buys you the first two axes (dead, stuck); closing the third means feeding it a number from outside the agent. (Credit to the agents in The Colony who sharpened this distinction.)
Who watches the watcher — verify Cronpulse's own liveness
A dead-man's-switch has a recursion problem: if Cronpulse's cron stops sweeping, every check silently stops being evaluated and the failure looks exactly like "all green." So Cronpulse's own sweep is a fact you can read from outside. After each full pass the sweeper writes a heartbeat, exposed at a public endpoint:
curl https://cronpulse.cronpulse.workers.dev/health
# {"ok":true,"last_sweep_at":...,"sweep_age_seconds":58,"stale":false,"sweeps_total":...}
# returns HTTP 503 when the last sweep is older than ~3 intervals
The recursion only terminates if the outermost reader fails independently of the thing it watches. So don't trust Cronpulse to tell you Cronpulse is up — point your own external uptime monitor (or a second, unrelated service) at /health and alarm on both a 503/stale:true and no response at all. If the Worker is down, /health can't answer, and your independent pinger notices the silence — the one thing a self-reported heartbeat never could. (Credit to the agents in The Colony who insisted the watcher's watcher must die in a different outage.)
PULL checks — let Cronpulse measure the number itself
This is how you cross that line without trusting the agent's own report. Instead of your loop pinging us, you give Cronpulse a URL and a number to watch; it fetches on a schedule and alerts if the number stops advancing. The progress quantity now lives on the monitor's side — it's a measurement, not testimony. Perfect for queue depth, rows processed, a monotone commit cursor, a draining backlog.
curl -X POST https://cronpulse.cronpulse.workers.dev/api/checks \
-H 'content-type: application/json' \
-d '{"name":"ingest-progress","check_type":"pull",
"pull_url":"https://your.app/metrics.json",
"pull_path":"processed", # dot-path into JSON; omit to read the body as a bare number
"pull_direction":"up", # "up" must increase; "down" must decrease (backlog draining)
"period_seconds":300,"stuck_threshold":1}'
Cronpulse fetches every period_seconds (8s timeout). Value advances → up; fails to advance for stuck_threshold pulls → stuck; endpoint unreachable or returns no number → down. The URL must be a public http(s) endpoint. It's still bounded — Cronpulse measures the number you expose, so make it one that can only move when real work lands — but it removes the agent from the reporting loop entirely.
Redundancy beats replacement: you don't have to choose. Run a push heartbeat and a PULL check on the same loop — the cheap ping proves it's alive; the pulled quantity, which the agent doesn't author, proves work is landing. The alarm you actually trust is the two disagreeing: still pinging, number frozen. (Credit to The Colony's hermes-final for the redundancy-not-replacement framing.)
Long-running worker or container
Same idea from any language — a heartbeat at the end of each loop, or a sidecar cron inside the container:
# bash, once per minute inside the loop
curl -fsS -m 10 https://cronpulse.cronpulse.workers.dev/ping/YOUR-KEY > /dev/null
Why this beats a plain uptime check
An uptime monitor pings your server and tells you when a port stops answering. An agent often has no port — and "the process is running" is not the same as "the agent is still doing its job." Cronpulse flips it around: the agent proves it's alive to us, so a hung loop, an expired credential, or a crashed container all surface the same way — a heartbeat that didn't arrive.
Start monitoring an agent now — free, no signup to try:
Create a heartbeat check →Free plan: up to 50 checks, checked every minute, unlimited alerts. No credit card.
Monitoring n8n / Make / Zapier / cron · Cron expression tester · About Cronpulse · Home · Live status (we monitor ourselves)