Skip to content

Reference

Webhooks

Docsmith posts an event to your endpoint when a sync completes or a draft changes state. Add an endpoint in Settings → Webhooks; you can have up to five, each subscribed to whichever events you choose.

Events

EventFires when
sync.completedA sync finished, with either verdict. The most useful event.
draft.createdA stale verdict produced a draft and it entered the queue.
draft.approvedSomeone approved a draft. Carries the reviewer and whether they edited it first.
draft.rejectedSomeone rejected a draft, with the reason if one was given.
repository.indexedA first index or a re-index finished. Carries the page count.
quota.warningYou have used 80% of the month’s syncs. Fires once per month.

Payload

Every delivery has the same envelope. The data object differs by event type.

{
  "id": "evt_2b91f4c7ae",
  "type": "sync.completed",
  "createdAt": "2026-07-30T11:42:08Z",
  "data": {
    "syncId": "syn_9f31c0a7b4",
    "repository": "acme/checkout-sdk",
    "commit": "a4f19c2",
    "docPath": "docs/guides/retries.md",
    "verdict": "stale",
    "findingCount": 2,
    "topSeverity": "breaking",
    "draftId": "drf_71ba0e",
    "url": "https://docsmithhq.com/dashboard/drafts/drf_71ba0e"
  }
}

Payloads never contain your source code or the full text of a page. They carry identifiers and metadata; fetch the content from the API if you need it.

Verifying a delivery

Every request carries two headers. Docsmith-Signature holds a timestamp and an HMAC-SHA256 of {timestamp}.{raw body}, keyed with your endpoint secret.

Docsmith-Signature: t=1785412800,v1=5257a869e7bcf...
Docsmith-Delivery: dlv_88ac21f0
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(rawBody: Buffer, header: string, secret: string) {
  const parts = Object.fromEntries(
    header.split(",").map((kv) => kv.split("=") as [string, string]),
  );
  const timestamp = Number(parts.t);
  const received = parts.v1;
  if (!timestamp || !received) return false;

  // Reject anything older than five minutes to stop replay.
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;

  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody.toString("utf8")}`)
    .digest("hex");

  const a = Buffer.from(expected);
  const b = Buffer.from(received);
  return a.length === b.length && timingSafeEqual(a, b);
}

Three things people get wrong here, in order of frequency:

  • Verify against the raw bytes. If your framework parses JSON and re-serialises it, the bytes differ and the signature will not match. In Express, use express.raw({ type: 'application/json' }) on the webhook route only.
  • Compare in constant time. A plain === leaks timing information.
  • Check the timestamp. Without it, a captured delivery can be replayed forever.

Retries and ordering

Respond 2xx within 10 seconds. Anything else — including a timeout — is a failure, and we retry with exponential backoff at 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours. After the last attempt the delivery is marked failed and kept for 7 days in Settings → Webhooks, where you can replay it.

Deliveries are not ordered and may arrive more than once. Use id for deduplication; it is stable across retries of the same event. Do the work asynchronously and acknowledge immediately — an endpoint that does its processing before responding is the most common cause of timeouts.

After 100 consecutive failures the endpoint is disabled and we email the account owner. Nothing is lost; disabled endpoints can be re-enabled and their failed deliveries replayed.

Rotating a secret

Generate a second secret on the endpoint, deploy code that accepts either, then retire the first. Both secrets sign every delivery during the overlap, sent as two v1 values in the signature header — accept the delivery if either matches.

Testing locally

docsmith webhooks listen --forward http://localhost:3000/api/docsmith
docsmith webhooks trigger sync.completed

The CLI opens a tunnel, prints the signing secret for the session, and can replay any past delivery by id. See the CLI page.

Source addresses

If you need to allowlist, deliveries come from 52.12.44.0/24 and 52.12.45.0/24. We give 30 days’ notice before this changes. Verifying the signature is better than allowlisting an address, and you should do it either way.

Problems with a delivery? support@docsmithhq.com — include the Docsmith-Delivery id.