Quickstart

Go from nothing to a monitor that pages you, in about five minutes, ending with a failure you trigger yourself to prove it works.

Here is the finished result. Two lines around the job you already run:

bash
API="https://api.drumbeats.io/v1/ping/<monitor-id>"

curl -sf "$API/start"
/usr/bin/backup.sh && EVENT=success || EVENT=failure
curl -sf "$API/$EVENT"
API="https://api.drumbeats.io/v1/ping/<monitor-id>"

curl -sf "$API/start"
/usr/bin/backup.sh && EVENT=success || EVENT=failure
curl -sf "$API/$EVENT"

The five steps below get you the <monitor-id>, a channel to alert, and a tested round-trip. Budget five minutes.

Step 1: Create the monitor#

Open the dashboard and click New Monitor. Pick the type that matches the work:

TypeUse it forNeeds
CronJobs on a fixed schedule, such as 0 2 * nightly backupsA cron expression and a timezone
HeartbeatWorker loops that tick every N minutesAn interval such as 5m
Event-drivenQueue workers, webhook handlers, one-off scriptsNothing. It reacts to the runs you report
UptimePublic HTTP endpoints. Drumbeats polls these, you send no pingsA URL and a check interval

Name the monitor, set the schedule and grace period, then save.

Drumbeats new cron monitor form showing the cron expression input, grace period selector, timezone dropdown, and name field
Drumbeats new cron monitor form showing the cron expression input, grace period selector, timezone dropdown, and name field

Step 2: Copy the ping URL#

Open the monitor and copy the ping URL from the URLs panel:

plaintext
https://api.drumbeats.io/v1/ping/<your-monitor-id>
https://api.drumbeats.io/v1/ping/<your-monitor-id>

Copy it rather than retyping. A single wrong character in the UUID sends your pings into a 404 and the monitor stays silent until it misses.

That URL is the credential. There is no API key on the ping path, so anyone holding the URL can ping the monitor. Treat it like a secret and keep it in your environment config, not in a public repo.

Step 3: Ping from your job#

Send start when the job begins and success or failure when it ends. Drumbeats pairs the two using run_id, any string that is unique per execution, and stores both on one run record.

Bash
RUN_ID="job-$(date +%s)"
API="https://api.drumbeats.io/v1/ping/<monitor-id>"

curl -sf "$API/start?run_id=$RUN_ID"

if /usr/bin/backup.sh; then
  curl -sf "$API/success?run_id=$RUN_ID"
else
  curl -sf "$API/failure?run_id=$RUN_ID"
fi
RUN_ID="job-$(date +%s)"
API="https://api.drumbeats.io/v1/ping/<monitor-id>"

curl -sf "$API/start?run_id=$RUN_ID"

if /usr/bin/backup.sh; then
  curl -sf "$API/success?run_id=$RUN_ID"
else
  curl -sf "$API/failure?run_id=$RUN_ID"
fi
Python
import uuid, requests

API = "https://api.drumbeats.io/v1/ping/<monitor-id>"
run_id = f"job-{uuid.uuid4()}"

requests.get(f"{API}/start", params={"run_id": run_id}, timeout=5)
try:
    run_my_job()
    requests.get(f"{API}/success", params={"run_id": run_id}, timeout=5)
except Exception as exc:
    requests.post(
        f"{API}/failure",
        params={"run_id": run_id},
        json={"payload": str(exc)},
        timeout=5,
    )
    raise
import uuid, requests

API = "https://api.drumbeats.io/v1/ping/<monitor-id>"
run_id = f"job-{uuid.uuid4()}"

requests.get(f"{API}/start", params={"run_id": run_id}, timeout=5)
try:
    run_my_job()
    requests.get(f"{API}/success", params={"run_id": run_id}, timeout=5)
except Exception as exc:
    requests.post(
        f"{API}/failure",
        params={"run_id": run_id},
        json={"payload": str(exc)},
        timeout=5,
    )
    raise
Node.js
import { randomUUID } from "node:crypto";

const API = "https://api.drumbeats.io/v1/ping/<monitor-id>";
const runId = `job-${randomUUID()}`;

await fetch(`${API}/start?run_id=${runId}`);
try {
  await runMyJob();
  await fetch(`${API}/success?run_id=${runId}`);
} catch (err) {
  await fetch(`${API}/failure?run_id=${runId}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ payload: String(err) }),
  });
  throw err;
}
import { randomUUID } from "node:crypto";

const API = "https://api.drumbeats.io/v1/ping/<monitor-id>";
const runId = `job-${randomUUID()}`;

await fetch(`${API}/start?run_id=${runId}`);
try {
  await runMyJob();
  await fetch(`${API}/success?run_id=${runId}`);
} catch (err) {
  await fetch(`${API}/failure?run_id=${runId}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ payload: String(err) }),
  });
  throw err;
}

The ping API takes both GET and POST. Use POST when you want to attach a payload such as an error message or the tail of stdout. See payloads.

Step 4: Route the alert to a channel#

A monitor with no notification group cannot page anyone. Three parts, in order:

  1. Open Settings → Notification Channels and connect one channel. Email, Slack, Microsoft Teams, Telegram, Discord, PagerDuty, or a webhook. Browser push is a per-device toggle on the same screen.
  2. Open Notification Groups, create a group, and add the channel. One group can hold several channels of different types. They all fire in parallel.
  3. Open the monitor's Notifications tab and assign the group.

Per-channel setup lives in notification channels. The thresholds that decide when a failure actually pages you live in alert logic.

Step 5: Break it on purpose#

Do not wait for a real outage to find out the wiring is wrong. Fire a failure by hand:

The alert should reach your channel within seconds. It names the monitor, the incident event, and links to the incident. Now resolve it:

You should get a recovery message carrying the outage duration, and the monitor should read UP again.

If no alert arrived#

Work down this list in order. Each step rules out one layer.

CheckWhat it tells you
The monitor's Incidents tab shows the incidentThe ping landed. The problem is delivery, not wiring
The Test button on the channel deliversThe channel is healthy. The problem is the group assignment
The group is assigned on the monitor's Notifications tabA monitor with no group never pages
For email, the recipient confirmed the verification mailUnverified external addresses are skipped
For browser push, both browser and OS permission are grantedPush needs both, and Do Not Disturb suppresses it entirely

Browser push has its own failure modes. Push troubleshooting covers all of them.

Next#

Pick the right monitor type for each job in monitor types, then harden the wiring with retries and timeouts in production hardening. Beats and usage explains what each ping costs.