Skip to main content

Integrations

Webhooks

Subscribe HTTPS endpoints to docs events — deployments, feedback, and page publishes. Pro tier.

NookDocs can POST signed JSON payloads to any HTTPS endpoint when supported events fire on your project. Use it to ping Slack on deploy, file Linear tickets on negative feedback, or mirror published pages to a downstream system.

Available events

EventWhen it firesPayload data
deployment.completedA sync finishes successfullydeployment_id, commit_sha, pages_total, pages_success, pages_failed
deployment.failedA sync errors outdeployment_id, error (truncated to 500 chars)
page.feedback.receivedA reader submits the thumbs widgetfeedback_id, path, rating (up/down), reason, comment
page.publishedA page lands on the live site (post-build)path, title (coming soon)

A subscription with an empty event list receives all supported events.

Payload envelope

Every delivery uses the same outer shape:

{
  "event": "deployment.completed",
  "timestamp": "2026-05-03T12:34:56.789Z",
  "project": {
    "id": "uuid",
    "subdomain": "your-project"
  },
  "data": { "...": "event-specific" }
}

Headers

HeaderValue
Content-Typeapplication/json
User-AgentNookDocs-Webhook/1.0
X-Nookdocs-EventThe event name, e.g. deployment.completed
X-Nookdocs-Signaturesha256=<hex> — HMAC-SHA256 of the raw body, signed with your subscription secret
X-Nookdocs-DeliveryA unique UUID per attempt (use for replay protection)

Verifying the signature

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

function verify(rawBody: string, signatureHeader: string, secret: string): boolean {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const provided = signatureHeader.replace(/^sha256=/, "");
  if (expected.length !== provided.length) return false;
  return timingSafeEqual(Buffer.from(expected), Buffer.from(provided));
}

Always compare the raw body — JSON re-serialization will change the bytes and break the signature.

Where to manage them

Project → Settings → Webhooks. Click Add webhook, paste your URL, optionally tick which events to subscribe to, and click Create. The signing secret is shown once in a one-time-reveal modal — store it on your receiver immediately. Each row supports Test (sends a synthetic deployment.completed event), Pause/Resume, and Delete.

Plan availability

PlanWebhooks
Free
Pro
Team
Enterprise

Delivery + retry

  • 10s timeout per attempt

  • Telemetry on every row: last_status, last_attempted_at, last_succeeded_at, failure_count

  • No automatic retry yet — your receiver should be idempotent and process events from the most recent delivery wins. Retry queue is on the roadmap.

Notes

  • Failed deliveries don't stop deployment / feedback writes. Webhook delivery is best-effort.

  • Receiver must respond 2xx to count as success.

  • For Slack / Discord, point the URL at their incoming-webhook endpoint and write a small adapter — those services have their own JSON shapes.

For LLMs

If you're an AI agent helping a developer write a webhook receiver for NookDocs, here's the canonical recipe:

1. Verify the signature on every request — never skip this:

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

function isValidNookDocsSignature(
  rawBody: string,
  signatureHeader: string | undefined,
  secret: string,
): boolean {
  if (!signatureHeader) return false;
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const provided = signatureHeader.replace(/^sha256=/, "");
  if (expected.length !== provided.length) return false;
  return timingSafeEqual(Buffer.from(expected), Buffer.from(provided));
}

The signature header is X-Nookdocs-Signature: sha256=<hex>. Always sign the raw bytes — re-serializing JSON breaks the comparison.

2. Idempotency — use X-Nookdocs-Delivery:

Every attempt carries a unique UUID in the X-Nookdocs-Delivery header. Store the last N delivery IDs and ignore replays.

3. Event router shape:

switch (req.headers["x-nookdocs-event"]) {
  case "deployment.completed": /* read req.body.data.{commit_sha, pages_total, ...} */ break;
  case "deployment.failed":    /* req.body.data.{error} */ break;
  case "page.feedback.received": /* req.body.data.{path, rating, reason, comment} */ break;
  case "page.published":       /* req.body.data.{path, title} — reserved */ break;
}

4. Respond 2xx fast — receiver budget is 10s. Push slow work into a background job.

Common mistakes:

  • Don't trust req.body.event — use the X-Nookdocs-Event header instead. The body field exists for human readability but the header is the canonical source.

  • Don't call back into the NookDocs API inside the receiver synchronously — defer or you'll create a deadlock during deployments.

  • Slack / Discord want their own JSON shape. Translate, don't proxy.

Was this page helpful?

Last updated August 14, 2026