Most alerting configs get their first real test during a real outage โ which is exactly when you find out the webhook URL had a typo, the confirmation threshold ate the alert, or the recovery notification never fires. The "send test notification" button in Uptime Kuma, UptimeRobot and friends doesn't help much: it tests the channel, not the pipeline. Detection, retries-before-alerting, the DOWN message, the RECOVERED message, the timeout path โ none of that runs when you press the button.
What you actually want is a target that fails on your schedule. Here are six drills, all free, no signup, using URLs your monitor can poll right now. They work with anything that checks a URL: Uptime Kuma, UptimeRobot, StatusCake, Pingdom, Checkly, Better Stack, a cron + curl script, or Mockbird's own one-curl monitor.
Point a monitor at Mockbird's always-up demo health endpoint:
https://mockbird.mockbird.workers.dev/m/demo/health
# โ 200 {"status":"ok",...}
And a second one at the same endpoint, forced down:
https://mockbird.mockbird.workers.dev/m/demo/health?mock_status=503
# โ 503 {"error":"simulated 503 error (mock_status)"}
Within one check interval you'll see exactly what a DOWN alert looks like in your channel โ subject line, formatting, what metadata your monitor includes โ without touching anything you run in production. Any status works: ?mock_status=500, =404, =429.
This is the drill the test-notification button can't do. ?mock_seq serves a deterministic status sequence: each request gets the next status in the list, and the last entry sticks.
https://mockbird.mockbird.workers.dev/m/demo/health?mock_seq=503,503,503,200
# check 1 โ 503
# check 2 โ 503
# check 3 โ 503
# check 4 and every one after โ 200
Point a monitor at that URL and it experiences a complete incident: three failed checks, then recovery. In one pass you've verified:
Every response carries x-mockbird-seq: pos/len so you can see where the sequence stands. To run the drill again, hit the URL once yourself with &mock_seq_reset=1 appended โ that request counts as check #1, and your monitor's next polls continue the sequence from #2.
Two scoping notes. On the shared demo project, sequence counters are additionally scoped per client IP โ so if your monitor probes from multiple locations, each location runs its own sequence (a Kuma instance or single-region checker is unaffected). On your own project there's no IP scoping: all probe locations share one counter, meaning "3 failures" is 3 failures total across locations. Either way the endpoint recovers and stays up once the sequence is exhausted. Parallel drills on one URL: isolate them with &mock_seq_key=drill1.
Query-param drills change the URL. For the most honest test โ the monitor polls the exact same URL the whole time, like production โ give it an endpoint whose behaviour you flip server-side. One curl creates a project, one adds a /health route:
curl -s -X POST https://mockbird.mockbird.workers.dev/api/projects \
-H 'content-type: application/json' -d '{"name":"alert drill"}'
# โ {"id":"YOURID","adminKey":"YOURKEY",...} (save both)
curl -s -X POST https://mockbird.mockbird.workers.dev/api/projects/YOURID/routes \
-H 'x-admin-key: YOURKEY' -H 'content-type: application/json' \
-d '{"method":"GET","path":"/health","status":200,"body":{"status":"ok"}}'
Point your monitor at https://mockbird.mockbird.workers.dev/m/YOURID/health โ it's green. Re-posting the same path overwrites the route in place, so starting an outage is one curl:
# take it down
curl -s -X POST https://mockbird.mockbird.workers.dev/api/projects/YOURID/routes \
-H 'x-admin-key: YOURKEY' -H 'content-type: application/json' \
-d '{"method":"GET","path":"/health","status":503,"body":{"status":"unavailable"}}'
# ...watch the DOWN alert arrive, then bring it back
curl -s -X POST https://mockbird.mockbird.workers.dev/api/projects/YOURID/routes \
-H 'x-admin-key: YOURKEY' -H 'content-type: application/json' \
-d '{"method":"GET","path":"/health","status":200,"body":{"status":"ok"}}'
The outage lasts exactly as long as you want โ long enough to check escalation policies, resend intervals ("still down" reminders) and on-call handoff, not just the first ping. Deleting the route instead simulates a different failure class: the monitor gets a 404, which some tools report differently from a 5xx.
Slow-but-alive is its own failure mode, and most monitors report it differently ("down: timeout" vs "down: 503"). Add a slow route, or use ?mock_delay on any URL:
https://mockbird.mockbird.workers.dev/m/demo/health?mock_delay=4500
# โ 200, after 4.5 seconds
Mockbird's delay cap is 5000 ms, so set the monitor's request timeout below that (say 2 s) for the drill. This is the path that exercises your monitor's timeout handling rather than its status-code handling โ worth one deliberate test, because the alert text and the failure reason differ.
Keyword checks exist because servers can return 200 with an error page. Drill that too: keep the route at status 200 but flip the body, using the same overwrite trick from ยง3:
curl -s -X POST https://mockbird.mockbird.workers.dev/api/projects/YOURID/routes \
-H 'x-admin-key: YOURKEY' -H 'content-type: application/json' \
-d '{"method":"GET","path":"/health","status":200,"body":{"status":"degraded"}}'
A keyword monitor watching for "status":"ok" should now alert even though the HTTP status is still 200 โ and a plain HTTP monitor on the same URL should stay green. Running both against one URL is a nice sanity check that you configured the keyword mode you think you did.
?mock_chaos=0.3 fails roughly 30% of requests with a random 5xx. Point a monitor at it for an hour:
https://mockbird.mockbird.workers.dev/m/demo/health?mock_chaos=0.3
You'll find out how your setup behaves under flapping โ whether the confirmation threshold suppresses one-off blips, whether you get an alert storm, whether incidents get merged or you wake up to forty pings. Tune the probability up or down; injected failures carry an x-mockbird-chaos: injected header so you can tell them apart in logs.
Mockbird also runs a one-curl uptime monitor (checks every 30 minutes, webhook on down and on recovery). Which means both halves of an alerting drill fit in two commands: create a /health route as in ยง3, then
curl -X POST https://mockbird.mockbird.workers.dev/api/status/monitor \
-H 'content-type: application/json' \
-d '{"url":"https://mockbird.mockbird.workers.dev/m/YOURID/health",
"notify":"https://hooks.slack.com/services/T000/B000/XXXX"}'
Flip the route to 503, and within a check interval your webhook gets the DOWN ping; flip it back for the RECOVERED ping. Target and monitor both disposable, both free, nothing installed.
| Approach | What it tests | Limits |
|---|---|---|
| Monitor's "test notification" button | The channel only | Skips detection, thresholds, recovery, timeouts |
| httpstat.us/503 | DOWN detection | Stateless โ one URL can't go down then recover; the service itself has been down for weeks at a time |
| httpbin.org/status/503 | DOWN detection | Stateless too, and has its own outage history |
| Stopping a real service you run | Everything | Needs prod (or prod-like) access; risky, slow to arrange, tests more than alerting at once |
| Mockbird drills (this page) | Detection, thresholds, DOWN + RECOVERED, timeout path, keyword path, flapping | 5s max delay; free-tier caps (10k req/day per project, 50k/day on demo) โ far above any polling monitor's needs |
This page is written by the Mockbird maker โ bias disclosed. Every command above was run against the live service before publishing (statuses, sequence positions, overwrite behaviour and the 4.5s delay were all verified with curl on Sep 23, 2026). A monitor polling every 30 s spends ~2,880 requests/day โ well inside the free cap. Sequence counters idle for 24h are pruned, which never affects a live drill; a monitor on a mock_seq URL that's been recovered for a day simply starts a fresh sequence, so use ยง3's flip-on-command route for anything long-running.
Related: free uptime monitoring with one curl ยท Uptime Kuma alternative ยท cron heartbeat monitoring ยท testing loading & error states ยท httpstat.us alternative. Full API reference in the docs.
/health custom route from the UI, point your monitor at it, and flip its status whenever you want a drill. No signup.