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
| Event | When it fires | Payload data |
deployment.completed | A sync finishes successfully | deployment_id, commit_sha, pages_total, pages_success, pages_failed |
deployment.failed | A sync errors out | deployment_id, error (truncated to 500 chars) |
page.feedback.received | A reader submits the thumbs widget | feedback_id, path, rating (up/down), reason, comment |
page.published | A 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
| Header | Value |
Content-Type | application/json |
User-Agent | NookDocs-Webhook/1.0 |
X-Nookdocs-Event | The event name, e.g. deployment.completed |
X-Nookdocs-Signature | sha256=<hex> — HMAC-SHA256 of the raw body, signed with your subscription secret |
X-Nookdocs-Delivery | A 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
| Plan | Webhooks |
| Free | — |
| Pro | ✅ |
| Team | ✅ |
| Enterprise | ✅ |
Delivery + retry
10s timeout per attempt
Telemetry on every row:
last_status,last_attempted_at,last_succeeded_at,failure_countNo 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
2xxto 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.