Webhook monitoring
Monitor a webhook receiver with an uptime check on a GET route at the same path, because providers only ever POST to it, and monitor the jobs behind it with a push monitor they call like an inbound webhook, so a missing call becomes an alert.
A webhook receiver fails quietly. Stripe retries a failed delivery for up to three days in live mode, so a broken endpoint does not look like an outage. It looks like nothing. You find out when a customer paid and their account never upgraded.
The obvious fix does not work as is. Logdash monitors send a plain GET with no body and no custom headers, and a receiver built for a provider only accepts POST. Point a monitor at /webhooks/stripe and it gets a 404 or a 405 on every check and stays red forever. So give the monitor its own door on the same path.
Webhook endpoint monitoring
import express from 'express';
import { pool } from '../db.js';
import { handleStripeEvent } from '../stripe.js';
export const webhooks = express.Router();
// What Stripe calls. Raw body, because the signature check needs it.
webhooks.post(
'/webhooks/stripe',
express.raw({ type: 'application/json' }),
handleStripeEvent,
);
// What the monitor calls. Same path, same process, same database.
webhooks.get('/webhooks/stripe', async (req, res) => {
try {
await pool.query('select 1');
res.status(200).send('ok');
} catch {
res.status(503).send('db');
}
});Same path on purpose. A router refactor that drops the route, a proxy rule that stops forwarding it, a deploy that never came up: each turns this GET red the moment the POST starts failing. What it cannot see is a rotated signing secret or a provider that stopped sending, because the GET never runs your signature check. The provider's delivery log covers those.
Cron webhook monitoring
Push monitors flip the direction. Your code calls POST https://api.logdash.io/ping/<monitorId> with no auth header and no body, and the monitor goes down when a check window passes without a call. Push monitors are on Pro, where the window is 15 seconds. That fits a worker or a queue consumer that loops all day. It does not fit a crontab line that runs hourly or nightly, because the monitor would go down 15 seconds after every run. For those, Healthchecks.io or Cronitor, with a cron expression and a grace period per job, are the better tool.
# The ping fires only when a pass succeeds, so a crash or a hang
# reads as a missed window. Keep one pass plus the sleep under 15 s.
while true; do
/srv/app/bin/drain-queue \
&& curl -fsS -m 5 -X POST https://api.logdash.io/ping/68b4c1f0e3a2d5c7b9f01234
sleep 10
done- Add the GET route Ship the GET handler beside the POST handler and open https://yourapp.com/webhooks/stripe in a browser. You should see ok, not a 405.
- Create the monitors Add a monitor with that URL. It checks every 5 minutes on the free plan, every minute on Builder and every 15 seconds on Pro. A service holds one monitor, so on Pro add a second service with a push monitor for the worker and paste its id into the loop.
- Break it on purpose Connect Telegram, then stop the database. The GET returns 503, the monitor flips to down on the next check, and a Telegram alert names the receiver and shows the 503.
Webhook monitoring tools
An uptime check and a webhook gateway answer different questions. Logdash asks your receiver whether it can take a request. Hookdeck sits in front of it and sees every real delivery.
Logdash vs Hookdeck
| Feature | Logdash | Hookdeck |
|---|---|---|
| Receiver down during a quiet hour | Caught on the next check, every 15 seconds on Pro | Seen when the next real event fails to deliver |
| One failed delivery | Invisible while the GET still answers 200 | Opens an issue with the payload attached |
| Retries | None, the provider retries on its own schedule | Automatic retries, plus manual and bulk retries |
| Alert channels | Telegram and webhook, and that is the list | Email and webhook, Slack and PagerDuty on paid plans |
| Change to your stack | One GET route | Every provider points at a Hookdeck URL, one more hop |
| Free tier | 5 monitors, checked every 5 minutes | 10,000 events a month, 3-day retention |
When Hookdeck is the better pick
- Every event matters. A GET that answers 200 tells you nothing about the one invoice.paid that failed at 02:14.
- You want failed events retried after the fix, not only an alert that something broke.
- You want the alert in Slack or PagerDuty without writing and hosting the bridge yourself.
Webhook failure alert
Logdash alerts on the transition, once when a monitor goes down and once when it recovers, not on every failed check. Besides Telegram, the webhook channel calls your own URL. On the free plan it sends a GET with no body. On Builder and Pro, set it to POST to receive JSON with httpMonitorId, newStatus, name, url, errorMessage and statusCode.