Skip to content

Webhook helpers

When your site’s feed rebuilds, Ghost Writr POSTs a feed.updated body to your registered URL with an X-GW-Signature header. Verify it, then bust your cache and re-fetch. The same two helpers sign and verify, so the signature matches by construction. See instant updates.

FeedUpdatedPayload:

interface FeedUpdatedPayload {
event: "feed.updated";
siteId: string;
buildId: string;
}

verifyFeedSignature(secret, rawBody, signature) returns a Promise<boolean>. It computes HMAC-SHA256 of the raw body with your shared secret and compares it to the header in constant time (so a wrong signature can’t be discovered byte-by-byte via timing). A missing or empty signature returns false.

verify a webhook (any fetch runtime)
import { verifyFeedSignature, FEED_SIGNATURE_HEADER } from "@ghostwritr/feed";
export async function POST(request: Request) {
const raw = await request.text(); // raw body, BEFORE JSON.parse
const sig = request.headers.get(FEED_SIGNATURE_HEADER);
const ok = await verifyFeedSignature(process.env.GHOSTWRITR_FEED_SECRET!, raw, sig);
if (!ok) return new Response("invalid signature", { status: 401 });
const payload = JSON.parse(raw); // FeedUpdatedPayload
// ...revalidate this site, then re-fetch the feed.
return new Response("ok");
}

FEED_SIGNATURE_HEADER is the header name ("X-GW-Signature") — import it rather than hardcoding the string.

signFeedPayload(secret, rawBody) returns a Promise<string> — the hex HMAC-SHA256 you’d put in the header. Ghost Writr signs your real webhooks; you’ll mostly use this to build a fixture in tests.

sign for a test
import { signFeedPayload, FEED_SIGNATURE_HEADER } from "@ghostwritr/feed";
const body = JSON.stringify({ event: "feed.updated", siteId, buildId });
const sig = await signFeedPayload(secret, body); // 64-char lowercase hex
const res = await app.request("/webhooks/ghostwritr", {
method: "POST",
headers: { [FEED_SIGNATURE_HEADER]: sig },
body,
});
  • The concept — how instant updates fit the immutable-snapshot feed. See Instant updates.
  • Re-fetch after verifying — the fetchers that read the new build. See Fetchers.
  • Full surface — every export, typed. See API reference.