Pular para o conteúdo principal

Signer quick reference

The essentials of Lacuna Signer on one page, with links to the full pages.

What it is for​

Lacuna Signer is a document and signature manager: your application uploads a file and defines a flow of participants (signers and approvers, in sequence or in parallel), and Signer notifies them, collects their digital or electronic signatures and returns the signed file. It runs as SaaS at dropsigner.com or as your own on-premises instance, and you integrate through its REST API, the client libraries used by the samples and an embeddable signing widget.

Choose insteadWhen
Rest PKI CoreYou want signatures inside your own application, without a document workflow: your application creates a session, sends the user to a page Rest PKI Core hosts and gets the result back. It also validates signatures, adds timestamps and checks biometric identity.
PKI SDKYour application is in .NET and must create or validate CMS/CAdES, PDF/PAdES or XmlDSig/XAdES signatures, or validate X.509 certificates, in its own code.
PKI ExpressYou need those same signature formats from virtually any language, on your own Windows or Linux server, with a license file.
Web PKIYour web page must reach the user's certificates from JavaScript (list them, read them, sign data) while a server-side SDK assembles the signature. Signer's own widget already relies on a Web PKI license for your domain.
Bulk SignerFiles must be signed in bulk on your own infrastructure with a certificate the server holds, with no person in the loop. Bulk Signer can also hand a profile's files to Signer when a person must sign.

Install​

LanguagePackageCurrent majorRegistry
.NETLacuna.Signer.Client1 (1.24.0)NuGet
Javacom.lacunasoftware.signer:signer-client2 (2.14.0)Maven Central
Node.jssigner-node-client1 (1.1.0)npm
PHPlacuna/signer-client1 (1.4.10)Packagist
JavaScript (browser), embedded signaturelacuna-signer-widget0 (0.8.0)CDN script, npm

Install with dotnet add package Lacuna.Signer.Client, Gradle implementation 'com.lacunasoftware.signer:signer-client:2.14.0', npm install signer-node-client or composer require lacuna/signer-client. See Client libraries.

The samples pin 1.17.0 (.NET), 2.13.0 (Java), ^1.1.0 (Node.js) and 1.4.10 (PHP), all on the current major. Without a client library, call the REST API directly, as Getting started does with curl.

Configuration and authentication​

  • API key. Every request carries the key in the X-Api-Key header, in the format application-name|<key>. If you have permission to create applications in your organization, create one under Applications and generate the key with its Keys button; otherwise, ask your instance administrator for a key. The key is shown only once, and it only sees and creates documents of that organization. See Getting started and the Integration Guide.
  • Administration and monitoring keys. The administration APIs (users and organizations) need a key from an application of the Sys Admin organization; the consumption monitoring APIs need one with the Monitor role. See Administration APIs and Consumption Monitoring.
  • API reference. Swagger, and the OpenAPI copy at https://docs.lacunasoftware.com/openapi/signer.json, titled "Dropsigner", version 2.16.1.
EnvironmentBase URL
Production, cloud (Dropsigner SaaS)https://www.dropsigner.com
Production, on-premisesYour instance URL, such as https://signer.yourcompany.com
Sandbox, for development and testshttps://signer-lac.azurewebsites.net

The client takes the instance URL and the API key; change both together when you move to production.

use Lacuna\Signer\PhpClient\SignerClient;
$this->endPoint = "https://signer-lac.azurewebsites.net/";
$this->apiKey = "your-application|0000000000000000000000000000000000000000000000000000000000000000";
$this->signerClient = new SignerClient($this->endPoint, $this->apiKey);

TODO(docs): the Signer docs pages do not mention the sandbox (only the samples do), nor how to get an organization and an API key on it.

Core operations​

The blocks below are PHP, the language with the most samples in the catalog; Try it links the same tasks in C#, Java and Node.js where they exist.

Upload a file​

