Skip to content

Alert bridge: self-hosted Cloudflare Worker

The alerts-bridge is an open-source Cloudflare Worker that translates Monitive webhook events into push notifications. Deploy it once to your own Cloudflare account and every monitor.down, monitor.up, and monitor.flapping event reaches your phone, Slack workspace, or Discord server — via Pushover, ntfy, Slack, Discord, or Telegram.

You own the Worker. Monitive sends the webhook; the bridge forwards it to your provider. No third-party relay, no vendor lock-in.

New to this? The Reliable alerts guide covers why Do Not Disturb makes browser push and email unreliable, and lets you pick a provider and runtime. This page is the full deploy reference.

  • A Cloudflare account (free tier is enough)
  • Node 20 or newer
  • A UptimeMonitoring.com account with at least one monitor
  • A provider account — this walkthrough uses Pushover
Terminal window
git clone https://github.com/uptimemonitoring/alerts-bridge
cd alerts-bridge
npm install

Log in to Cloudflare, then deploy:

Terminal window
npx wrangler login
npm run deploy

Wrangler prints the Worker URL when the deploy succeeds:

Published alerts-bridge (0.00 sec)
https://alerts-bridge.<your-subdomain>.workers.dev

Note that URL — you register it with your monitor next.

  1. Go to Recipients in app.uptimemonitoring.com and click Create recipient.
  2. Choose Webhook, paste the *.workers.dev URL from the deploy step, and click Create recipient.
  3. Copy Your recipient secret — shown once — you’ll paste it as MONITOR_WEBHOOK_SECRETS in the next step.
  4. Open your monitor’s Recipients section, check the new recipient, and save.

(API equivalent: POST /api/v1/recipients with type: "webhook" returns the secret once in the creation response; attach the recipient to a monitor with PUT /api/v1/monitors/<id>/recipients — see Choose who gets alerts.) The bridge verifies the X-UptimeMonitoring-Signature header against this secret on every request — see Webhooks for the signature contract.

Configure the Worker secrets with npx wrangler secret put. Wrangler prompts you for each value; nothing is written to disk.

Terminal window
# Which provider to use
npx wrangler secret put PROVIDER
# → pushover
# Pushover application token (from pushover.net → Your Applications)
# Tip: name the Pushover application "UptimeMonitoring" — that name is what
# shows as the source on each push notification.
npx wrangler secret put PUSHOVER_TOKEN
# Pushover user key (from pushover.net → Your User Key)
npx wrangler secret put PUSHOVER_USER
# The whs_live_… signing secret you copied from the recipient's creation response above
npx wrangler secret put MONITOR_WEBHOOK_SECRETS

Watch the Worker logs in one terminal:

Terminal window
npx wrangler tail

Then trigger a real event: point the monitor at a URL that returns 5xx (or stop the service it watches) so Monitive fires a monitor.down webhook, then restore it to fire monitor.up. Confirm each notification arrives on your device, and watch wrangler tail for the incoming request and the provider’s response.

The webhook recipient’s Send test button (on its row in Recipients) is the fastest way to fire a sample delivery without affecting the monitor’s real state.

Set PROVIDER to one of the values in the first column and supply the corresponding secrets:

PROVIDER value Required secrets Notes
pushover PUSHOVER_TOKEN, PUSHOVER_USER Pushover app token + user key
ntfy NTFY_TOPIC (+ optional NTFY_URL, NTFY_TOKEN) NTFY_URL defaults to https://ntfy.sh; NTFY_TOKEN only for protected topics
slack SLACK_WEBHOOK_URL Slack incoming webhook URL
discord DISCORD_WEBHOOK_URL Discord webhook URL
telegram TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID Bot token + target chat ID

401 from the Worker The signature verification failed. Check that the MONITOR_WEBHOOK_SECRETS value in the Worker matches the recipient secret you copied at creation time exactly — trailing whitespace or a copy-paste error will cause every request to be rejected. UptimeMonitoring doesn’t show that secret again, and there’s no rotate endpoint — don’t just delete the recipient and create a new one: that leaves every monitor attached to it unalerted until you reattach them by hand, and deleting a recipient also discards whatever of its deliveries are still queued (pending/retrying) at that moment, outright. Instead, follow one of the two procedures on the Webhooks page, picking based on what the Worker can verify at once:

  • Dual-secret overlap, if your deployed version of the bridge checks multiple candidate secrets: create the replacement, install its secret in the Worker alongside the still-valid original, attach it to every monitor the original was on with each monitor’s saved down_delay_s, verify delivery with Send test or a controlled down/up transition, then wait out the original’s queued deliveries and delete it.
  • Downtime window, if the Worker can only hold one secret: create the replacement, take the Worker offline, swap old for new on every affected monitor (one PUT per monitor), install the new secret and bring the Worker back online, verify delivery, then wait out and delete the original the same way.

Either path, a clean delivery-log scan only proves nothing was queued as of that read — it can’t prove nothing is queued the instant you call DELETE, since a transition firing in that gap still queues a delivery that deletion discards outright, not delivers late. The Webhooks page’s procedures show how to run that scan; for margin against that gap, keep the original recipient around for at least the longest retry delay (2 hours) past a clean scan before deleting it.

No push notification Signature passed but the provider rejected the request. Double-check PUSHOVER_TOKEN and PUSHOVER_USER (or the equivalent for your provider). Run npx wrangler tail while triggering a test event to see the provider’s error response.

413 Request Entity Too Large The webhook payload exceeded the Worker’s request size limit. This should not happen with standard Monitive payloads — if it does, open an issue in the alerts-bridge repository.