DOCS
CronBB is a dead-simple cron job heartbeat monitor. One curl in your crontab. If the job stops running, you get a webhook alert within 2 minutes. No agents, no SDK, no dashboard to babysit.
INTRODUCTION
Cron jobs fail silently all the time — a script gets a bad path after a server update, the cron daemon dies after a reboot, a dependency breaks. You don't notice for days. CronBB solves this with the dead man's switch pattern: your job sends a ping after every successful run. If the ping stops arriving on schedule, CronBB fires a webhook to your Slack or Discord.
CronBB runs entirely on Cloudflare Workers + KV — zero servers to maintain, zero databases to back up. Everything fits in the free tier.
QUICK START
Get up and running in 60 seconds.
1. Create an account
# POST your email, get an API key back curl -X POST https://cronbb.vzbb.site/api/signup \ -H "Content-Type: application/json" \ -d '{"email":"[email protected]"}' # Response: {"api_key": "cronbb_k8x2m..."}
2. Create a monitor
curl -X POST https://cronbb.vzbb.site/api/monitors \ -H "Authorization: Bearer cronbb_k8x2m..." \ -H "Content-Type: application/json" \ -d '{"name":"db-backup","interval_minutes":60,"webhook_url":"https://hooks.slack.com/..."}' # Response: {"monitor": {"id":"a3f8x1","ping_url":"https://cronbb.vzbb.site/ping/a3f8x1", ...}}
3. Add ping to crontab
# Append && curl ... to your existing cron command 0 * * * * /usr/local/bin/backup.sh && curl -fsS --retry 3 https://cronbb.vzbb.site/ping/a3f8x1
&& operator is critical — it means the curl only runs if the script succeeds (exit code 0). If your script fails, the ping never fires, and CronBB alerts you.
Or use the CLI
npm install -g cronbb-monitor cronbb signup [email protected] cronbb create db-backup 60 --webhook https://hooks.slack.com/... cronbb list
HOW IT WORKS
A Cloudflare Worker cron trigger runs every 2 minutes and checks all monitors. For each monitor, it computes: has a ping arrived within the expected window (interval + grace period)?
| State | Condition | Action |
|---|---|---|
up | Ping arrived within interval + grace | Nothing |
waiting | No ping received yet (new monitor) | Nothing |
down | Ping overdue | Fire webhook alert |
recovered | Was down, ping arrived again | Fire recovery webhook |
paused | Manually paused | No alerts fired |
Storage is Cloudflare KV — a globally distributed key-value store. No relational database, no persistent server, no TCP connections to maintain.
AUTHENTICATION
All authenticated endpoints require a Bearer token in the Authorization header. You get your API key when you sign up.
Authorization: Bearer cronbb_k8x2m...
SIGNUP / LOGIN
Creates a new account and returns an API key. Idempotent — calling with the same email returns the existing key.
Request body
| Field | Type | Description |
|---|---|---|
emailrequired | string | Valid email address. Used for account identification only. |
Response
{
"api_key": "cronbb_k8x2m...",
"message": "Account created."
}Retrieves an existing API key by email. Returns 404 if the account doesn't exist.
{ "email": "[email protected]" }Returns account info, plan details, and current usage limits.
{
"email": "[email protected]",
"plan": "free",
"limits": { "monitors": 3, "interval_min": 2 },
"created_at": 1700000000000
}MONITORS
Creates a new monitor. Returns the full monitor object including the ping URL.
Request body
| Field | Type | Description |
|---|---|---|
namerequired | string | Identifier for the job (e.g., "db-backup"). |
interval_minutesrequired | number | Expected time between pings, in minutes. Range: 1–43200. |
webhook_urloptional | string | URL to POST alert payloads to. Supports Slack, Discord, or any https URL. |
grace_minutesoptional | number | Extra buffer before alerting. Useful for jobs that sometimes run a bit late. Default: 0. |
tagsoptional | string[] | Up to 5 labels for organization (e.g., ["production","db"]). |
{
"monitor": {
"id": "a3f8x1",
"name": "db-backup",
"status": "waiting",
"interval_minutes": 60,
"grace_minutes": 0,
"ping_url": "https://cronbb.vzbb.site/ping/a3f8x1",
"ping_count": 0,
"last_ping": null,
"paused": false,
"webhook_url": "https://hooks.slack.com/...",
"created_at": 1700000000000
}
}Returns all monitors for the current account.
{
"monitors": [ /* array of monitor objects */ ],
"count": 2,
"limit": 3,
"plan": "free"
}Returns a single monitor with the last 20 ping events.
{
"monitor": { /* monitor object */ },
"history": [
{ "ts": 1700000000000, "recovered": false },
/* ... up to 20 entries, newest first */
]
}Updates one or more fields of a monitor without recreating it. All fields optional.
| Field | Type |
|---|---|
name | string |
interval_minutes | number (1–43200) |
webhook_url | string |
grace_minutes | number (0–60) |
tags | string[] |
Permanently removes the monitor and all its history.
PING
Sends a heartbeat for the monitor. Call this at the end of each successful cron run. No authentication required — the URL itself is the secret.
# Append to existing cron command 0 3 * * * /usr/bin/backup.sh && curl -fsS --retry 3 https://cronbb.vzbb.site/ping/YOUR_ID # --retry 3 ensures transient network issues don't false-alarm # -f makes curl fail on non-2xx (so you know if the API is down) # -s silences progress output # -S shows errors even with -s
Response
{
"ok": true,
"status": "up",
"ping_count": 42,
"recovered": false,
"next_expected": "2026-03-01T04:00:00.000Z"
}PAUSE / RESUME
Use pause during deployments or maintenance windows to prevent false alarms.
# Pause before deployment curl -X POST https://cronbb.vzbb.site/api/monitors/a3f8x1/pause \ -H "Authorization: Bearer cronbb_k8x2m..." # Resume after deployment completes curl -X POST https://cronbb.vzbb.site/api/monitors/a3f8x1/resume \ -H "Authorization: Bearer cronbb_k8x2m..."
CLI — INSTALLATION
npm install -g cronbb-monitor
# Verify
cronbb --versionRequires Node.js ≥ 14. The CLI stores your API key in ~/.cronbb/config.json.
CLI — COMMANDS
| Command | Description |
|---|---|
cronbb signup <email> | Create account, save API key locally. |
cronbb login <email> | Retrieve and save existing API key. |
cronbb whoami | Show account info and plan limits. |
cronbb create <name> <mins> | Create a monitor. |
cronbb list [--json] | List all monitors with status. |
cronbb status <id|name> | Detailed view + ping history. |
cronbb edit <id> [flags] | Update monitor fields. |
cronbb pause <id> | Pause a monitor. |
cronbb resume <id> | Resume a paused monitor. |
cronbb rm <id> | Delete a monitor. |
cronbb ping <id> | Manually send a heartbeat. |
cronbb crontab <id> | Print ready-to-paste crontab snippets. |
cronbb health | Check API status. |
Create flags
cronbb create db-backup 60 \ --webhook https://hooks.slack.com/... \ --grace 10 \ --tags production,db
WEBHOOKS — SLACK
CronBB auto-detects Slack URLs and sends rich Block Kit messages with color, monitor name, and context.
Step-by-step
- Go to api.slack.com/apps and click Create New App → From scratch
- Give it a name (e.g. "CronBB") and pick your workspace → Create App
- In the left sidebar click Incoming Webhooks → toggle it On
- Scroll down → click Add New Webhook to Workspace
- Choose the channel that should receive alerts (e.g.
#alerts) → Allow - Copy the webhook URL — it looks like:
https://hooks.slack.com/services/T.../B.../xxx - Paste it as
webhook_urlwhen creating or editing a monitor
# At create time: cronbb create db-backup 60 --webhook https://hooks.slack.com/services/T.../B.../xxx # Or add to existing monitor: cronbb edit YOUR_MONITOR_ID --webhook https://hooks.slack.com/services/T.../B.../xxx
What the Slack message looks like
Last ping was 73m ago (expected every 60m).
ID:
a3f8x1 · Interval: 60m · CronBB
Recovered after being down for 13m.
ID:
a3f8x1 · Interval: 60m · CronBB
WEBHOOKS — DISCORD
CronBB auto-detects Discord URLs and sends rich Embeds with color-coded status (🔴 red for down, 🟢 green for recovery).
Step-by-step
- Open Discord, right-click the channel you want alerts in → Edit Channel
- Go to Integrations → Webhooks → New Webhook
- Give it a name (e.g. "CronBB") and optionally set an avatar → Save Changes
- Click Copy Webhook URL — it looks like:
https://discord.com/api/webhooks/123456.../xxx - Paste it as
webhook_urlwhen creating or editing a monitor
# At create time: cronbb create db-backup 60 --webhook https://discord.com/api/webhooks/123.../xxx # Or add to existing monitor: cronbb edit YOUR_MONITOR_ID --webhook https://discord.com/api/webhooks/123.../xxx
What the Discord embed looks like
Last ping was 73m ago (expected every 60m).
a3f8x1
Interval: 60m
Recovered after being down for 13m.
Tip: use a dedicated channel
#cronbb-alerts channel in your server so alerts don't mix with conversation.
You can also mention a role by editing the channel topic to include @oncall.
WEBHOOKS — GENERIC
Any https:// URL works. CronBB will POST a JSON payload.
# 1. Visit https://webhook.site and copy your unique URL # 2. Use it as webhook_url when creating a monitor cronbb create test-job 1 --webhook https://webhook.site/YOUR-UUID
WEBHOOK PAYLOAD REFERENCE
Down alert
{
"status": "down",
"monitor_id": "a3f8x1",
"monitor_name": "db-backup",
"interval_minutes": 60,
"message": "Last ping was 73m ago (expected every 60m).",
"down_since": 1700000000000,
"timestamp": 1700004380000
}Recovery alert
{
"status": "recovered",
"monitor_id": "a3f8x1",
"monitor_name": "db-backup",
"interval_minutes": 60,
"message": "Recovered after being down for 13m.",
"timestamp": 1700005200000
}GRACE PERIODS
Some jobs run a few minutes late by design (heavy DB queries, slow network, etc.). A grace period adds a buffer before CronBB considers a monitor down.
now > last_ping + interval_minutes + grace_minutes
# Job runs every hour but sometimes takes up to 10 extra minutes cronbb create slow-report 60 --grace 10 # Alert will fire only if ping is more than 70 minutes overdue
DEPLOYING YOUR INSTANCE
CronBB runs on Cloudflare Workers. Deploy your own instance in under 5 minutes.
Requirements
- A free Cloudflare account
- Node.js ≥ 18 and npm
- Wrangler CLI:
npm install -g wrangler
1 — Authenticate Wrangler
wrangler login
2 — Create a KV namespace
wrangler kv:namespace create KV
# Copy the returned id = "..." into wrangler.toml → [[kv_namespaces]]3 — Add Stripe secrets
wrangler secret put STRIPE_SECRET_KEY # paste: sk_live_... wrangler secret put STRIPE_PRO_PRICE_ID # paste: price_... wrangler secret put STRIPE_WEBHOOK_SECRET # paste: whsec_...
4 — Deploy
wrangler deploy # Live at: https://cronbb.YOUR_SUBDOMAIN.workers.dev # Custom domain: add a CNAME in Cloudflare DNS → Workers route
wrangler.toml reference
name = "cronbb" main = "src/worker.js" compatibility_date = "2024-09-23" # Runs every minute — plan logic (free: min 2m, pro: min 1m) is enforced in code [triggers] crons = ["* * * * *"] [[kv_namespaces]] binding = "KV" id = "YOUR_KV_NAMESPACE_ID"
ERROR CODES
| Status | Code / message | Meaning |
|---|---|---|
400 | Various validation messages | Invalid request body (missing field, wrong type, out-of-range value). |
401 | "Authentication required" | Missing or invalid Authorization header. |
403 | "Monitor limit reached" | You've hit your plan's monitor limit. Upgrade or delete an existing monitor. |
404 | "Monitor not found" | Monitor ID doesn't exist or belongs to another account. |
429 | "Too many requests" | Rate limit hit on signup/login. Try again in a minute. |
All error responses have the shape: {"error": "message here"}