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, providing the desired type in the type parameter:
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 |
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
SignaturesandPrinterFriendlyVersionexist only after at least one participant has signed. For a document nobody has signed yet, the API answers422with the error codeDocumentHasNoSignatures. Until the document isConcluded,Signaturesholds the signatures collected so far.- Canceled, expired or refused documents can only be downloaded as
OriginalandPrinterFriendlyVersion.
Before downloading, read the document with the Document Details API and check its hasSignature and status fields, as the samples do. See Error responses for the format of the error.
With a client library, read the document, check its status, then download Signatures and PrinterFriendlyVersion:
- C#
- Java
- Node.js
- PHP
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);
}
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);
}
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));
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. In production, learn that a document changed through webhooks instead of polling its details.
How each client reports the error, when you call it without checking first:
- C#:
GetDocumentContentAsyncthrows aSignerApiException. - Java:
getDocumentContentthrows aRestUnreachableExceptionfor any HTTP error, without the status code or the error code, so check the document first. The other methods throw aRestErrorException, whosegetStatusCode(),getErrorCode()andgetErrorMessage()give the status and theErrorModel'scodeandmessage. - Node.js: the promise is rejected with the axios error;
error.response.statushas the status anderror.response.datatheErrorModel(withresponseType: "arraybuffer", as bytes). - PHP:
getDocumentContentdoes not throw, as the warning below explains.
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# · Java · PHP · Node.js
List documents
It is possible to list documents according to different patterns and needs. The available parameters are described in the Document Listing API. 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.
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.