---
title: "Download and list documents"
description: "How to download the versions of a Signer document, such as the original, the signed file and the printer friendly version, and how to list documents."
canonical: "https://docs.lacunasoftware.com/en-us/articles/signer/integration/download-and-list"
locale: "en"
product: "Signer"
last_updated: "2026-10-22"
---

# Download and list documents

## Download document versions

Once a document has been created, it is possible to download different versions of it, according to the stage it is in. Use the [Document Download API](https://www.dropsigner.com/swagger/index.html#operations-Documents-get_api_documents__id__content), providing the desired type in the `type` parameter:

```javascript
GET /api/documents/b12cb1b2-5d6e-40b2-a050-097d068c4c11/content?type=Original
```

### Version types (`type`)

| `type` | Description |
| --- | --- |
| `Original` | Original uploaded file |
| `Signatures` | Signed file |
| `PrinterFriendlyVersion` | Printer friendly version |
| `SigningTags` | Signing tags |
| `SignatureMarks` | Signature marks |
| `OriginalWithMarks` | Original file with the signature marks |
| `SignaturesManifest` | Signatures manifest |

> **Note: Return format**
>
> There are two methods with different return types: one returns the document as a `Stream` of data and the other returns the document as a vector of `bytes[]`.

In the client libraries: 

| Client | The file (`GET .../content`) | Other forms |
| --- | --- | --- |
| C# | `GetDocumentContentAsync(id, DocumentDownloadTypes)`, a `Stream` | `GetDocumentContentBytesAsync(id, DocumentDownloadTypes)`, a `DocumentContentModel` (Base64); `GetDocumentBytesAsync(id, DocumentTicketType)`, a `byte[]` |
| Java | `getDocumentContent(id, DocumentDownloadTypes)`, an `InputStream` | `getDocumentBytes(id, DocumentTicketType)`, a `byte[]` |
| Node.js | `apiDocumentsIdContentGet(id, DocumentDownloadTypes, { responseType: "arraybuffer" })`. Without `responseType: "arraybuffer"`, axios decodes the file as text and corrupts it | `apiDocumentsIdContentB64Get(id, DocumentDownloadTypes)`, the bytes in Base64 |
| PHP | `getDocumentContent($id, DocumentDownloadTypes::...)`, the file's content | `GetDocumentContentBytesAsync($id, DocumentDownloadTypes::...)`, the `content-b64` JSON, with the bytes in Base64 |

The `DocumentDownloadTypes` of the PHP client 1.4.10 has no constant for `SignaturesManifest`. `getDocumentContent` puts the type in the query string as it gets it, so pass the text `"SignaturesManifest"`. 

### When a version cannot be downloaded

* `Signatures` and `PrinterFriendlyVersion` exist only after at least one participant has signed. For a document nobody has signed yet, the API answers `422` with the error code `DocumentHasNoSignatures`. Until the document is `Concluded`, `Signatures` holds the signatures collected so far.
* Canceled, expired or refused documents can only be downloaded as `Original` and `PrinterFriendlyVersion`.

Before downloading, read the document with the [Document Details API](https://www.dropsigner.com/swagger/index.html#operations-Documents-get_api_documents__id_) and check its `hasSignature` and `status` fields, as the samples do. See [Error responses](https://docs.lacunasoftware.com/en-us/articles/signer/integration-guide#error-responses) for the format of the error.

With a client library, read the document, check its status, then download `Signatures` and `PrinterFriendlyVersion`: 

**C#**

```csharp
using Lacuna.Signer.Api;

// 1. Check the status
var details = await signerClient.GetDocumentDetailsAsync(documentId);
// details.IsConcluded: the whole flow is done
// details.FlowActions[i].Status == ActionStatus.Completed: that participant is done
if (!details.HasSignature)
{
    return; // only Original can be downloaded yet
}

// 2. Signatures: the signed file. Not for canceled, refused or expired documents.
if (details.Status != DocumentStatus.Canceled && details.Status != DocumentStatus.Refused && details.Status != DocumentStatus.Expired)
{
    using (var signed = await signerClient.GetDocumentContentAsync(documentId, DocumentDownloadTypes.Signatures))
    using (var fileStream = new FileStream("signed.pdf", FileMode.Create, FileAccess.Write))
    {
        signed.CopyTo(fileStream);
    }
}

// 3. PrinterFriendlyVersion: a PDF with the signature marks and a signatures manifest
using (var printerFriendly = await signerClient.GetDocumentContentAsync(documentId, DocumentDownloadTypes.PrinterFriendlyVersion))
using (var fileStream = new FileStream("printer-friendly.pdf", FileMode.Create, FileAccess.Write))
{
    printerFriendly.CopyTo(fileStream);
}
```

**Java**

```java
import com.lacunasoftware.signer.ActionStatus;
import com.lacunasoftware.signer.DocumentDownloadTypes;
import com.lacunasoftware.signer.DocumentStatus;
import com.lacunasoftware.signer.documents.DocumentModel;

// 1. Check the status
DocumentModel details = signerClient.getDocumentDetails(documentId);
// details.isIsConcluded(): the whole flow is done
// details.getFlowActions().get(i).getStatus() == ActionStatus.COMPLETED: that participant is done
if (!Boolean.TRUE.equals(details.isHasSignature())) {
    return; // only Original can be downloaded yet
}

// 2. Signatures: the signed file. Not for canceled, refused or expired documents.
DocumentStatus status = details.getStatus();
if (status != DocumentStatus.CANCELED && status != DocumentStatus.REFUSED && status != DocumentStatus.EXPIRED) {
    try (InputStream signed = signerClient.getDocumentContent(documentId, DocumentDownloadTypes.SIGNATURES)) {
        Files.copy(signed, Paths.get("signed.pdf"), StandardCopyOption.REPLACE_EXISTING);
    }
}

// 3. PrinterFriendlyVersion: a PDF with the signature marks and a signatures manifest
try (InputStream printerFriendly = signerClient.getDocumentContent(documentId, DocumentDownloadTypes.PRINTERFRIENDLYVERSION)) {
    Files.copy(printerFriendly, Paths.get("printer-friendly.pdf"), StandardCopyOption.REPLACE_EXISTING);
}
```

**Node.js**

```ts
import * as fs from "fs";
import { ActionStatus, DocumentDownloadTypes, DocumentStatus } from "signer-node-client";

// 1. Check the status
const details = (await documentsApi.apiDocumentsIdGet(documentId)).data;
// details.isConcluded: the whole flow is done
// details.flowActions[i].status === ActionStatus.Completed: that participant is done
if (!details.hasSignature) {
  return; // only Original can be downloaded yet
}

// 2. Signatures: the signed file. Not for canceled, refused or expired documents.
if (
  details.status !== DocumentStatus.Canceled &&
  details.status !== DocumentStatus.Refused &&
  details.status !== DocumentStatus.Expired
) {
  // responseType "arraybuffer": without it, axios decodes the file as text and corrupts it
  const signed = await documentsApi.apiDocumentsIdContentGet(documentId, DocumentDownloadTypes.Signatures, {
    responseType: "arraybuffer",
  });
  fs.writeFileSync("signed.pdf", Buffer.from(signed.data as unknown as ArrayBuffer));
}

// 3. PrinterFriendlyVersion: a PDF with the signature marks and a signatures manifest
const printerFriendly = await documentsApi.apiDocumentsIdContentGet(documentId, DocumentDownloadTypes.PrinterFriendlyVersion, {
  responseType: "arraybuffer",
});
fs.writeFileSync("printer-friendly.pdf", Buffer.from(printerFriendly.data as unknown as ArrayBuffer));
```

**PHP**

```php
use Lacuna\Signer\Model\ActionStatus;
use Lacuna\Signer\Model\DocumentDownloadTypes;
use Lacuna\Signer\Model\DocumentsDocumentModel;
use Lacuna\Signer\Model\DocumentStatus;

// 1. Check the status
$details = new DocumentsDocumentModel($signerClient->getDocumentDetails($documentId));
// $details->getIsConcluded(): the whole flow is done
// $details->getFlowActions()[$i]["status"] == ActionStatus::COMPLETED: that participant is done
if (!$details->getHasSignature()) {
    return; // only Original can be downloaded yet; getDocumentContent would return the error JSON
}

// 2. Signatures: the signed file. Not for canceled, refused or expired documents.
if (!in_array($details->getStatus(), array(DocumentStatus::CANCELED, DocumentStatus::REFUSED, DocumentStatus::EXPIRED))) {
    $signed = $signerClient->getDocumentContent($documentId, DocumentDownloadTypes::SIGNATURES);
    file_put_contents("signed.pdf", $signed);
}

// 3. PrinterFriendlyVersion: a PDF with the signature marks and a signatures manifest
$printerFriendly = $signerClient->getDocumentContent($documentId, DocumentDownloadTypes::PRINTER_FRIENDLY_VERSION);
file_put_contents("printer-friendly.pdf", $printerFriendly);
```

The client is created as in [Create documents](https://docs.lacunasoftware.com/en-us/articles/signer/integration/documents#step-by-step). In production, learn that a document changed through [webhooks](https://docs.lacunasoftware.com/en-us/articles/signer/webhooks) instead of polling its details.

How each client reports the error, when you call it without checking first: 

* **C#**: `GetDocumentContentAsync` throws a `SignerApiException`.
* **Java**: `getDocumentContent` throws a `RestUnreachableException` for any HTTP error, without the status code or the error code, so check the document first. The other methods throw a `RestErrorException`, whose `getStatusCode()`, `getErrorCode()` and `getErrorMessage()` give the status and the `ErrorModel`'s `code` and `message`.
* **Node.js**: the promise is rejected with the axios error; `error.response.status` has the status and `error.response.data` the `ErrorModel` (with `responseType: "arraybuffer"`, as bytes).
* **PHP**: `getDocumentContent` does not throw, as the warning below explains.

> **Warning: PHP client**
>
> In the PHP client, `getDocumentContent` does not throw an exception on HTTP errors: it returns the body of the error response, the `ErrorModel` JSON, as if it were the file. Saved without a check, it becomes a "file" that holds the error. Check the document first, as above.

**Examples on GitHub:** [C#](https://github.com/LacunaSoftware/SignerSamples/blob/master/dotnet/console/Console/Scenarios/DownloadDocumentVersionScenario.cs) · [Java](https://github.com/LacunaSoftware/SignerSamples/blob/master/java/console/src/main/java/com/lacunasoftware/signer/sample/scenarios/DownloadDocumentVersionScenario.java) · [PHP](https://github.com/LacunaSoftware/SignerSamples/blob/master/php/Scenarios/DownloadDocumentVersionScenario.php) · [Node.js](https://github.com/LacunaSoftware/SignerSamples/blob/master/nodejs/scenarios/downloadDocumentVersionScenario.ts)

## List documents

It is possible to list documents according to different patterns and needs. The available parameters are described in the [Document Listing API](https://www.dropsigner.com/swagger/index.html#operations-Documents-get_api_documents). Among the possible listing types:

* Pending documents for a particular participant
* Concluded documents
* List by folders
* List by organizations

### Filter by status

The `status` parameter filters by document status: `Pending`, `Paused`, `Concluded`, `Refused`, `Canceled`, `Expired`, `PendingOrRefused` or `CanceledOrExpired`. The `Pending` value also returns paused documents. To list only paused documents, use `Paused`.

### Filter by participant

To list a participant's documents, send two parameters together:

| Parameter | Description |
| --- | --- |
| `participantQ` | The text to search for, for example the participant's CPF or e-mail. |
| `participantQueryType` | Where to search for the text: `Name`, `Identifier`, `Email` or `Phone`. |

Without both parameters, the Signer does not filter by participant. Available from version 3.0.0, the `participantSituation` parameter filters by the situation of the participant's action:

| Value | Documents listed |
| --- | --- |
| `Pending` | The participant still has to act, and the document is in progress or paused. |
| `Completed` | The participant has already acted: signed, approved or refused. |

Without `participantSituation`, the list returns every situation.

```javascript
GET /api/documents?participantQ=81976153069&participantQueryType=Identifier&participantSituation=Pending
```

### Folders

To list the folders of the API key's organization, use `GET /api/folders`. The `q` parameter searches by name, and `parentId` with `filterByParent=true` lists a folder's subfolders. To list the documents in a folder, use the `folderId` parameter of `GET /api/documents`.

The web interface uses `GET /api/folders/search` to search the folders the user can access in all organizations. This route does not accept an API key.

**Examples on GitHub:** [C#](https://github.com/LacunaSoftware/SignerSamples/blob/master/dotnet/console/Console/Scenarios/ListDocumentsScenario.cs) · [Java](https://github.com/LacunaSoftware/SignerSamples/blob/master/java/console/src/main/java/com/lacunasoftware/signer/sample/scenarios/ListDocumentsScenario.java) · [PHP](https://github.com/LacunaSoftware/SignerSamples/blob/master/php/Scenarios/ListDocumentsScenario.php) · [Node.js](https://github.com/LacunaSoftware/SignerSamples/blob/master/nodejs/scenarios/listDocumentScenario.ts)

**Demos and samples**

- List documents: [.NET](https://github.com/LacunaSoftware/SignerSamples/blob/master/dotnet/console/Console/Scenarios/ListDocumentsScenario.cs) · [Java](https://github.com/LacunaSoftware/SignerSamples/blob/master/java/console/src/main/java/com/lacunasoftware/signer/sample/scenarios/ListDocumentsScenario.java) · [Node.js](https://github.com/LacunaSoftware/SignerSamples/blob/master/nodejs/scenarios/listDocumentsScenario.ts) · [PHP](https://github.com/LacunaSoftware/SignerSamples/blob/master/php/Scenarios/ListDocumentsScenario.php)
- Download a document's versions: [.NET](https://github.com/LacunaSoftware/SignerSamples/blob/master/dotnet/console/Console/Scenarios/DownloadDocumentVersionScenario.cs) · [Java](https://github.com/LacunaSoftware/SignerSamples/blob/master/java/console/src/main/java/com/lacunasoftware/signer/sample/scenarios/DownloadDocumentVersionScenario.java) · [Node.js](https://github.com/LacunaSoftware/SignerSamples/blob/master/nodejs/scenarios/downloadDocumentVersionScenario.ts) · [PHP](https://github.com/LacunaSoftware/SignerSamples/blob/master/php/Scenarios/DownloadDocumentVersionScenario.php)
