Webhooks
Instead of waiting for the PDF, give the API a URL. It answers at once with 204 No Content, makes the file, and sends it to your server.
When to use them
- Your client cannot or should not hold a connection open for many seconds (serverless functions, request queues).
- You already have a job queue and want the result to arrive as a request to your app.
A webhook does not make conversions faster, and does not lift the 60-second limit. The
request counts as one PDF when it is accepted with 204, also if the job fails later.
Headers
Add these headers to any conversion or PDF tool request (but not to the metadata and bookmarks reads, which have no file to send):
| Header | Type | Default | What it does |
|---|---|---|---|
Webhook-Url required | header | – | Where the result goes, as the body of an HTTP request. |
Webhook-Events-Url | header | – | Where a short JSON event goes after the delivery: webhook.success, or webhook.error with the status and message. Send this one or Webhook-Error-Url (or both). |
Webhook-Error-Url | header | – | Where a JSON error goes when the job fails. The older way; prefer Webhook-Events-Url. |
Webhook-Method | header | POST | POST, PUT or PATCH for the call to Webhook-Url. |
Webhook-Error-Method | header | POST | The same for Webhook-Error-Url. Events always use POST. |
Webhook-Extra-Http-Headers | header (JSON object) | – | Headers we add to every call to your URLs, for example {"Authorization": "Bearer your-own-secret"}. A Content-Disposition here replaces ours. |
Example
curl --fail-with-body \
--header "Authorization: Bearer $PDD_KEY" \
--header "Webhook-Url: https://app.example.com/hooks/pdf?invoice=1042" \
--header "Webhook-Events-Url: https://app.example.com/hooks/pdf-events" \
--header 'Webhook-Extra-Http-Headers: {"Authorization": "Bearer your-own-secret"}' \
--header "Output-Filename: invoice-1042" \
--form files=@index.html \
https://api.pdfdispatch.com/v1/convert/html
The answer is 204 with an X-Request-Id. Keep the id: every call that follows carries it.
What your server receives
The file, at Webhook-Url (with Webhook-Method, POST by default):
- the body is the file itself (PDF, image or ZIP), not multipart;
Content-TypeandContent-Lengthas usual,Content-Disposition: attachment; filename="invoice-1042.pdf";X-Request-Id: the id from the204answer;- your
Webhook-Extra-Http-Headers.
An event, at Webhook-Events-Url, always POST, JSON, after the delivery:
{ "event": "webhook.success", "correlationId": "0192f7c4-5b7e-7d3a-9c41-2f0e8a6b1d55", "timestamp": "2026-10-26T09:41:07.512Z" }
{
"event": "webhook.error",
"correlationId": "0192f7c4-5b7e-7d3a-9c41-2f0e8a6b1d55",
"timestamp": "2026-10-26T09:41:07.512Z",
"error": { "status": 400, "message": "Invalid form data: form field 'paperWidth' is invalid ..." }
}
correlationId is the request id. On an error at Webhook-Error-Url, the body is
{"status": 400, "message": "..."}, sent with Webhook-Error-Method.
Answering, and retries
Answer with any 2xx once you have stored the file. A failed delivery (a network error, 429 or a 5xx from your
server) is retried up to four times, with waits growing from 1 to 30 seconds; each try may take up to 30 seconds. A
4xx other than 429 is not retried. Events are sent once and not retried; use them as a signal, not as the only
record.
Securing your endpoint
Anyone who knows your webhook URL could post to it. Protect it:
- Put a secret in
Webhook-Extra-Http-Headers({"Authorization": "Bearer ..."}) and check it on every call. Use a different secret from your PDFDispatch key. - Or put an unguessable token in the URL itself, one per job, and accept each token once.
- Match the
X-Request-Idto the request you made before you trust the content.
Your URLs must be on the public internet; private and local addresses are refused with 403 before the job starts
(details).