Skip to main content

Merge PDFs

Merges multiple PDF files into a single PDF. Files are combined in alphanumeric order (numbers first, then alphabetical).

See the PDF Engines module configuration for engine selection flags.

Basics

POST/forms/pdfengines/merge
Headers
Gotenberg-Output-Filenamestring
The filename of the resulting file - Gotenberg automatically appends the file extension. Defaults to a random UUID filename.
Gotenberg-Tracestring
A custom request ID to identify the request in the logs; overrides the default UUID.
Form Files
filesfile[]required
PDF files to merge.
cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form files=@/path/to/3_pdf.pdf \
-o my.pdf
The merged PDF file.
Content-Disposition: attachment; filename={output-filename.pdf}
Content-Type: {content-type}
Content-Length: {content-length}
Gotenberg-Trace: {trace}
Body: {output-file}

Structure & Metadata

Metadata (PDF Engines)

Inject XMP metadata (Author, Title, Copyright, Keywords, etc.) into the PDF as a JSON object.

Not all tags are writable. Gotenberg uses ExifTool under the hood. See the XMP Tag Name documentation for valid keys. Writing metadata usually breaks PDF/A compliance.

Form Fields
metadatajson
Writes metadata (Author, Title, etc.).
Default:None
cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form files=@/path/to/3_pdf.pdf \
--form 'metadata={"Author":"Julien Neuhart","Copyright":"Julien Neuhart","CreationDate":"2006-09-18T16:27:50-04:00","Creator":"Gotenberg","Keywords":["first","second"],"Marked":true,"ModDate":"2006-09-18T16:27:50-04:00","PDFVersion":1.7,"Producer":"Gotenberg","Subject":"Sample","Title":"Sample","Trapped":"Unknown"}' \
-o my.pdf

Bookmarks (PDF Engines)

Form Fields
bookmarksjson
Bookmarks to write (JSON). A list applies to the final merged PDF. A map of filename→bookmarks shifts page indexes per file before merging.
Default:None
autoIndexBookmarksboolean
Extracts existing bookmarks from input files and offsets their page numbers based on their position in the merged document.
Default:false

Use autoIndexBookmarks to automatically extract existing bookmarks and offset their page numbers based on position in the merged document.

cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form files=@/path/to/3_pdf.pdf \
--form autoIndexBookmarks=true \
-o my.pdf

You can also provide custom bookmarks with the bookmarks form field. When provided as a list, it is applied directly to the final merged PDF. When provided as a map of filename to bookmarks, page indexes are shifted per file before merging.

Both options work together. When autoIndexBookmarks is enabled, Gotenberg respects your custom mappings for specific files while automatically recalculating indexes for the others.

cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form autoIndexBookmarks=true \
--form 'bookmarks={"1_pdf.pdf":[{"title":"Introduction","page":1,"children":[]}],"2_pdf.pdf":[{"title":"Appendix","page":1,"children":[]}]}' \
-o my.pdf

Attachments (PDF Engines)

Attach external files directly inside the PDF container. Commonly used for e-invoicing standards like ZUGFeRD / Factur-X, which require a machine-readable XML invoice as an attachment.

Provide per-attachment metadata with embedsMetadata to satisfy PDF/A-3 and Factur-X requirements: each entry sets the embedded file stream's /Subtype, writes /AFRelationship on the file specification, and references the attachment from the Document Catalog's /AF array.

