Webhooks

PDFox fires a job.completed event to your endpoint when a PDF generation job finishes. No polling required.

Webhook delivery is non-blocking — a delivery failure never affects the job status or your credits.

Configuring a webhook URL

There are two ways to supply a webhook URL:

  1. Per-request — include webhookUrl in the generate body. Takes precedence over the org default.
  2. Organisation default — set a URL once in Settings → Developer → Webhooks. All jobs that don't supply their own webhookUrl use this. Only org owners can update the default.

Payload fields

FieldTypeDescription
eventstringAlways "job.completed"
jobIdstringUnique job identifier
orgIdstringOrganisation that owns the job
statusstringAlways "completed" in this event
templateIdstringTemplate used to generate the PDF
documentIdstringID of the stored document
documentFilenamestringFilename of the generated PDF
documentExpiresAtstring | nullISO 8601 expiry timestamp; null if retention is indefinite
downloadUrlstringOne-hour token URL — no auth header required to download
completedAtstringISO 8601 timestamp when the job completed

Retries

If your endpoint returns a non-2xx status, PDFox retries with exponential back-off: roughly 1 min, 2 min, 4 min, 8 min (4 attempts total). Delivery history is visible in Settings → Developer → Webhooks.

The downloadUrl in the payload is a 1-hour token. Download the PDF or store the token immediately upon receiving the webhook. To generate a fresh token later, call GET /v1/generate/jobs/:jobId/download.
POST https://your-server.com/hooks/pdfox
Content-Type: application/json

{
  "event":             "job.completed",
  "jobId":             "job_7c9d2e3f",
  "orgId":             "org_a1b2c3d4",
  "status":            "completed",
  "templateId":        "tpl_8f3e2a1b",
  "documentId":        "doc_9e4f1c2a",
  "documentFilename":  "invoice-job_7c9d.pdf",
  "documentExpiresAt": "2026-06-11T10:00:00.000Z",
  "downloadUrl":       "https://api.pdfox.com/v1/generate/jobs/job_7c9d2e3f/file?token=...",
  "completedAt":       "2026-06-04T10:00:03.000Z"
}
app.post('/hooks/pdfox', express.json(), async (req, res) => {
  const event = req.body;
  if (event.event !== 'job.completed') return res.sendStatus(200);

  // Download immediately — token is valid for 1 hour
  const pdf = await fetch(event.downloadUrl);
  await savePdfToStorage(event.documentFilename, await pdf.arrayBuffer());

  res.sendStatus(200); // always respond 2xx to acknowledge
});