Criar documentos
Criar um documento no Signer envolve dois passos: enviar o arquivo (upload) e criar o documento definindo o fluxo de participantes. A partir daí, o Signer notifica automaticamente os participantes na ordem definida.
Passo a passo
Cada passo mostra a chamada REST e a mesma chamada pelas bibliotecas cliente de C#, Java, Node.js e PHP. O código vem dos cenários do SignerSamples, reduzido à chamada. Crie o cliente uma vez, com a URL da sua instância do Signer e a chave de API:
- C#
- Java
- Node.js
- PHP
using Lacuna.Signer.Client;
// URL do sandbox; em produção, https://www.dropsigner.com ou a sua instância on-premises
var signerClient = new SignerClient("https://signer-lac.azurewebsites.net", "your-application|0000000000000000000000000000000000000000000000000000000000000000");
import com.lacunasoftware.signer.javaclient.SignerClient;
// URL do sandbox; em produção, https://www.dropsigner.com ou a sua instância on-premises
// (este construtor lança URISyntaxException)
SignerClient signerClient = new SignerClient("https://signer-lac.azurewebsites.net", "your-application|0000000000000000000000000000000000000000000000000000000000000000");
import { Configuration, DocumentsApi, UploadApi } from "signer-node-client";
const config = new Configuration({
apiKey: "your-application|0000000000000000000000000000000000000000000000000000000000000000",
// URL do sandbox; em produção, https://www.dropsigner.com ou a sua instância on-premises
basePath: "https://signer-lac.azurewebsites.net",
});
const uploadApi = new UploadApi(config);
const documentsApi = new DocumentsApi(config);
use Lacuna\Signer\PhpClient\SignerClient;
// URL do sandbox; em produção, https://www.dropsigner.com ou a sua instância on-premises
$signerClient = new SignerClient("https://signer-lac.azurewebsites.net/", "your-application|0000000000000000000000000000000000000000000000000000000000000000");
O cliente Node.js é gerado a partir da API: tem uma classe por grupo de APIs (UploadApi, DocumentsApi) e um método por endpoint, com o nome da rota, como apiDocumentsPost para POST /api/documents. Todo método devolve a resposta do axios, com o corpo em data.
1. Upload do arquivo
Faça o upload do arquivo a ser assinado. Há duas APIs de upload:
- A API simplificada de Upload (POST /api/uploads/bytes) recebe uma requisição JSON com os bytes do arquivo em Base64, no campo
bytes. É a opção mais simples, a que os Primeiros passos usam. - A API de Upload (POST /api/uploads) recebe o arquivo em uma requisição multipart/form-data. Prefira-a para arquivos grandes, porque evita o custo de transportar o arquivo em Base64.
As duas retornam um ID de upload que identifica aquele arquivo:
{
...
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
Com uma biblioteca cliente, o uploadFile do Java e do PHP envia o arquivo para POST /api/uploads (multipart); no Node.js, o apiUploadsBytesPost o envia para POST /api/uploads/bytes (Base64). Os métodos de C#, Java e PHP devolvem um modelo de upload, que o passo seguinte recebe inteiro; no Node.js, guarde o data.id.
- C#
- Java
- Node.js
- PHP
var file = File.ReadAllBytes("Contract.pdf");
var uploadModel = await signerClient.UploadFileAsync("Contract.pdf", file, "application/pdf");
import com.lacunasoftware.signer.javaclient.models.UploadModel;
byte[] content = Files.readAllBytes(Paths.get("Contract.pdf"));
UploadModel uploadModel = signerClient.uploadFile("Contract.pdf", content, "application/pdf");
import { getBase64 } from "signer-node-client";
// getBase64 lê o arquivo e o codifica em Base64
const upload = await uploadApi.apiUploadsBytesPost({ bytes: getBase64("Contract.pdf") });
const uploadId = upload.data.id;
use Lacuna\Signer\PhpClient\Models\UploadModel;
$file = fopen("Contract.pdf", "r");
$uploadModel = new UploadModel($signerClient->uploadFile("Contract.pdf", $file, "application/pdf"));
// O Guzzle fecha o stream depois de enviá-lo, e fclose() em um stream já fechado lança erro no PHP 8.
if (is_resource($file)) {
fclose($file);
}
2. Criação do documento
Crie um documento a partir do upload usando a API de Criação de Documentos. Nessa chamada você monta o fluxo do documento, isto é, define os participantes e em qual ordem devem tomar suas ações:
POST /api/documents
{
"files": [
{
"displayName": "Contrato Integração", //Nome que será dado ao documento criado
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", //ID do upload
"name": "Contrato.pdf", //Nome do arquivo original
"contentType": "application/pdf" //mime type do arquivo original
}
],
"flowActions": [
{
"type": "Signer", //Tipo do participante, nesse caso, assinante
"step": 1, //Ordem de assinatura
"user": {
"name": "John Wick", //Nome do participante
"identifier": "81976153069", //CPF do participante
"email": "john.wick@mailinator.com", //Email do participante
},
"allowElectronicSignature": true //Permite assinatura eletrônica
}
]
}
A resposta retorna o ID do documento criado associado ao ID do upload, assim você sabe exatamente qual documento corresponde a qual upload:
[
{
"uploadId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"documentId": "b12cb1b2-5d6e-40b2-a050-097d068c4c11"
}
]
A mesma requisição com uma biblioteca cliente. A criação devolve a lista de pares uploadId e documentId; com um só arquivo, use o primeiro.
- C#
- Java
- Node.js
- PHP
using Lacuna.Signer.Api;
using Lacuna.Signer.Api.Documents;
using Lacuna.Signer.Api.FlowActions;
using Lacuna.Signer.Api.Users;
// O nome que o documento terá no Signer
var fileUploadModel = new FileUploadModel(uploadModel) { DisplayName = "Integration Contract" };
// Um FlowActionCreateModel por ação (assinatura ou aprovação) do fluxo
var flowAction = new FlowActionCreateModel()
{
Type = FlowActionType.Signer,
Step = 1,
User = new ParticipantUserModel()
{
Name = "John Wick",
Identifier = "81976153069",
Email = "john.wick@mailinator.com"
},
AllowElectronicSignature = true
};
var documentRequest = new CreateDocumentRequest()
{
Files = new List<FileUploadModel>() { fileUploadModel },
FlowActions = new List<FlowActionCreateModel>() { flowAction }
};
var result = (await signerClient.CreateDocumentAsync(documentRequest)).First();
// result.DocumentId, result.UploadId
import com.lacunasoftware.signer.FlowActionType;
import com.lacunasoftware.signer.documents.CreateDocumentRequest;
import com.lacunasoftware.signer.documents.CreateDocumentResult;
import com.lacunasoftware.signer.flowactions.FlowActionCreateModel;
import com.lacunasoftware.signer.javaclient.builders.FileUploadModelBuilder;
import com.lacunasoftware.signer.users.ParticipantUserModel;
// O nome que o documento terá no Signer
FileUploadModelBuilder fileUploadModelBuilder = new FileUploadModelBuilder(uploadModel);
fileUploadModelBuilder.setDisplayName("Integration Contract");
ParticipantUserModel user = new ParticipantUserModel();
user.setName("John Wick");
user.setIdentifier("81976153069");
user.setEmail("john.wick@mailinator.com");
// Um FlowActionCreateModel por ação (assinatura ou aprovação) do fluxo
FlowActionCreateModel flowAction = new FlowActionCreateModel();
flowAction.setType(FlowActionType.SIGNER);
flowAction.setStep(1);
flowAction.setUser(user);
flowAction.setAllowElectronicSignature(true);
CreateDocumentRequest documentRequest = new CreateDocumentRequest();
documentRequest.addFilesItem(fileUploadModelBuilder.toModel());
documentRequest.addFlowActionsItem(flowAction);
CreateDocumentResult result = signerClient.createDocument(documentRequest).get(0);
// result.getDocumentId(), result.getUploadId()
import {
DocumentsCreateDocumentRequest,
FileUploadModel,
FlowActionsFlowActionCreateModel,
FlowActionType,
} from "signer-node-client";
const fileUploadModel: FileUploadModel = {
id: uploadId,
name: "Contract.pdf",
contentType: "application/pdf",
displayName: "Integration Contract", // o nome que o documento terá no Signer
};
// Um FlowActionsFlowActionCreateModel por ação (assinatura ou aprovação) do fluxo
const flowAction: FlowActionsFlowActionCreateModel = {
type: FlowActionType.Signer,
step: 1,
user: { name: "John Wick", identifier: "81976153069", email: "john.wick@mailinator.com" },
allowElectronicSignature: true,
};
const documentRequest: DocumentsCreateDocumentRequest = {
files: [fileUploadModel],
flowActions: [flowAction],
};
const result = (await documentsApi.apiDocumentsPost(documentRequest)).data[0];
// result.documentId, result.uploadId
use Lacuna\Signer\Model\DocumentsCreateDocumentRequest;
use Lacuna\Signer\Model\FlowActionsFlowActionCreateModel;
use Lacuna\Signer\Model\FlowActionType;
use Lacuna\Signer\Model\UsersParticipantUserModel;
use Lacuna\Signer\PhpClient\Builders\FileUploadBuilder;
// O nome que o documento terá no Signer
$fileUploadModelBuilder = new FileUploadBuilder($uploadModel);
$fileUploadModelBuilder->setDisplayName("Integration Contract");
$user = new UsersParticipantUserModel();
$user->setName("John Wick");
$user->setIdentifier("81976153069");
$user->setEmail("john.wick@mailinator.com");
// Um FlowActionsFlowActionCreateModel por ação (assinatura ou aprovação) do fluxo
$flowAction = new FlowActionsFlowActionCreateModel();
$flowAction->setType(FlowActionType::SIGNER);
$flowAction->setStep(1);
$flowAction->setUser($user);
$flowAction->setAllowElectronicSignature(true);
$documentRequest = new DocumentsCreateDocumentRequest();
$documentRequest->setFiles(array($fileUploadModelBuilder->toModel()));
$documentRequest->setFlowActions(array($flowAction));
$result = $signerClient->createDocument($documentRequest)[0];
// $result->getDocumentId(), $result->getUploadId()
Você pode criar mais de um documento na mesma chamada, adicionando quantos arquivos forem necessários. Nesse caso, todos os documentos terão o mesmo fluxo.
Ao criar o documento, o Signer notifica automaticamente os participantes seguindo a ordem especificada. Cada participante receberá um e-mail com um link que permite assinar ou aprovar o documento sem precisar de autenticação.
A requisição também aceita opções que valem para todos os documentos dela, como o carimbo do tempo e a validação de acesso. Veja Opções do documento.
O fluxo (flowActions)
Cada item de flowActions representa uma ação de um participante. Os principais campos são:
| Campo | Descrição |
|---|---|
type | Tipo da ação (veja a tabela abaixo) |
step | Ordem da ação no fluxo. Ações com o mesmo step acontecem em paralelo; steps diferentes são executados em sequência |
user | Dados do participante (name, identifier, email). Veja O participante (user) |
allowElectronicSignature | Permite que o participante assine eletronicamente (sem certificado digital) |
Os tipos de ação (type) disponíveis são:
type | Descrição |
|---|---|
Signer | Participante que assina o documento |
Approver | Participante que aprova o documento (sem assinar) |
SignRule | Regra de assinatura: um grupo em que qualquer membro pode assinar |
ApproverRule | Regra de aprovação: um grupo em que qualquer membro pode aprovar |
Nas bibliotecas cliente, esses valores são o enum FlowActionType. O FlowActionType do cliente .NET (no Lacuna.Signer.Api 1.67.0, que o Lacuna.Signer.Client 1.24.0 instala) e o do cliente PHP 1.4.10 não têm ApproverRule; os de Java e Node.js têm os quatro.
Exemplos no GitHub, por cenário:
| Cenário | Exemplos |
|---|---|
| Um assinante | C# · Java · PHP · Node.js |
| Dois ou mais assinantes, com ordenação | C# · Java · PHP · Node.js |
| Dois ou mais assinantes, sem ordenação | C# · Java · PHP · Node.js |
| Com aprovação | C# · Java · PHP · Node.js |
| Com anexo | C# · Java · PHP · Node.js |
| Com descrição | C# · Java · PHP · Node.js |
Para os campos do participante e os tipos de identificador, veja O participante (user). Para as outras opções de cada item de flowActions, como a rubrica e as verificações da assinatura eletrônica, veja Opções do participante.
Assinatura na sua própria aplicação
Caso você queira realizar a assinatura do documento dentro da sua própria aplicação, use a opção de Assinatura Embutida.
Siga os mesmos passos da seção anterior, mas, ao enviar o documento, recomenda-se adicionar o parâmetro disablePendingActionNotifications com valor true. Dessa forma, não serão enviadas notificações para os participantes.
Após a criação do documento, utilize o ID do documento para obter a URL de assinatura usando a API de URL de Ação:
POST /api/documents/b12cb1b2-5d6e-40b2-a050-097d068c4c11/action-url
{
//devem ser enviadas informações que identifiquem o participante desejado
"identifier": "81976153069",
"emailAddress": "john.wick@mailinator.com"
}
Caso o signatário tenha permitido assinatura eletrônica (parâmetro allowElectronicSignature = true), é possível exigir a confirmação do e-mail dele por meio de um código de acesso no momento da assinatura. Para isso, defina requireEmailAuthentication como true:
POST /api/documents/b12cb1b2-5d6e-40b2-a050-097d068c4c11/action-url
{
//devem ser enviadas informações que identifiquem o participante desejado
"identifier": "81976153069",
"emailAddress": "john.wick@mailinator.com",
//se a assinatura for eletrônica e esse parâmetro for true, requer confirmação de e-mail com código para completar a assinatura
"requireEmailAuthentication": true
}
A resposta apresenta duas URLs:
{
//URL para redirecionar o usuário para a página de assinatura no Signer
"url": "https://...",
//URL para usar com o Widget de assinatura
"embedUrl": "https://..."
}
Use a embedUrl com o Widget de assinatura para exibir a página de assinatura do Signer dentro da sua aplicação. A página Assinatura embutida descreve como utilizar o Widget.
Com uma biblioteca cliente, defina disablePendingActionNotifications na requisição do passo 2 e, depois de criar o documento, peça as URLs do participante:
- C#
- Java
- Node.js
- PHP
// Antes de CreateDocumentAsync: não enviar e-mail aos participantes
documentRequest.DisablePendingActionNotifications = true;
var result = (await signerClient.CreateDocumentAsync(documentRequest)).First();
var actionUrlRequest = new ActionUrlRequest()
{
Identifier = "81976153069",
EmailAddress = "john.wick@mailinator.com"
};
var actionUrlResponse = await signerClient.GetActionUrlAsync(result.DocumentId, actionUrlRequest);
// actionUrlResponse.Url: redirecione o usuário para o Signer
// actionUrlResponse.EmbedUrl: passe-a ao widget
import com.lacunasoftware.signer.documents.ActionUrlRequest;
import com.lacunasoftware.signer.documents.ActionUrlResponse;
// Antes de createDocument: não enviar e-mail aos participantes
documentRequest.setDisablePendingActionNotifications(true);
CreateDocumentResult result = signerClient.createDocument(documentRequest).get(0);
ActionUrlRequest actionUrlRequest = new ActionUrlRequest();
actionUrlRequest.setIdentifier("81976153069");
actionUrlRequest.setEmailAddress("john.wick@mailinator.com");
ActionUrlResponse actionUrlResponse = signerClient.getActionUrl(result.getDocumentId(), actionUrlRequest);
// actionUrlResponse.getUrl(): redirecione o usuário para o Signer
// actionUrlResponse.getEmbedUrl(): passe-a ao widget
import { DocumentsActionUrlRequest } from "signer-node-client";
// Não enviar e-mail aos participantes
const result = (
await documentsApi.apiDocumentsPost({ ...documentRequest, disablePendingActionNotifications: true })
).data[0];
const actionUrlRequest: DocumentsActionUrlRequest = {
identifier: "81976153069",
emailAddress: "john.wick@mailinator.com",
};
const actionUrl = (await documentsApi.apiDocumentsIdActionUrlPost(result.documentId, actionUrlRequest)).data;
// actionUrl.url: redirecione o usuário para o Signer
// actionUrl.embedUrl: passe-a ao widget
use Lacuna\Signer\Model\DocumentsActionUrlRequest;
use Lacuna\Signer\Model\DocumentsActionUrlResponse;
// Antes de createDocument: não enviar e-mail aos participantes
$documentRequest->setDisablePendingActionNotifications(true);
$result = $signerClient->createDocument($documentRequest)[0];
$actionUrlRequest = new DocumentsActionUrlRequest();
$actionUrlRequest->setIdentifier("81976153069");
$actionUrlRequest->setEmailAddress("john.wick@mailinator.com");
$actionUrlResponse = new DocumentsActionUrlResponse($signerClient->getActionUrl($result->getDocumentId(), $actionUrlRequest));
// $actionUrlResponse->getUrl(): redirecione o usuário para o Signer
// $actionUrlResponse->getEmbedUrl(): passe-a ao widget
Para exigir o código por e-mail, defina também RequireEmailAuthentication = true (C#), setRequireEmailAuthentication(true) (Java e PHP) ou requireEmailAuthentication: true (Node.js) na requisição da URL de ação.
Exemplos no GitHub: C# · Java · PHP · Node.js
Pré-posicionar uma assinatura no documento
Durante a criação de um documento, é possível posicionar a assinatura de cada participante em um local específico do documento. Todas as definições são feitas durante a criação do flowAction: há opções para definir a página e a localização da assinatura dentro dela.
Exemplos no GitHub: C# · Java · PHP · Node.js
Posicionamento da assinatura pelo signatário
Disponível a partir da versão 3.0.0. Dois campos controlam onde fica a representação visual da assinatura (a imagem da assinatura no PDF):
| Campo | Descrição |
|---|---|
signerPositioningEnabled | Se false, o signatário não pode posicionar a própria assinatura. Se ele envia uma posição, a API responde com o erro SignerPositioningNotAllowed. |
automaticSignaturePositioning | O que o Signer faz com a assinatura que ninguém posicionou: Disabled (não coloca a assinatura no PDF), Footnote (coloca no rodapé do documento) ou SignatureSheet (coloca numa folha de assinaturas no fim do PDF). |
Envie os campos na criação do documento. Um campo null ou ausente usa a configuração atual da organização. O documento guarda o valor: uma mudança posterior na organização não altera o documento. Você não pode combinar signerPositioningEnabled igual a false com Disabled: a API responde com o erro SignerPositioningIncompatibleWithAutomaticPositioningDisabled.
Para mudar os dois campos depois da criação, envie um PUT. É necessária a mesma permissão de editar o fluxo:
PUT /api/documents/b12cb1b2-5d6e-40b2-a050-097d068c4c11/signer-positioning
{
"signerPositioningEnabled": false,
"automaticSignaturePositioning": "SignatureSheet"
}
Você só pode fazer essa mudança antes de um participante assinar ou aprovar. Depois disso, a API responde com o erro SignerPositioningLockedAfterSignature. A mudança também falha em documentos cancelados, expirados, recusados ou pausados.
Ao editar o fluxo de um documento já assinado (POST /api/documents/{id}/flow), a folha de assinaturas pode não ter espaço para um novo participante. Nesse caso, a API responde com o erro SignatureSheetFull. Para aceitar a edição mesmo assim, envie ?acceptFootnoteVisualRepresentation=true. O Signer coloca no rodapé da página as assinaturas que não cabem na folha e deixa de fora as rubricas opcionais desses participantes.
Mesclar múltiplos arquivos em um único documento (envelope)
É possível fazer o upload de múltiplos arquivos e mesclar todos em um só documento (envelope). Para isso, adicione o parâmetro isEnvelope=true e um nome de envelope em EnvelopeName.
A mesclagem só funciona se todos os arquivos enviados forem PDFs.
Exemplos no GitHub: C# · Java · PHP · Node.js
Próximos passos
Com o documento criado, veja como acompanhar o status e enviar lembretes.