Send the file first; the upload id in the response is what the document refers to. POST /api/uploads/bytes takes the bytes in Base64, in JSON (the simplest), and POST /api/uploads takes multipart/form-data (better for large files); PHP uploadFile. See Create documents.

$filePath = "resources/sample.pdf";
$fileName = basename($filePath);
$file = fopen($filePath, "r");
$uploadModel = new UploadModel($this->signerClient->uploadFile($fileName, $file, "application/pdf"));
fclose($file);

Create a document with a flow​

Create the document from the upload and say who acts and in which order: each flowActions item has a type (Signer, Approver, SignRule or ApproverRule), a step (the same step runs in parallel, a higher step runs later) and a user (name, identifier, email). POST /api/documents; PHP createDocument. The response has one uploadId and documentId pair per file. Signer e-mails the participants of the first step at once, unless you send disablePendingActionNotifications: true. See Create documents.

$fileUploadModelBuilder = new FileUploadBuilder($uploadModel);
$fileUploadModelBuilder->setDisplayName("One Signer Sample");
$user = new UsersParticipantUserModel();
$user->setName("Jack Bauer");
$user->setEmail("jack.bauer@mailinator.com");
$user->setIdentifier("75502846369");
$flowActionCreateModel = new FlowActionsFlowActionCreateModel();
$flowActionCreateModel->setType(FlowActionType::SIGNER);
$flowActionCreateModel->setUser($user);
$documentRequest = new DocumentsCreateDocumentRequest();
$documentRequest->setFiles(array($fileUploadModelBuilder->toModel()));
$documentRequest->setFlowActions(array($flowActionCreateModel));
$docResult = $this->signerClient->createDocument($documentRequest)[0];

Get the signing URL (embedded signature)​

To have the participant sign inside your application instead of through the e-mail, ask for their action URL, identifying the participant by identifier (the docs also send emailAddress): POST /api/documents/{id}/action-url; PHP getActionUrl. The response has url, to redirect the user to Signer's signing page, and embedUrl, which you pass to the render method of LacunaSignerWidget on your page. Combine it with disablePendingActionNotifications: true on creation. The widget needs Signer to be configured with a Web PKI license for your page's domain. See Embedded signature.

$actionUrlRequest = new DocumentsActionUrlRequest();
$actionUrlRequest->setIdentifier($user->getIdentifier());
$actionUrlResponse = new DocumentsActionUrlResponse($this->signerClient->getActionUrl($docResult->getDocumentId(), $actionUrlRequest));
echo ($actionUrlResponse->getEmbedUrl());

Check the document status​

GET /api/documents/{id}; PHP getDocumentDetails. The document is finished when status is Concluded (the others are Pending, FlowConcluded, Refused, Canceled and Expired), and a participant is done when their flowActions item has status Completed. In production, use webhooks instead of polling. See Track documents.

$documentDetails = new DocumentsDocumentModel($this->signerClient->getDocumentDetails($document->getDocumentId()));
if ($documentDetails->getIsConcluded()) {
echo "Document concluded";
}
foreach ($documentDetails->getFlowActions() as $item) {
if ($item["status"] == ActionStatus::PENDING) {
$user = new UsersParticipantUserModel($item["user"]);
echo "The status of User " . $user->getName() . " is Pending\n";
}
}

Download the signed file​

GET /api/documents/{id}/content?type=...; PHP getDocumentContent. Signatures is the signed file (the PDF with the signatures embedded, or a .p7s for CAdES), PrinterFriendlyVersion a PDF with the signatures manifest and Original the uploaded file. Canceled, expired or refused documents download only as Original and PrinterFriendlyVersion, and a document nobody has signed yet has no Signatures or PrinterFriendlyVersion: check hasSignature and status first. GET /api/documents/{id}/content-b64 returns the bytes in Base64, in JSON. See Download and list.

$signed = $this->signerClient->getDocumentContent($documentId, DocumentDownloadTypes::SIGNATURES);
file_put_contents("./resources/downloaded-signed.pdf", $signed);
$printerFriendly = $this->signerClient->getDocumentContent($documentId, DocumentDownloadTypes::PRINTER_FRIENDLY_VERSION);
file_put_contents("./resources/downloaded-printer-friendly.pdf", $printerFriendly);

