ReportingCloud

Appending Documents

Topic

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:

PropertyContent
documentsRequired. An array of AppendDocument objects in the order they are appended
documentSettingsOptional. The document properties of the result, listed in Set the Document Properties

Each AppendDocument has these properties:

PropertyContent
documentThe document as a Base64 string. Required
documentDividerOptional. 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 -n \
  --rawfile first <(base64 -w 0 cover-letter.docx) \
  --rawfile second <(base64 -w 0 invoice.docx) \
  '{
    documents: [
      {document: $first},
      {document: $second, documentDivider: 3}
    ]
  }' > append.json

curl "https://api.reporting.cloud/v1/document/append?returnFormat=PDF" \
  -H "Authorization: ReportingCloud-APIKey $REPORTINGCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @append.json \
  --output appended.json

# Decode the Base64 string of the response
jq -r . appended.json | base64 -d > invoice.pdf

The commands work as follows:

  • Each --rawfile reads the Base64 string of one file into a jq variable, and the filter writes the documents array with them to append.json.
  • The first document needs no divider. documentDivider: 3 on the second one inserts a new section before the invoice.
  • jq -r . removes the quotes of the response, and base64 -d decodes 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:

<?php
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 $documents names a file in 'filename' and its divider in 'divider'. appendDocument reads and encodes the files itself.
  • $documentSettings sets properties of the result with the keys author, creation_date, creator_application, document_subject, document_title, last_modification_date and user_password. The two dates are Unix timestamps, such as the value of time().
  • appendDocument returns the combined document as a binary string, which file_put_contents saves. A refused request throws a TextControl\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.NETPHPEffect
1DocumentDivider.NoneDOCUMENT_DIVIDER_NONENothing is inserted between the documents
2DocumentDivider.NewParagraphDOCUMENT_DIVIDER_NEW_PARAGRAPHA new paragraph is inserted between the documents
3DocumentDivider.NewSectionDOCUMENT_DIVIDER_NEW_SECTIONA 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:

SettingEffect
authorThe author
documentTitleThe title
documentSubjectThe subject
creatorApplicationThe application that created the document
creationDateThe creation date, as an ISO 8601 date string such as "2026-10-02T09:00:00+00:00"
lastModificationDateThe date of the last change, in the same format
userPasswordThe password a user must enter to open the document

Add the object next to documents in the filter of the jq command:

jq -n \
  --rawfile first <(base64 -w 0 cover-letter.docx) \
  --rawfile second <(base64 -w 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 -n \
  --rawfile letter <(base64 -w 0 cover-letter.docx) \
  --slurpfile 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