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:
- Per-request — include
webhookUrlin the generate body. Takes precedence over the org default. - Organisation default — set a URL once in Settings → Developer → Webhooks. All jobs that don't supply their own
webhookUrluse this. Only org owners can update the default.
Payload fields
| Field | Type | Description |
|---|---|---|
event | string | Always "job.completed" |
jobId | string | Unique job identifier |
orgId | string | Organisation that owns the job |
status | string | Always "completed" in this event |
templateId | string | Template used to generate the PDF |
documentId | string | ID of the stored document |
documentFilename | string | Filename of the generated PDF |
documentExpiresAt | string | null | ISO 8601 expiry timestamp; null if retention is indefinite |
downloadUrl | string | One-hour token URL — no auth header required to download |
completedAt | string | ISO 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
});