Aller au contenu
FR
On this page

Webhooks

The async modes of the API (async=true emissions, batches) produce the PDF in a queue, a few seconds after the 201/202 response. Without webhooks you track progress by polling GET /documents/{public_id}/pdf or GET /batches/{batch_id}. With a webhook, DokKraft notifies you as soon as the state changes — polling remains the source of truth, the webhook is an optimization (a delivery can be lost: your handler must tolerate receiving nothing). Document events also cover the lifecycle of every document in the workspace, emitted through the API as well as the UI, a batch or an automation.

Declare an endpoint

Webhooks are included from the Pro plan upward (they complement the async API) — Free and Starter workspaces get read-only access. In DokKraft: Settings → Webhooks. Each workspace can declare up to 10 endpoints; an endpoint subscribes to a subset of the events below. At creation, a secret (whsec_…) is shown once — store it immediately. It can be rotated at any time (the old one stops signing immediately).

Events

One extra event, ping, is the test delivery: send one at any time with the endpoint's "Test" button (or right after creating it) to validate your URL and signature verification without emitting a real document. Its payload contains a message and the endpoint id.

Event Emitted when Data
ping Test delivery ("Test" button in the UI) message, endpoint
document.emitted A document was emitted (number reserved + frozen snapshot) — API, UI, batch or automation document: publicId, name, number, status, pdfUrl
document.pdf_ready A document's PDF rendering finished and was stored same, status is ready
document.pdf_failed Rendering definitively failed (attempts exhausted) same + errorCode, errorMessage
document.cancelled A document was cancelled (terminal status, neutralised document) same, status is annule
batch.completed A batch reached its final state (completed, partial or failed) batch: publicId, status, counts (emitted, failed), completedAt
automation.run.completed An automation processed an occurrence (emission/delivery finished) automationId, runId, documentId, status (emitted|sent), mode (notify|email|none)

Document, batch and automation identifiers are the same as in the REST API: fetch details with GET /documents/{publicId} or GET /batches/{publicId}. The PDF itself is never in the payload — download it through pdfUrl.

The received body is a common envelope:

{
  "id": "0b6e0f2e-…",
  "event": "document.pdf_ready",
  "createdAt": "2026-09-22T14:03:11+00:00",
  "data": {
    "document": {
      "publicId": "doc_…",
      "name": "Invoice ABC",
      "number": "INV-2026-0042",
      "status": "ready",
      "pdfUrl": "https://dokkraft.blocsland.com/api/v1/documents/doc_…/pdf"
    }
  }
}

Example envelope for automation.run.completed:

{
  "id": "7c1d94a0-…",
  "event": "automation.run.completed",
  "createdAt": "2026-09-30T09:00:04+00:00",
  "data": {
    "automationId": "e3a1c07e-…",
    "runId": 42,
    "documentId": "9f2b6d41-…",
    "status": "sent",
    "mode": "email"
  }
}

status reflects the run's outcome (emitted for "notification" delivery, sent after an email send); mode echoes the channel declared on the rule (notify, email or none).

Verify the signature

Each delivery carries the X-DokKraft-Event, X-DokKraft-Delivery and X-DokKraft-Signature headers, formatted t=<timestamp>,v1=<hmac>. The HMAC is an HMAC-SHA256 of "{t}.{raw_body}" keyed with the endpoint secret — sign the exact body received (not a re-encoded JSON), then compare both v1 values with a constant-time check.

Reference verification (PHP):

$parts = [];
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_DOKKRAFT_SIGNATURE']), $parts);
$expected = hash_hmac('sha256', $parts['t'] . '.' . file_get_contents('php://input'), $secret);
if (! hash_equals($expected, $parts['v1'] ?? '')) {
    http_response_code(401); // invalid signature — not a DokKraft delivery
    exit;
}
if (abs(time() - (int) $parts['t']) > 300) {
    http_response_code(401); // stale delivery — replay or ignore
    exit;
}

Delivery and retries

  • A delivery answering 2xx is considered complete; any other response (or more than 5 s) triggers a retry.
  • Retries: 1 min, 10 min, 1 h, 6 h (5 attempts total), then the delivery is abandoned. It stays visible and replayable in Settings → Webhooks.
  • Idempotence: the same event is never delivered twice to the same endpoint. Conversely, your handler can receive two distinct events in any order — filter on document.status if needed.
  • Daily cap: deliveries are limited to 1,000 per day per workspace (all endpoints combined). Beyond that, no more deliveries are created for the day — events emitted afterwards do not reach you; the counter resets the next day.
  • Disabling an endpoint (toggle) cuts its deliveries immediately, without losing its history.
  • Automatic shutdown: after 20 consecutive abandoned deliveries (an URL that never answers 2xx), the endpoint is switched off automatically to preserve the delivery quota — the UI marks it "Off (failures)" and re-enabling it is one toggle click.