Get downtime alerts in Discord
- integrations
- webhook
- discord
Can I point a YoPingMe webhook straight at Discord?
No, and this page does not pretend otherwise. YoPingMe's webhook channel
sends one fixed JSON payload (documented in full below). Discord's webhook
endpoint expects its own shape - a content string or embeds array -
and returns 400 for anything else. The bridge is a relay of about 30
lines that runs free on Cloudflare Workers: it reshapes the payload into
a Discord embed and forwards it.
You need: a Discord server where you can manage webhooks, a free Cloudflare account, and a YoPingMe monitor.
Step 1: create the Discord webhook
In Discord, open your server settings, then Integrations, then Webhooks. Create one, pick the channel alerts should land in, and copy the webhook URL. Treat that URL as a secret - anyone who has it can post to your channel.
Step 2: create the relay Worker
In the Cloudflare dashboard, create a new Worker (Workers and Pages, then Create). Replace its code with:
export default {
async fetch(request, env) {
if (request.method !== 'POST') {
return new Response('expected POST', { status: 405 });
}
const body = await request.text();
const alert = JSON.parse(body);
const look = {
down: { title: 'Down', color: 0xed4245 },
up: { title: 'Recovered', color: 0x57f287 },
still_down: { title: 'Still down', color: 0xfee75c },
}[alert.event] ?? { title: alert.event, color: 0x5865f2 };
const discord = await fetch(env.DISCORD_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
embeds: [{
title: `${look.title}: ${alert.monitor}`,
description: alert.cause ?? '',
color: look.color,
fields: [{ name: 'Target', value: alert.target }],
timestamp: alert.sequence_at,
}],
}),
});
// A non-2xx answer makes YoPingMe retry, so a Discord hiccup is
// covered by the same retry schedule as everything else.
return new Response(null, { status: discord.ok ? 200 : 502 });
},
};
This recipe is written for Cloudflare Workers for one reason: the free tier. Cloudflare's published free-tier limit, checked 2026-08-07, is 100,000 requests a day, and your alerts will not dent it.
Step 3: set the Worker's secret
The Worker needs one value. In the Worker's settings, add it as a secret (not a plaintext variable):
DISCORD_WEBHOOK_URL: the URL from step 1.
With the wrangler CLI instead: wrangler secret put DISCORD_WEBHOOK_URL.
Step 4: add the channel in YoPingMe
In the YoPingMe dashboard, add an alert channel of type webhook. The URL is your Worker's URL. Attach the channel to your monitors.
Keep the Worker URL private
There is no shared secret between YoPingMe and the relay. The Worker's
URL is the credential - exactly like the Discord webhook URL it wraps.
Anyone who has either URL can post an embed to that channel, so treat
the Worker URL the same way you already treat the Discord one: do not
commit it, paste it into a public issue, or share it outside your team.
If it ever leaks, create a new Worker (a new name gets a new URL), point
its DISCORD_WEBHOOK_URL secret at the same Discord channel, repoint
YoPingMe's alert channel at the new Worker URL, then delete the old
Worker. That is how you rotate it.
Step 5: send a test alert
Simulate what YoPingMe sends:
WORKER_URL='https://your-relay.example.workers.dev'
BODY='{"event":"down","idempotency_key":"test-1","incident_id":"inc-test","monitor":"my site","target":"https://example.com","cause":"HTTP 500","sequence_at":"2026-08-07T12:00:00Z"}'
curl -sS -X POST "$WORKER_URL" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: test-1' \
--data "$BODY"
A red "Down: my site" embed should appear in your channel.
The webhook payload
This is the full contract, the same one every webhook receiver gets, verified against the dispatcher on 2026-08-07:
| Field | Value |
|---|---|
event |
down, up, or still_down |
idempotency_key |
Stable per delivery; duplicates of a retried alert carry the same key |
incident_id |
Groups the down, still_down, and up alerts of one incident |
monitor |
The monitor's name |
target |
The URL or host being checked |
cause |
What failed; present on down and still_down |
sequence_at |
RFC 3339 timestamp; orders events within an incident |
Headers: Content-Type: application/json, Idempotency-Key (same value
as the body field), and X-YoPingMe-Signature: sha256=<hex HMAC-SHA-256 of the raw body>. YoPingMe generates the signing secret itself when you
create the channel and never exposes it again, so the header is always
present but is not something this recipe verifies - see "Keep the Worker
URL private" above for how the relay is secured instead.
Delivery: 10 second timeout, then retries - up to 5 attempts over about
10 minutes of doubling backoff. Delivery is at-least-once: if Discord or
your relay answers slowly, you may get the same alert twice, which is
what idempotency_key is for. The relay above forwards duplicates as-is;
a busy channel that wants exactly-once display can remember recent keys
in Workers KV, which is more machinery than most alert channels need.