Appending Documents
When an invoice needs a cover letter in front of it, or a contract needs its terms and conditions after it, the documents have to become one file. POST /v1/document/append combines several documents into one in the order you list them, separates them by nothing, a new paragraph or a new section, and returns the result in the format you choose. A common use is to merge separate templates first and then append the merged documents into one.
How Appending Works
The endpoint is POST /v1/document/append. Its JSON body, the AppendBody, has two properties:
| Property | Content |
|---|---|
documents | Required. An array of AppendDocument objects in the order they are appended |
documentSettings | Optional. The document properties of the result, listed in Set the Document Properties |
Each AppendDocument has these properties:
| Property | Content |
|---|---|
document | The document as a Base64 string. Required |
documentDivider | Optional. What separates the document from the one before it: 1 nothing, 2 a new paragraph, 3 a new section. It is ignored for the first document, and a document without it, or with another number, is appended without a divider, as with 1 |
The query parameter returnFormat sets the format of the result: PDF, PDFA, RTF, DOC, DOCX, HTML, TXT or TX, in any letter case, and PDF without it. The optional parameter test=true sends a test run, which doesn't count against the quota and creates a document with a "TEST MODE" watermark; test mode isn't available with a Free account. The response is the combined document as a Base64 string in a JSON string.
The documents can be RTF, DOC, DOCX, HTML, PDF, TX, XLSX or plain text. The PHP SDK accepts files with the extensions .doc, .docx, .html, .pdf, .rtf and .tx.
A body without a documents array, or with a document that is left out or isn't Base64, is refused with 400 Bad Request; an empty array returns an empty string. Every successful request adds one document to the document quota, however many documents it combines; Plans and Licensing lists the quota of each plan. Every request also needs the Authorization header that Authentication describes.
Append Documents With curl
The following commands put cover-letter.docx in front of invoice.docx, start the invoice in a new section and save the result as invoice.pdf:
# Build the AppendBody from the two documents
jq \
first <(base64 0 cover-letter.docx) \
second <(base64 0 invoice.docx) \
'{
documents: [
{document: $first},
{document: $second, documentDivider: 3}
]
}' > append.json
curl "https://api.reporting.cloud/v1/document/append?returnFormat=PDF" \
"Authorization: ReportingCloud-APIKey $REPORTINGCLOUD_API_KEY" \
"Content-Type: application/json" \
@append.json \
appended.json
# Decode the Base64 string of the response
jq . appended.json | base64 > invoice.pdf
The commands work as follows:
- Each
--rawfilereads the Base64 string of one file into ajqvariable, and the filter writes thedocumentsarray with them to append.json. - The first document needs no divider.
documentDivider: 3on the second one inserts a new section before the invoice. jq -r .removes the quotes of the response, andbase64 -ddecodes the combined document.
Append Documents With PHP
The PHP SDK, the Composer package textcontrol/textcontrol-reportingcloud, reads the files, encodes them and decodes the result:
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use TextControl\ReportingCloud\ReportingCloud;
$reportingCloud = new ReportingCloud([
'api_key' => (string) getenv('REPORTINGCLOUD_API_KEY'),
]);
// The documents in the order they are appended, each with its divider
$documents = [
[
'filename' => __DIR__ . '/cover-letter.docx',
'divider' => ReportingCloud::DOCUMENT_DIVIDER_NONE,
],
[
'filename' => __DIR__ . '/invoice.docx',
'divider' => ReportingCloud::DOCUMENT_DIVIDER_NEW_SECTION,
],
];
// Optional document properties of the result
$documentSettings = [
'document_title' => 'Invoice',
'author' => 'Sales Department',
];
$binaryData = $reportingCloud->appendDocument(
$documents,
ReportingCloud::FILE_FORMAT_PDF,
$documentSettings
);
file_put_contents(__DIR__ . '/invoice.pdf', $binaryData);
The code works as follows:
- Each entry of
$documentsnames a file in'filename'and its divider in'divider'.appendDocumentreads and encodes the files itself. $documentSettingssets properties of the result with the keysauthor,creation_date,creator_application,document_subject,document_title,last_modification_dateanduser_password. The two dates are Unix timestamps, such as the value oftime().appendDocumentreturns the combined document as a binary string, whichfile_put_contentssaves. A refused request throws aTextControl\ReportingCloud\Exception\RuntimeException.
In the .NET SDK, the same call is AppendDocument(appendBody, ReturnFormat.PDF). Its AppendBody holds a List<AppendDocument> in Documents and a DocumentSettings object; each AppendDocument takes the file as a byte array in Document and a DocumentDivider value. A DocumentDivider that is left unset is sent as 0, which the service treats like None.
Choose the Divider
The divider decides how a document continues the one before it:
documentDivider | .NET | PHP | Effect |
|---|---|---|---|
1 | DocumentDivider.None | DOCUMENT_DIVIDER_NONE | Nothing is inserted between the documents |
2 | DocumentDivider.NewParagraph | DOCUMENT_DIVIDER_NEW_PARAGRAPH | A new paragraph is inserted between the documents |
3 | DocumentDivider.NewSection | DOCUMENT_DIVIDER_NEW_SECTION | A new section is inserted between the documents |
A section is a part of a document with its own page size, margins, orientation and headers and footers. Sections in the documentation of TX Text Control .NET Server, the component behind ReportingCloud, explains how sections divide a document.
Set the Document Properties
The optional documentSettings object sets the properties that are saved in the combined document:
| Setting | Effect |
|---|---|
author | The author |
documentTitle | The title |
documentSubject | The subject |
creatorApplication | The application that created the document |
creationDate | The creation date, as an ISO 8601 date string such as "2026-10-02T09:00:00+00:00" |
lastModificationDate | The date of the last change, in the same format |
userPassword | The password a user must enter to open the document |
Add the object next to documents in the filter of the jq command:
jq \
first <(base64 0 cover-letter.docx) \
second <(base64 0 invoice.docx) \
'{
documents: [
{document: $first},
{document: $second, documentDivider: 3}
],
documentSettings: {documentTitle: "Invoice", author: "Sales Department"}
}' > append.json
Combine Merged Documents
The documents to append often come from the merge endpoint. Merge each template with its data first, then append the results in one request. The merge endpoint answers with a JSON array of Base64 strings, and each string goes into the document property of an AppendDocument unchanged. The following commands put the cover letter in front of the first document of a merge response saved as merged.json:
jq \
letter <(base64 0 cover-letter.docx) \
merged merged.json \
'{
documents: [
{document: $letter},
{document: $merged[0][0], documentDivider: 3}
]
}' > append.json
The merge request and the append request each add one document to the quota. To combine the documents that one merge creates from several records, the merge endpoint has its own append parameter, which Create One Document or One per Record describes.
Next Steps
- Mail Merge: create the documents to combine from a template and JSON data.
- Converting Documents: convert a single document to another format.
- Thumbnails: render page images of the combined document.
- Errors and Status Codes: what to do when a request is refused.