Uptime monitoring webhook

When a check flips between up and down, Logdash sends one HTTP request to your URL, and on POST, PUT or PATCH it carries a flat JSON body with the monitor id, new status, name, URL, status code and error.

A downtime webhook is the escape hatch for everything Logdash does not ship natively: Slack, Discord, email, PagerDuty, a status light on your desk. Logdash sends a request on every state change, nothing more. The first failed check flips the monitor to down and fires one request. The first good check after that fires one more with the new status set to up. A three-hour outage is two requests. Every channel on the monitor fires on the same state change, so the webhook and a Telegram chat hear about it at the same moment.

The uptime webhook payload

down
{
  "httpMonitorId": "6650f3c2a1b2c3d4e5f60718",
  "newStatus": "down",
  "name": "API",
  "url": "https://api.example.com/health",
  "errorMessage": "{\"ok\":false,\"error\":\"db\"}",
  "statusCode": "503"
}
up
{
  "httpMonitorId": "6650f3c2a1b2c3d4e5f60718",
  "newStatus": "up",
  "name": "API",
  "url": "https://api.example.com/health",
  "statusCode": "200"
}
  • newStatus is down or up. Down means a status outside 200-399, a connection error, or no answer within 10 seconds.
  • statusCode is a string. It is "0" when nothing answered at all.
  • errorMessage is the first 1,000 characters of the response body on a bad status, or the network error, such as Connection refused or Timed out after 10s. On up it is left out of the body entirely.
  • url is the address being checked. For a heartbeat monitor it is the literal string push monitor, and a missed beat arrives as status "0" with a Did not receive call for this time range error.

Website down webhook delivery rules

  • On the free Hobby plan the method is GET, with no body, no query string and no custom headers. Your endpoint learns that some monitor on that channel changed, not which way.
  • Builder ($9/month) and Pro ($15/month) add POST, PUT, PATCH and DELETE plus custom headers. POST, PUT and PATCH carry the JSON as application/json. GET and DELETE never carry a body.
  • Requests are not signed. Add a header with a long random secret and reject anything without it.
  • One attempt, a 30-second deadline, and any answer outside 2xx counts as a failure that is logged and not retried. Answer first, do the slow work after.
  • Up to 5 redirects are followed, but a 301 or 302 turns a POST into a GET without the body, so paste the final https URL. Private and internal addresses are refused, so localhost and 10.x URLs do not work.
  • Saving the channel sends nothing. Unlike Telegram there is no welcome message, so test the receiver with curl first.
receiver.mjs
// receiver.mjs - LOGDASH_SECRET=change-me node receiver.mjs
import { createServer } from 'node:http';

createServer(async (req, res) => {
  const secret = req.headers['x-logdash-secret'];

  if (req.method !== 'POST' || secret !== process.env.LOGDASH_SECRET) {
    res.writeHead(403).end();
    return;
  }

  let body = '';
  for await (const chunk of req) body += chunk;

  let alert;
  try {
    alert = JSON.parse(body);
  } catch {
    res.writeHead(400).end();
    return;
  }

  // Answer first: Logdash waits 30 seconds at most and never retries.
  res.writeHead(204).end();

  const { name, newStatus, statusCode, errorMessage = '' } = alert;
  console.log(`${name} is ${newStatus} (${statusCode}) ${errorMessage}`);
}).listen(8080);
  1. Expose the receiver Run it behind any public HTTPS URL and simulate a down alert first: post the JSON above to it with curl and the x-logdash-secret header, and check the log line appears.
  2. Add the webhook channel In the monitor's Alerts card, Add channel, then Webhook: method POST, your URL, header x-logdash-secret. Tick the box to attach it to the monitor, and keep a Telegram channel attached as well.
  3. Break it Stop the app behind the monitor. On the next check your receiver logs the down payload, and the Telegram alert arrives at the same moment from the same state change.

Logdash vs Uptime Kuma webhooks

Downtime webhooks: Logdash vs Uptime Kuma

FeatureLogdashUptime Kuma
Default bodyFlat JSON, six fieldsNested heartbeat and monitor objects plus a message
Custom body templateNo, reshape it in a relayYes, with template variables
Body and headers at $0Bare GET on HobbyYes, on your own server
Request signingNone, use a secret headerNone, use a secret header
Where the sender runsHosted, outside your stackYour server, and it goes down with it

When Uptime Kuma is the better pick

  • You want to template the body and post straight to Slack, Discord or Teams with no relay in between.
  • You need a webhook with a body at $0 and already have a server to run Kuma on.
  • You want the alert only after several failed checks in a row. Kuma has retries, Logdash fires on the first.
What is in the uptime webhook payload?
httpMonitorId, newStatus, name, url, statusCode and, on down, errorMessage. statusCode is a string, "0" when nothing answered. The body is only sent on POST, PUT and PATCH, which need the Builder or Pro plan.
When does the downtime webhook fire?
On every state change: the first failed check sends one down request, the first good check after it sends one up request. Nothing repeats while the status stays the same.
Does a website down webhook retry if my endpoint fails?
No. Logdash makes one attempt with a 30-second deadline. A timeout or a non-2xx answer is logged on our side and dropped, so answer fast and queue any slow work.
Which plan includes the uptime monitoring webhook?
All of them, with a catch. Hobby sends a GET without body or headers. Builder at $9 a month and Pro at $15 add POST, PUT and PATCH with the JSON body, plus custom headers. For comparison, UptimeRobot puts webhooks on Team, the plan above Solo.

Point it at your own URL and watch it for real.