Receive webhooks​

Register your URL under Organization, Integration, or with POST /api/organizations/{orgId}/webhooks (url, authType, token). Signer then posts { "type": "...", "data": { ... } } for DocumentsCreated, DocumentSigned, DocumentApproved, DocumentRefused, DocumentConcluded, DocumentCanceled, DocumentExpired and DocumentDeleted. It does not sign the body: check the header you chose, such as Authorization: Bearer <token>, answer 2xx at once (any other answer is retried, by default up to 10 attempts), make processing idempotent and read the document back, because events can arrive out of order. See Webhooks.

if (!hash_equals(self::EXPECTED_AUTHORIZATION, $_SERVER['HTTP_AUTHORIZATION'] ?? '')) {
http_response_code(401);
return;
}
$body = file_get_contents('php://input');
$webhook = json_decode($body, true);
if (($webhook['type'] ?? null) === WebhookTypes::DOCUMENT_CONCLUDED) {
$document = new DocumentsDocumentModel($this->signerClient->getDocumentDetails($webhook['data']['id']));
if ($document->getStatus() !== DocumentStatus::CONCLUDED) {
return;
}
}

Cancel, refuse or delete a document​

POST /api/documents/{id}/cancellation and POST /api/documents/{id}/refusal, each with a reason, and DELETE /api/documents/{id}; PHP cancelDocument, refuseDocument and deleteDocument. Call the one you need. See Document lifecycle.

$cancelDocument = new DocumentsCancelDocumentRequest();
$cancelDocument->setReason("This is a document cancellation");
$this->signerClient->cancelDocument($documentId, $cancelDocument);
$refuseDocument = new RefusalRefusalRequest();
$refuseDocument->setReason("This is a document refusal");
$this->signerClient->refuseDocument($documentId, $refuseDocument);
$this->signerClient->deleteDocument($documentId);

Errors​

The API answers with an HTTP status code: 400 for a syntax error, such as a required field that was not sent; 401 when the API key was not sent or is invalid; 403 when the key is valid but the application lacks permission for the operation; and 422 for an API error, whose JSON body (ErrorModel) has code, message and details. An entity id that is not found is also a 422. Test code, not the message. See Error responses.

The ones an integrator hits first:

  • 401 or 403 on the first call: the key is invalid, revoked or belongs to another organization.
  • DocumentNotFound and InvalidFlowAction, among the main codes the API reference lists (the list is not comprehensive, and new codes may appear).
  • DocumentHasNoSignatures (422): downloading Signatures or PrinterFriendlyVersion of a document nobody has signed yet.
  • MaxWebhooksLimitReached (more webhooks than MaxWebhooksPerOrganization, 5 by default) and OperationNotSupportedInPersonalOrganizations when registering a webhook.

In PHP, client calls throw an exception whose message carries the whole inner exception, but getDocumentContent does not throw on HTTP errors: it returns the JSON error body, so check the document's details before saving the content as a file.

Test certificates and credentials​

  • Test certificates: the Lacuna Test PKI. The samples' public signature scenario signs on the sandbox with one of them, the certificate for CPF 56072386105.
  • Sandbox https://signer-lac.azurewebsites.net: the samples run there with a shared sample key; to use your own organization, replace it with a key of yours. Every run creates real documents and Signer e-mails the participants, so the samples use made-up @mailinator.com addresses.
  • Cloud signing in the widget: the online example uses the BirdID test certificate (CPF 026.102.470-10), paired by a QR code in the BirdID app. See Embedded signature.
  • By default, a Signer instance accepts only ICP-Brasil certificates; TrustArbitrator shows how to accept other roots.
  • TODO(docs): which test certificates Dropsigner and the sandbox accept.

Try it​

Live demos and the samples for each language:

View as Markdown