MoonitorMoonitor
Browse documentation

Webhooks

Receive secure, organization-wide lifecycle events. Moonitor signs the exact JSON body, retries failures, and keeps every attempt visible for diagnosis.

Set up a receiver

  1. Expose a public HTTPS endpoint

    The receiver must return a 2xx response after it has safely accepted the event. Private, loopback and cloud-metadata targets are blocked.

  2. Add it in Moonitor

    Open Dashboard → API & webhooks, enter the URL, and subscribe to individual event types or all current and future events.

  3. Copy the signing secret

    The whsec_… secret appears once. Store it securely and use it to verify every delivery before parsing or acting on the data.

  4. Send a test

    The Test button queues a real webhook.test event through the same signing, delivery and attempt-log path as production events.

Payload

{
  "id": "msg_…",
  "type": "incident.created",
  "createdAt": "2026-07-15T12:00:00.000Z",
  "data": { "monitor": { "id": "…" }, "incident": { "id": "…" } }
}

Use id as your idempotency key. Retries keep the same id and payload.

Event types

EventWhen it fires
monitor.createdThe monitor lifecycle reaches created.
monitor.updatedThe monitor lifecycle reaches updated.
monitor.deletedThe monitor lifecycle reaches deleted.
monitor.pausedThe monitor lifecycle reaches paused.
monitor.resumedThe monitor lifecycle reaches resumed.
monitor.downThe monitor lifecycle reaches down.
monitor.recoveredThe monitor lifecycle reaches recovered.
incident.createdThe incident lifecycle reaches created.
incident.acknowledgedThe incident lifecycle reaches acknowledged.
incident.resolvedThe incident lifecycle reaches resolved.
status_page.createdThe status page lifecycle reaches created.
status_page.updatedThe status page lifecycle reaches updated.
status_page.deletedThe status page lifecycle reaches deleted.
maintenance.createdThe maintenance lifecycle reaches created.
maintenance.updatedThe maintenance lifecycle reaches updated.
maintenance.deletedThe maintenance lifecycle reaches deleted.
webhook.testA user or API client requests a receiver test.

Verify signatures

Moonitor follows the Standard Webhooks HMAC-SHA256 format and sends webhook-id, webhook-timestamp and webhook-signature. Sign the exact raw request body; do not parse and re-serialize it first.

import { createHmac, timingSafeEqual } from "node:crypto";

const id = request.headers.get("webhook-id");
const timestamp = request.headers.get("webhook-timestamp");
const signatures = request.headers.get("webhook-signature").split(" ");
const rawBody = await request.text();
const key = Buffer.from(process.env.MOONITOR_WEBHOOK_SECRET.slice(6), "base64");
const expected = createHmac("sha256", key)
  .update([id, timestamp, rawBody].join("."))
  .digest("base64");
const valid = signatures.some((value) => {
  const actual = Buffer.from(value.replace(/^v1,/, ""), "base64");
  const wanted = Buffer.from(expected, "base64");
  return actual.length === wanted.length && timingSafeEqual(actual, wanted);
});
  • Reject timestamps more than five minutes away from your current time to limit replay.
  • Store each webhook-id before processing so a retry cannot apply the event twice.
  • During secret rotation, Moonitor signs with both old and new secrets for 24 hours. Accept either valid signature while deploying the new secret.

Delivery and retries

Any 2xx status is success. Redirects, 4xx, 5xx, timeouts and network errors are failures. Moonitor makes up to 6 attempts: the first immediately, then after 30 seconds, 2 minutes, 10 minutes, 1 hours, 6 hours. A reasonable Retry-After response overrides the next delay.

Tip

Acknowledge quickly, then process the event from your own durable queue. Moonitor waits up to 20 seconds for each response.

Operate safely

  • Treat signing secrets like API keys. They are encrypted at rest and shown only at create or rotate time.
  • Never disable signature verification, even if the endpoint URL is difficult to guess.
  • Inspect recent delivery status in Dashboard → API & webhooks; use the public API for every response attempt and diagnostic body.
  • Disable or delete an endpoint that is no longer used; revoke leaked API keys immediately.