Use webhooks to receive terminal verification outcomes without polling.

Current terminal events

The current terminal events are:

  • verification.completed
  • verification.review_required
  • verification.failed

Webhook configuration is stored per client app. Each app owns its own endpoint and signing secret.

Required headers

Every outbound request includes:

  • Content-Type: application/json
  • User-Agent: docufykit-webhooks/1.0
  • X-Webhook-Event
  • X-Webhook-Id
  • X-Webhook-Timestamp
  • X-Webhook-Signature

Signature format

The signature format is:

sha256=HMAC_SHA256(secret, timestamp + "." + delivery_id + "." + raw_body)

Verify the signature against the raw request body bytes, not reserialized JSON.

import crypto from "node:crypto";

function verifyDocufykitWebhook({
  secret,
  timestamp,
  deliveryId,
  rawBody,
  signature,
}: {
  secret: string;
  timestamp: string;
  deliveryId: string;
  rawBody: Buffer;
  signature: string;
}) {
  const payload = Buffer.concat([
    Buffer.from(timestamp, "utf8"),
    Buffer.from(".", "utf8"),
    Buffer.from(deliveryId, "utf8"),
    Buffer.from(".", "utf8"),
    rawBody,
  ]);

  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(payload).digest("hex");

  const actual = Buffer.from(signature, "utf8");
  const wanted = Buffer.from(expected, "utf8");

  return (
    actual.length === wanted.length &&
    crypto.timingSafeEqual(actual, wanted)
  );
}

Retry behavior by environment

Sandbox

  • one delivery attempt only
  • no automatic retry schedule
  • no dead_letter state
  • failed deliveries stay in failed until manual replay

Production

If your endpoint does not return 2xx, the platform retries automatically on this schedule:

  • 30 seconds
  • 2 minutes
  • 10 minutes
  • 1 hour
  • 6 hours
  • 24 hours

After the final retry, the delivery moves to dead_letter.

Replay workflow

Customers can:

  • inspect the latest webhook request and response in the portal
  • see failed and dead-letter deliveries
  • replay a delivery manually from the deliveries page

Integration guidance

  • Use a separate webhook URL per environment.
  • Use separate apps and environment-prefixed API keys for sandbox and production.
  • Treat signed webhook delivery as the primary async result path.
  • Use REST lookups as a secondary inspection path, not a replacement for signature verification.