Referência rápida do Signer
O essencial do Lacuna Signer em uma página, com links para as páginas completas.
Para que serve
O Lacuna Signer é um gerenciador de documentos e assinaturas: sua aplicação faz upload de um arquivo e define um fluxo de participantes (signatários e aprovadores, em sequência ou em paralelo), e o Signer os notifica, coleta as assinaturas digitais ou eletrônicas deles e devolve o arquivo assinado. Ele funciona como SaaS em dropsigner.com ou como uma instância on-premises própria, e você integra pela API REST, pelas bibliotecas cliente usadas nos exemplos e por um widget de assinatura que pode ser embutido na sua página.
| Prefira | Quando |
|---|---|
| Rest PKI Core | Você quer assinaturas dentro da sua própria aplicação, sem um fluxo de documentos: sua aplicação cria uma sessão, envia o usuário para uma página hospedada pelo Rest PKI Core e recebe o resultado de volta. Ele também valida assinaturas, aplica carimbos de tempo e verifica identidade biométrica. |
| PKI SDK | Sua aplicação é .NET e precisa criar ou validar assinaturas CMS/CAdES, PDF/PAdES ou XmlDSig/XAdES, ou validar certificados X.509, no próprio código. |
| PKI Express | Você precisa desses mesmos formatos de assinatura em praticamente qualquer linguagem, no seu próprio servidor Windows ou Linux, com um arquivo de licença. |
| Web PKI | Sua página web precisa acessar os certificados do usuário por JavaScript (listá-los, lê-los, assinar dados) enquanto um SDK no servidor monta a assinatura. O próprio widget do Signer já depende de uma licença do Web PKI para o seu domínio. |
| Bulk Signer | Os arquivos precisam ser assinados em lote na sua própria infraestrutura, com um certificado mantido pelo servidor, sem uma pessoa no processo. O Bulk Signer também pode encaminhar ao Signer os arquivos de um perfil de assinatura quando uma pessoa precisa assinar. |
Instalação
| Linguagem | Pacote | Versão major atual | Registro |
|---|---|---|---|
| .NET | Lacuna.Signer.Client | 1 (1.24.0) | NuGet |
| Java | com.lacunasoftware.signer:signer-client | 2 (2.14.0) | Maven Central |
| Node.js | signer-node-client | 1 (1.1.0) | npm |
| PHP | lacuna/signer-client | 1 (1.4.10) | Packagist |
| JavaScript (navegador), assinatura embutida | lacuna-signer-widget | 0 (0.8.0) | Script na CDN, npm |
Instale com dotnet add package Lacuna.Signer.Client, Gradle implementation 'com.lacunasoftware.signer:signer-client:2.14.0', npm install signer-node-client ou composer require lacuna/signer-client. Veja Bibliotecas cliente.
Os exemplos fixam 1.17.0 (.NET), 2.13.0 (Java), ^1.1.0 (Node.js) e 1.4.10 (PHP), todas na versão major atual. Sem uma biblioteca cliente, chame a API REST diretamente, como fazem os Primeiros passos com curl.
Configuração e autenticação
- Chave de API. Toda requisição leva a chave no header
X-Api-Key, no formatonome-da-aplicacao|<chave>. Se você tem permissão para criar aplicações na sua organização, crie uma em Aplicações e gere a chave com o botão Chaves dela; caso contrário, peça uma chave ao administrador da sua instância. A chave é exibida uma única vez e só enxerga e cria documentos daquela organização. Veja Primeiros passos e o Guia de Integração. - Chaves de administração e de monitoramento. As APIs de administração (usuários e organizações) exigem uma chave de uma aplicação da organização
Sys Admin; as APIs de monitoramento de consumo exigem uma com o papelMonitor. Veja APIs de Administração e Monitoramento de consumo. - Referência da API. Swagger e a cópia do OpenAPI em https://docs.lacunasoftware.com/openapi/signer.json, com o título "Dropsigner", versão 2.16.1.
| Ambiente | URL base |
|---|---|
| Produção, em nuvem (Dropsigner SaaS) | https://www.dropsigner.com |
| Produção, on-premises | A URL da sua instância, como https://signer.suaempresa.com.br |
| Sandbox, para desenvolvimento e testes | https://signer-lac.azurewebsites.net |
O cliente recebe a URL da instância e a chave de API; troque as duas juntas ao passar para produção.
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): as páginas da documentação do Signer não mencionam o sandbox (só os exemplos o mencionam), nem como obter uma organização e uma chave de API nele.
Operações principais
Os blocos abaixo são em PHP, a linguagem com mais exemplos no catálogo; Experimente traz as mesmas tarefas em C#, Java e Node.js, quando existem.
Fazer upload de um arquivo
Envie o arquivo primeiro; o id de upload da resposta é o que o documento referencia. POST /api/uploads/bytes recebe os bytes em Base64, em JSON (o mais simples), e POST /api/uploads recebe multipart/form-data (melhor para arquivos grandes); em PHP, uploadFile. Veja Criar documentos.
$filePath = "resources/sample.pdf";
$fileName = basename($filePath);
$file = fopen($filePath, "r");
$uploadModel = new UploadModel($this->signerClient->uploadFile($fileName, $file, "application/pdf"));
fclose($file);
Criar um documento com um fluxo
Crie o documento a partir do upload e defina quem age e em que ordem: cada item de flowActions tem um type (Signer, Approver, SignRule ou ApproverRule), um step (ações no mesmo step acontecem em paralelo, e um step maior acontece depois) e um user (name, identifier, email). POST /api/documents; em PHP, createDocument. A resposta traz um par uploadId e documentId por arquivo. O Signer envia e-mail aos participantes do primeiro passo imediatamente, a menos que você envie disablePendingActionNotifications: true. Veja Criar documentos.
$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];
Obter a URL de assinatura (assinatura embutida)
Para que o participante assine dentro da sua aplicação em vez de pelo e-mail, peça a URL de ação dele, identificando o participante por identifier (a documentação também envia emailAddress): POST /api/documents/{id}/action-url; em PHP, getActionUrl. A resposta traz url, para redirecionar o usuário à página de assinatura do Signer, e embedUrl, que você passa ao método render do LacunaSignerWidget na sua página. Combine com disablePendingActionNotifications: true na criação. O widget exige que o Signer esteja configurado com uma licença do Web PKI para o domínio da sua página. Veja Assinatura Embutida.
$actionUrlRequest = new DocumentsActionUrlRequest();
$actionUrlRequest->setIdentifier($user->getIdentifier());
$actionUrlResponse = new DocumentsActionUrlResponse($this->signerClient->getActionUrl($docResult->getDocumentId(), $actionUrlRequest));
echo ($actionUrlResponse->getEmbedUrl());
Verificar o status do documento
GET /api/documents/{id}; em PHP, getDocumentDetails. O documento está finalizado quando status é Concluded (os demais são Pending, FlowConcluded, Refused, Canceled e Expired), e um participante concluiu a ação dele quando o item correspondente em flowActions tem status Completed. Em produção, use webhooks em vez de polling. Veja Acompanhar documentos.
$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";
}
}
Baixar o arquivo assinado
GET /api/documents/{id}/content?type=...; em PHP, getDocumentContent. Signatures é o arquivo assinado (o PDF com as assinaturas embutidas, ou um .p7s para CAdES), PrinterFriendlyVersion é um PDF com o manifesto de assinaturas e Original é o arquivo enviado. Documentos cancelados, expirados ou recusados só podem ser baixados como Original e PrinterFriendlyVersion, e um documento que ninguém assinou ainda não tem Signatures nem PrinterFriendlyVersion: verifique hasSignature e status antes. GET /api/documents/{id}/content-b64 devolve os bytes em Base64, em JSON. Veja Baixar e listar.
$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);
Receber webhooks
Cadastre a sua URL na tela da Organização, seção Integração, ou com POST /api/organizations/{orgId}/webhooks (url, authType, token). O Signer passa então a enviar um POST com { "type": "...", "data": { ... } } para DocumentsCreated, DocumentSigned, DocumentApproved, DocumentRefused, DocumentConcluded, DocumentCanceled, DocumentExpired e DocumentDeleted. Ele não assina o corpo: verifique o header que você escolheu, como Authorization: Bearer <token>, responda 2xx imediatamente (qualquer outra resposta gera novas tentativas, por padrão até 10), torne o processamento idempotente e consulte o documento de novo, porque os eventos podem chegar fora de ordem. Veja 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;
}
}
Cancelar, recusar ou excluir um documento
POST /api/documents/{id}/cancellation e POST /api/documents/{id}/refusal, cada um com um reason, e DELETE /api/documents/{id}; em PHP, cancelDocument, refuseDocument e deleteDocument. Chame a operação de que você precisa. Veja Ciclo de vida do documento.
$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);
Erros
A API responde com um código de status HTTP: 400 para um erro de sintaxe, como um campo obrigatório que não foi enviado; 401 quando a chave de API não foi enviada ou é inválida; 403 quando a chave é válida, mas a aplicação não tem permissão para a operação; e 422 para um erro da API, cujo corpo JSON (ErrorModel) tem code, message e details. Um id de entidade que não é encontrado também gera 422. Teste o code, e não a mensagem. Veja Respostas de erro.
Os primeiros que um integrador encontra:
401ou403na primeira chamada: a chave é inválida, foi revogada ou pertence a outra organização.DocumentNotFoundeInvalidFlowAction, entre os principais códigos que a referência da API lista (a lista não é completa, e novos códigos podem surgir).DocumentHasNoSignatures(422): baixarSignaturesouPrinterFriendlyVersionde um documento que ninguém assinou ainda.MaxWebhooksLimitReached(mais webhooks que o limiteMaxWebhooksPerOrganization, 5 por padrão) eOperationNotSupportedInPersonalOrganizationsao cadastrar um webhook.
Em PHP, as chamadas do cliente lançam uma exceção cuja mensagem traz a exceção interna inteira, mas getDocumentContent não lança exceção em erros HTTP: ele devolve o corpo JSON do erro, então verifique os detalhes do documento antes de salvar o conteúdo como arquivo.
Certificados e credenciais de teste
- Certificados de teste: a ICP de testes da Lacuna Software. O cenário de assinatura pública dos exemplos assina no sandbox com um deles, o certificado do CPF 56072386105.
- Sandbox
https://signer-lac.azurewebsites.net: os exemplos rodam nele com uma chave de exemplo compartilhada; para usar a sua própria organização, troque-a por uma chave sua. Cada execução cria documentos reais e o Signer envia e-mail aos participantes, por isso os exemplos usam endereços@mailinator.comfictícios. - Assinatura em nuvem no widget: o exemplo online usa o certificado de teste do BirdID (CPF 026.102.470-10), pareado por um QR code no aplicativo BirdID. Veja Assinatura Embutida.
- Por padrão, uma instância do Signer aceita só certificados ICP-Brasil; a página TrustArbitrator mostra como aceitar outras ACs raiz.
- TODO(docs): quais certificados de teste o Dropsigner e o sandbox aceitam.
Experimente
Demonstrações online e os exemplos de cada linguagem: