FOR DEVELOPERS
Your website changes state.
We give your tools a hoot.
Receive a small JSON POST when a monitor confirms an outage, and optionally when it recovers. Available on Essential, Pro and Agency. View plans
Set up an endpoint
- Create a public endpoint that accepts
POSTwithContent-Type: application/json. - Open a monitor's edit form and enter its Webhook URL. Use HTTP or HTTPS on port 80 or 443. HTTPS is recommended. Private networks, localhost, URL credentials and fragments are rejected.
- Enable Also send a webhook when the site recovers if you want recovery events. It is off by default and independent of email and SMS recovery settings.
- Save the monitor. Confirmed outages will now queue webhooks. Leave the URL blank to disable them.
Return a successful 2xx response directly. Redirects are not followed. The endpoint's DNS addresses are checked for public accessibility on every delivery.
Events & payload
monitor.down is sent once when the monitor reaches its configured consecutive-failure threshold. monitor.up is sent when that incident closes, if webhook recovery alerts are enabled. Healthy checks, confirmation retries and SSL reminders do not send webhooks.
{
"id": "a7dc5203-1b80-48fa-9772-834b1494dd52",
"event": "monitor.down",
"monitor": {
"id": 42,
"name": "My shop",
"url": "https://shop.example.com"
},
"status": "down",
"checked_at": "2026-09-14T12:00:00+00:00",
"http_status": 503,
"downtime_seconds": null
}
| Field | Meaning |
|---|---|
| id | Unique event UUID. Preserved when you replay the event. |
| event | monitor.down or monitor.up. |
| monitor | The monitor ID, name and website URL at the time of the event. |
| status | down or up. This is the original event state, even on a later replay. |
| checked_at | ISO 8601 timestamp of the check that confirmed the outage or recovery (UTC). |
| http_status | Website response code; null when the check received no HTTP response. This is separate from the webhook endpoint's response code. |
| downtime_seconds | null for a down event. For an up event, elapsed whole seconds from the first failed check to recovery, including confirmation time. |
A recovery is a new event with a new UUID. Its event is monitor.up, status is up, and downtime_seconds is a number, for example 180.
Request headers
Content-Type: application/json
Accept: application/json
User-Agent: PingHoot (monitor webhook)
X-Site-Uptime-Event-Id: a7dc5203-1b80-48fa-9772-834b1494dd52
X-Site-Uptime-Delivery-Id: 123
X-Site-Uptime-Replay: false
Every replay has a new delivery ID and X-Site-Uptime-Replay: true. The event ID and original body remain unchanged. Standard HTTP transport headers are also recorded in the delivery log.
Receive a webhook
Webhooks currently have no cryptographic signature. Event IDs and headers are identifiers, not authentication. Protect your HTTPS endpoint with a long random secret in its URL, such as https://hooks.example.com/uptime?token=YOUR_RANDOM_SECRET, and verify it before accepting a payload. Avoid logging that URL in publicly accessible systems.
This minimal PHP receiver validates the method, secret and event. Set UPTIME_WEBHOOK_TOKEN on your server and replace the processing comment with your application's durable queue or storage operation.
<?php
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit;
}
$expected = getenv('UPTIME_WEBHOOK_TOKEN');
$provided = $_GET['token'] ?? '';
if (!$expected || !is_string($provided) || !hash_equals($expected, $provided)) {
http_response_code(401);
exit;
}
try {
$event = json_decode(file_get_contents('php://input'), true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException) {
http_response_code(400);
exit;
}
if (!is_array($event) || !is_string($event['id'] ?? null)
|| !in_array($event['event'] ?? null, ['monitor.down', 'monitor.up'], true)) {
http_response_code(422);
exit;
}
// Store or enqueue the event durably here before acknowledging it.
// Use event['id'] as a unique key to avoid processing a replay twice.
// Return a 5xx status if you could not persist the event.
http_response_code(204);
For frameworks with CSRF protection, use a dedicated webhook endpoint with its own token check. Keep browser-facing routes protected. Parse JSON as data and never execute content from the request.
Delivery behaviour
- Requests run through the queue after the incident transaction commits. Delivery timing depends on worker availability.
- The connection timeout is 5 seconds and the HTTP request timeout is 10 seconds. A complete 2xx response marks the attempt successful; other status codes mark it failed.
- There are no automatic delivery retries. Review failures and replay them from the history screen.
- Requests are skipped if the account no longer has a paid plan or verified email, the monitor is deleted, or its webhook URL has changed. Automatic recovery deliveries also check that recovery alerts are still enabled.
- Delivery order is not guaranteed. Use the event timestamp when reconciling state. Replays can arrive long after an event occurred.
- A timeout or interrupted worker does not prove that the receiver did nothing. Store the event ID with a unique constraint and make processing idempotent so a replay cannot accidentally repeat a completed action.
Inspect history & replay
Open Webhooks in your dashboard to filter deliveries by monitor or status. Choose Details to inspect the method, URL, request headers and exact JSON body, response status, headers and complete received body, timing and errors. Binary response bodies are displayed as base64.
Logs are retained without an automatic expiry, independently of monitor-check history. They remain available after a downgrade or monitor deletion, and are removed when the account is deleted. URL, header, body and error fields are encrypted at rest. Logging begins when this feature is installed; past responses cannot be recovered.
| Queued | Waiting for a worker. |
|---|---|
| Sending | A worker has started this attempt. |
| Succeeded | The endpoint returned a complete 2xx response. |
| Failed | The endpoint returned a non-2xx response, or a validation, DNS or connection error occurred. An interrupted response contains only the bytes received. |
| Skipped | Current account or monitor settings prevented the request from being sent. |
| Unknown | The worker stopped without a recorded outcome. Check your receiver before replaying. |
Choose Replay on a completed attempt and confirm the destination. This creates a new delivery linked to the original, preserving its exact JSON body and URL. The monitor must still exist and use that URL; changing it back is necessary before replaying an older destination. An explicit replay of a recovery event is allowed even when automatic recovery webhooks are now disabled.
Replay requires a paid plan. View plans . Pending attempts cannot be replayed, only one replay of the same attempt can be pending at a time, and each account can request up to 10 replays per minute. Event IDs remain stable, so a receiver that has already processed the event may correctly acknowledge a replay without repeating its work.