How it works
1. Create a check. Give it a name and tell us how often you expect it to run — say, every 1 day with a 1-hour grace period.
2. Ping when your job runs. Add a request to the check's unique URL at the end of your job:
3. Relax. Each ping resets the clock. If a ping doesn't arrive within the period + grace, the check goes DOWN and we email you. When pings resume, you get an UP recovery notice.
Alerts your way. Every check emails you by default. Paste a Slack, Discord, or generic webhook URL on any check and DOWN / FAIL / recovery alerts post there too — no config, works out of the box.
Works with anything that can make an HTTP request
cron, systemd timers, Kubernetes CronJobs, GitHub Actions, Windows Task Scheduler, backup tools, ETL pipelines, AI agent loops — if it can run curl or wget or open a URL, it can ping Cronping.
Three signals, same URL:
- Hit /ping/<id> when the job succeeds — resets the clock.
- Hit /ping/<id>/start when it begins — we time the run and show its duration.
- Hit /ping/<id>/fail if it errors — go DOWN and alert immediately, no waiting for the window.
POST any of them with a short body (exit code, row count, error message) and it's attached to the event.
⬇️ Can't ping? Let Cronping pull instead
Some things you want to watch can't call a URL — a public health endpoint, a status page, a third-party API. Create a pull check with a pull_url and Cronping fetches it on your schedule, going DOWN if it returns an error, times out, or is unreachable. Add pull_contains to also require specific text in the response. Push (you ping us) and pull (we fetch you) — both directions, one free tool.
🟠 Catch zombie runs, not just silence
A missed ping catches a job that stopped. But an agent loop or worker can keep pinging on schedule while its real work has stalled — green dashboard, stale output. Add a progress token to your success ping: a short digest of the run's actual output (last-processed id, row count, a content hash). If the same token repeats for stuck_after consecutive runs (default 3), the check flips to STUCK and alerts — separate from a missed-ping DOWN.
When the token changes, the check recovers to UP automatically. Pass a real digest of the output — not a self-bumped counter — so a stuck job can't fake progress.
🔌 Give your AI agent a dead-man's-switch over MCP
Cronping is a remote MCP server, so an AI agent (Claude, OpenCode, Cursor, or any MCP client) can wire up its own monitoring — no human, no dashboard. Point your client at the endpoint below and the agent gets seven tools: create_heartbeat (returns a ping URL), ping_heartbeat (signal success/start/fail each loop), get_heartbeat_status, set_heartbeat_alert (attach an email or webhook), and import_crontab (arm a dead-man's-switch for an entire crontab in one call), plus delete_heartbeat and pause_heartbeat for full lifecycle control.
Drop it in your MCP client config (Cursor, Windsurf, OpenCode — any client that speaks streamable-HTTP):
Stdio-only client (e.g. Claude Desktop)? Bridge it with mcp-remote:
A long-running agent creates a heartbeat once, pings it at the top of every loop, and you get alerted the moment the loop dies silently. It's also listed on the public MCP registry as heartbeat-monitor. See framework snippets (LangGraph, CrewAI, plain loops) on the AI-agent monitoring page.
Copy-paste examples
Crontab — ping after a nightly backup:
GitHub Actions — confirm a scheduled workflow ran:
Python — wrap a job with success/fail signals:
systemd timer — add to the service's ExecStartPost:
🛡️ Is the alert path separate from the thing it's watching?
Yes — and that's the whole point. The classic watchdog failure is co-location: a monitor running on the same box, container, or account as the job dies in the same crash, OOM, or outage, so nobody gets paged. Cronping is off your infrastructure entirely. The clock, the miss-detection, and the alert send all run on Cronping's side, independent of wherever your job runs. When your host, network, or process disappears, the ping simply stops arriving — and the absence is exactly what triggers the alert. There's no agent on your box to die with it.
For an extra layer, point a second check's pull_url at your primary alert channel, or add a webhook alongside email so a single channel outage can't swallow the notice.