Webhook & Download
Asynchronous processing and external file delivery.
Webhooks
By default, requests are synchronous: you send a file and wait for the PDF. For large files or batch operations, add webhook headers to switch to async:
- Gotenberg validates the request and returns
204 No Content. - The conversion runs in the background.
- The result is POSTed to your
Gotenberg-Webhook-Url.
Webhook callbacks carry a W3C traceparent header. Since 8.34.0, asynchronous processing continues the caller's trace: send a traceparent header with the request and every callback (success, error, events) carries the same trace id. This is distinct from Gotenberg-Trace, a plain correlation id unrelated to OpenTelemetry. See Telemetry.
Gotenberg retries a callback after a connection error, a 429, or a 5xx other than 501, up to WEBHOOK_MAX_RETRY times, waiting between WEBHOOK_RETRY_MIN_WAIT and WEBHOOK_RETRY_MAX_WAIT. Other 4xx responses are not retried. Since 8.37.0, a Retry-After header never stretches a wait past WEBHOOK_RETRY_MAX_WAIT, and each callback, retries included, gives up after WEBHOOK_CLIENT_TIMEOUT × (WEBHOOK_MAX_RETRY + 1) + WEBHOOK_RETRY_MAX_WAIT × WEBHOOK_MAX_RETRY, at least one second. With the defaults, that is 270 seconds.
See the Webhook module configuration for retries, timeouts, and allowed URLs. Tools like PipeDream or Webhook.site are great for testing.
NoneNonePOSTPOSTNoneNonecurl \
--request POST http://localhost:3000/forms/chromium/convert/url \
--header 'Gotenberg-Webhook-Url: https://my.webhook/success' \
--header 'Gotenberg-Webhook-Events-Url: https://my.webhook/events' \
--header 'Gotenberg-Webhook-Extra-Http-Headers: {"Authorization": "Bearer 123"}' \
--form url=https://my.url
- 204
- 400
- 403
- Success
- Error
Content-Type: text/plain; charset=UTF-8
Gotenberg-Trace: {trace}
Body: {error}
Content-Type: text/plain; charset=UTF-8
Gotenberg-Trace: {trace}
Body: Forbidden
Content-Disposition: attachment; filename={output-filename.ext}
Content-Type: {content-type}
Content-Length: {content-length}
Gotenberg-Trace: {trace}
traceparent: {traceparent}
User-Agent: Gotenberg
Body: {output-file}
Content-Type: application/json; charset=UTF-8
Gotenberg-Trace: {trace}
traceparent: {traceparent}
User-Agent: Gotenberg
{
"status": 500,
"message": "conversion failed"
}
Webhook Events
Set the Gotenberg-Webhook-Events-Url header to receive structured JSON events after each webhook operation. Events fire after the main webhook callbacks and do not affect the result delivery.
Gotenberg delivers each event as a POST request, regardless of Gotenberg-Webhook-Method.
Success event:
{
"event": "webhook.success",
"correlationId": "unique-request-id",
"timestamp": "2025-01-15T10:30:00.000000000Z"
}
Error event:
{
"event": "webhook.error",
"correlationId": "unique-request-id",
"timestamp": "2025-01-15T10:30:00.000000000Z",
"error": {
"status": 500,
"message": "conversion failed"
}
}
Delivery failures to the events URL are logged but do not cascade to the main webhook flow.
Download From
All multipart/form-data endpoints accept the downloadFrom form field. This allows you to instruct Gotenberg to fetch files from remote URLs (e.g., S3, Azure Blob, or an internal CDN) instead of uploading them directly.
This feature is enabled by default. It can be disabled via the API_DISABLE_DOWNLOAD_FROM environment variable.
For further details, refer to the API module configuration.
Object Structure
The downloadFrom form field accepts a JSON-formatted array. Each item in the array must be an object with the following keys:
| Key | Description | Default |
|---|---|---|
url | URL of the file. The remote server MUST return a Content-Disposition header with a filename parameter. | Required |
extraHttpHeaders | A JSON object containing extra HTTP headers to send when fetching this specific URL. | None |
embedded | Attaches the downloaded file inside the output PDF (legacy, prefer field). See Attachments. | false |
field | Routes the downloaded file to a feature: "embedded" attaches it (see Attachments), "watermark" and "stamp" supply the watermark or stamp file. Takes precedence over embedded. | "" (regular file) |
Limits
Since 8.37.0, Gotenberg fetches at most API_DOWNLOAD_FROM_MAX_CONCURRENCY entries at once per request (default 10), and returns 400 Bad Request when the array holds more than API_DOWNLOAD_FROM_MAX_ENTRIES entries (default 0, no limit). Every download, retries included, shares the request deadline set by API_TIMEOUT, and a Retry-After header never stretches a wait past it. See the API module configuration.
Currently, a bug in Go prevents Gotenberg from parsing responses with an empty media type.
AWS S3 Users: Ensure your S3 objects have the Content-Disposition metadata set (e.g., attachment; filename="doc.pdf") to avoid this issue.
Nonecurl \
--request POST http://localhost:3000/forms/libreoffice/convert \
--form 'downloadFrom=[{
"url": "http://url/to/file.docx",
"extraHttpHeaders": {
"X-My-Header": "MyValue"
}
}]' \
-o my.pdf
With field, the downloaded file feeds a feature instead of being converted. Download a watermark image instead of uploading it:
curl \
--request POST http://localhost:3000/forms/pdfengines/watermark \
--form files=@/path/to/file.pdf \
--form watermarkSource=image \
--form 'downloadFrom=[{"url":"http://url/to/logo.png","field":"watermark"}]' \
-o my.pdf
Outbound URL Security
Webhook callbacks and downloadFrom fetches go through Gotenberg's outbound filter pipeline. Rejected URLs return 403 Forbidden.
Defaults are permissive. See Outbound URL Filtering to opt into stricter checks.
What's Next?
Restrict where Gotenberg can reach with Outbound URL filtering, or observe requests with Telemetry.