# Appending Documents

> Combine merge results, contracts and terms and conditions into one PDF or DOCX with document/append: dividers, title, author and password, curl and PHP.

- **Product:** ReportingCloud
- **Section:** Documentation
- **Kind:** Topic
- **Path:** ReportingCloud > Document Processing > Appending Documents
- **2537 words**
- **Web URL:** https://docs.textcontrol.com/reporting-cloud/document-processing/appending/
- **LLMs URL:** https://docs.textcontrol.com/reporting-cloud/document-processing/appending/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/reporting-cloud/document-processing/appending/llms-full.txt

---

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](https://docs.textcontrol.com/reporting-cloud/reference/web-api/document/append/post/llms-full.txt). 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](#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](https://docs.textcontrol.com/reporting-cloud/overview/licensing/#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](https://docs.textcontrol.com/reporting-cloud/overview/licensing/llms-full.txt) lists the quota of each plan. Every request also needs the `Authorization` header that [Authentication](https://docs.textcontrol.com/reporting-cloud/document-processing/authentication/llms-full.txt) 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*:

```bash
# 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
<?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` | .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](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/sections/llms-full.txt) 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:

```bash
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](https://docs.textcontrol.com/reporting-cloud/mail-merge/overview/#the-merge-request). 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*:

```bash
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](https://docs.textcontrol.com/reporting-cloud/mail-merge/overview/#create-one-document-or-one-per-record) describes.

### Next Steps

- [Mail Merge](https://docs.textcontrol.com/reporting-cloud/mail-merge/llms-full.txt): create the documents to combine from a template and JSON data.
- [Converting Documents](https://docs.textcontrol.com/reporting-cloud/document-processing/converting/llms-full.txt): convert a single document to another format.
- [Thumbnails](https://docs.textcontrol.com/reporting-cloud/document-processing/thumbnails/llms-full.txt): render page images of the combined document.
- [Errors and Status Codes](https://docs.textcontrol.com/reporting-cloud/document-processing/errors/llms-full.txt): what to do when a request is refused.

---

## Nearby

- **Previous:** [Converting Documents](https://docs.textcontrol.com/reporting-cloud/document-processing/converting/llms-full.txt)
- **Next:** [Find and Replace](https://docs.textcontrol.com/reporting-cloud/document-processing/find-replace/llms-full.txt)
