Sitemap webhook documentation

A dependable event for every new sitemap URL.

Point a monitor at your HTTPS endpoint. SitemapKit signs each JSON delivery, identifies it for safe deduplication, and retries failures.

Create a monitor

Event payload

Handle one event: sitemap.pages.discovered

The payload identifies the monitor and run, reports the complete new-URL count, and includes the first 100 discovered URLs. Use the delivery ID as your idempotency key.

  • The first baseline never sends this event
  • A URL already observed is never announced twice
  • newUrlsTruncated tells you when the sample exceeds 100 URLs
application/json
{
  "id": "qv2k8...",
  "event": "sitemap.pages.discovered",
  "createdAt": "2026-09-03T14:00:12.412Z",
  "data": {
    "monitor": {
      "id": "t9cc2...",
      "name": "Product catalog",
      "url": "https://example.com"
    },
    "run": {
      "id": "x7m1a...",
      "checkedAt": "2026-09-03T14:00:12.412Z",
      "totalUrls": 1287,
      "newUrlCount": 3,
      "truncated": false
    },
    "newUrls": [
      "https://example.com/products/atlas",
      "https://example.com/products/orbit",
      "https://example.com/blog/launch-week"
    ],
    "newUrlsTruncated": false
  }
}

Verify before processing.

Compute HMAC SHA-256 over the timestamp, a period, and the exact raw request body. Compare it with the signature header using a constant-time comparison.

X-SitemapKit-Event

X-SitemapKit-Delivery

X-SitemapKit-Timestamp

X-SitemapKit-Signature

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

const rawBody = await request.text();
const timestamp = request.headers.get("x-sitemapkit-timestamp");
const received = request.headers
  .get("x-sitemapkit-signature")
  ?.replace("sha256=", "");

const timestampNumber = Number(timestamp);
const fresh = Number.isFinite(timestampNumber) &&
  Math.abs(Math.floor(Date.now() / 1000) - timestampNumber) <= 300;

const expected = createHmac("sha256", process.env.SITEMAPKIT_SECRET)
  .update(`${timestamp}.${rawBody}`)
  .digest("hex");

const signatureHex = received ?? "";
const validSignature = /^[a-f0-9]{64}$/i.test(signatureHex) &&
  timingSafeEqual(
    Buffer.from(signatureHex, "hex"),
    Buffer.from(expected, "hex")
  );

if (!fresh || !validSignature) {
  return new Response("Invalid signature", { status: 401 });
}

Public HTTPS only

Private, loopback, reserved, credential-bearing, and non-HTTPS destinations are rejected. DNS is checked again at connection time.

Durable retries

Non-2xx responses and network failures retry after 1 minute, 5 minutes, 30 minutes, and 2 hours, up to five attempts.

Rotatable secret

The signing secret is shown once, encrypted at rest, and can be rotated immediately from monitor settings.

Connect your first webhook.

The Free plan includes one daily sitemap monitor and signed delivery retries.