Form Fields
embedsMetadatajson
Per-attachment metadata keyed by filename. Each entry accepts mimeType (written to the embedded file stream's /Subtype) and relationship (the /AFRelationship value, e.g., Source, Data, Alternative, Supplement, Unspecified). Requires a PDF engine that supports the feature (QPDF by default).
Default:None
Form Files
embedsfile[]
Embeds files into the PDF.
Default:None
cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form files=@/path/to/3_pdf.pdf \
--form embeds=@factur-x.xml \
--form embeds=@logo.png \
--form embedsMetadata='{"factur-x.xml":{"mimeType":"text/xml","relationship":"Alternative"},"logo.png":{"mimeType":"image/png","relationship":"Supplement"}}' \
-o my.pdf

Factur-X (PDF Engines)

Produces a Factur-X / ZUGFeRD e-invoice in one request: upload the CII invoice XML with facturxXml and set facturxConformanceLevel. Both fields are required together. Gotenberg embeds the XML into the resulting PDF and never converts or merges it like other uploaded files. Requires a PDF engine that supports the feature (QPDF by default). See PDF Engines module configuration.

See the dedicated Factur-X route for the full behavior: canonical embedding, XMP metadata, and the PDF/A-3 requirement.

Form Fields
facturxConformanceLevelenum
The Factur-X conformance level recorded in the XMP metadata. Options: MINIMUM, BASIC WL, BASIC, EN 16931, EXTENDED, XRECHNUNG.
Default:None
facturxDocumentTypeenum
The Factur-X document type. Options: INVOICE, ORDER, ORDER_RESPONSE, ORDER_CHANGE.
Default:INVOICE
facturxVersionstring
The Factur-X version recorded in the XMP metadata.
Default:1.0
Form Files
facturxXmlfile
The Factur-X CII invoice XML. Embedded as factur-x.xml regardless of the uploaded filename.
Default:None
cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form files=@/path/to/3_pdf.pdf \
--form facturxXml=@/path/to/factur-x.xml \
--form 'facturxConformanceLevel=EN 16931' \
-o my.pdf

Flatten (PDF Engines)

Merges all interactive form fields (text inputs, checkboxes, etc.) into the page content, making the PDF non-editable.

Form Fields
flattenboolean
Converts form fields into static content, preventing further editing.
Default:false
cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form files=@/path/to/3_pdf.pdf \
--form flatten=true \
-o my.pdf

Watermark (PDF Engines)

Adds a watermark behind the content of each page during post-processing. Sources: text, image, or pdf.

image and pdf sources require an uploaded watermark file. watermarkExpression cannot reference an arbitrary filesystem path; the route returns 400 Bad Request.

The watermarkOptions form field accepts a JSON object whose keys depend on the configured PDF engine:

EngineSyntax Reference
pdfcpu (Default)See pdfcpu watermark documentation
pdftkSee pdftk documentation

Available keys include font, points (font size), color, rotation, opacity, scale, offset, and more. Example:

{
"font": "Helvetica",
"points": 48,
"color": "#808080",
"rotation": 45,
"opacity": 0.15
}

Check the PDF Engines module configuration to see which engine is active.

Form Fields
watermarkSourceenum
The watermark source type. Options: text, image, pdf.
watermarkExpressionstring
The watermark content. For text, the string to render. For image or pdf, the filename of the uploaded watermark file.
watermarkPagesstring
Page ranges to watermark (e.g., 1-3, 5). Empty means all pages.
watermarkOptionsjson
Advanced options in JSON format (e.g., font, color, rotation, opacity, scaling).
Form Files
watermarkfile
An image or PDF file used as watermark source (required when watermarkSource is image or pdf).
cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form files=@/path/to/3_pdf.pdf \
--form watermarkSource=text \
--form watermarkExpression=CONFIDENTIAL \
--form 'watermarkOptions={"opacity":0.25,"rotation":45}' \
-o my.pdf

Stamp (PDF Engines)

Adds a stamp on top of the content of each page during post-processing. Sources: text, image, or pdf.

image and pdf sources require an uploaded stamp file. stampExpression cannot reference an arbitrary filesystem path; the route returns 400 Bad Request.

The stampOptions form field accepts a JSON object whose keys depend on the configured PDF engine:

EngineSyntax Reference
pdfcpu (Default)See pdfcpu stamp documentation
pdftkSee pdftk documentation

Available keys include font, points (font size), color, rotation, opacity, scale, offset, and more. Example:

{
"font": "Helvetica",
"points": 24,
"color": "#008000",
"rotation": 0,
"opacity": 0.6
}

Check the PDF Engines module configuration to see which engine is active.

Form Fields
stampSourceenum
The stamp source type. Options: text, image, pdf.
stampExpressionstring
The stamp content. For text, the string to render. For image or pdf, the filename of the uploaded stamp file.
stampPagesstring
Page ranges to stamp (e.g., 1-3, 5). Empty means all pages.
stampOptionsjson
Advanced options in JSON format (e.g., font, color, rotation, opacity, scaling).
Form Files
stampfile
An image or PDF file used as stamp source (required when stampSource is image or pdf).
cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form files=@/path/to/3_pdf.pdf \
--form stampSource=text \
--form stampExpression=APPROVED \
--form 'stampOptions={"opacity":0.5,"rotation":0}' \
-o my.pdf

Rotate (PDF Engines)

Rotates pages by 90, 180, or 270 degrees during post-processing.

Form Fields
rotateAngleenum
The rotation angle. Options: 90, 180, 270.
rotatePagesstring
Page ranges to rotate (e.g., 1-3, 5). Empty means all pages.
cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form files=@/path/to/3_pdf.pdf \
--form rotateAngle=90 \
-o my.pdf

PDF/A & PDF/UA (PDF Engines)

Converts to PDF/A (archival) or PDF/UA (accessibility) during post-processing using LibreOffice. Slower than native conversion (requires a second pass).

PDF/A and encryption are mutually exclusive: requesting both returns 400 Bad Request. PDF/A-1b and PDF/A-2b don't support file attachments; use PDF/A-3b if you need both.

When PDF/A runs alongside other post-processing, LibreOffice overwrites CreateDate, ModDate, and Keywords. Other metadata fields are preserved.

Form Fields
pdfaenum
Converts to a specific PDF/A archival standard. Options: PDF/A-1b, PDF/A-2b, PDF/A-3b.
pdfuaboolean
Enables PDF/UA (Universal Accessibility) compliance.
Default:false
cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form files=@/path/to/3_pdf.pdf \
--form pdfa=PDF/A-1b \
--form pdfua=true \
-o my.pdf

Encryption (PDF Engines)

Set passwords and permissions to control PDF access. The user password is required to open the PDF. The owner password grants full access and lifts the permission restrictions; when empty, it defaults to the user password.

Since 8.34.0, an owner password alone produces an owner-only PDF: it opens without a password, but the document permissions apply. Permission restrictions require a userPassword or an ownerPassword, otherwise Gotenberg returns 400 Bad Request. Restrictions are advisory: viewers honor them, but they are not cryptographically enforced once the document opens.

Encryption strength (e.g., AES-256) and permission handling depend on the active PDF engine: QPDF honors each permission individually, pdfcpu restricts all permissions if any one is denied, and PDFtk supports neither owner-only encryption nor permission restrictions. See PDF Engines module configuration.

Form Fields
userPasswordstring
The password required to open the PDF.
Default:None
ownerPasswordstring
The password granting full access; lifts the permission restrictions. Defaults to the user password.
Default:None
allowPrintingboolean
Permits printing the document.
Default:true
allowCopyingboolean
Permits extracting text and graphics.
Default:true
allowModifyingboolean
Permits changing the document content.
Default:true
allowAnnotatingboolean
Permits adding or modifying annotations.
Default:true
allowFillingFormsboolean
Permits filling in form fields.
Default:true
allowAssemblingboolean
Permits inserting, deleting, and rotating pages.
Default:true
cURL
curl \
--request POST http://localhost:3000/forms/pdfengines/merge \
--form files=@/path/to/1_pdf.pdf \
--form files=@/path/to/2_pdf.pdf \
--form files=@/path/to/3_pdf.pdf \
--form userPassword=my_user_password \
--form ownerPassword=my_owner_password \
--form allowCopying=false \
-o my.pdf

What's Next?

Break files apart with Split, or produce archival files with PDF/A & PDF/UA.