Introdução
A API REST do Signer permite criar e gerenciar documentos para assinatura digital, usar modelos (templates) salvos, configurar webhooks e receber notificações em tempo real sobre o ciclo de vida dos documentos. Todos os endpoints retornam JSON e exigem autenticação via API Key.
URL base
https://signer.suaempresa.com/api/v1
Autenticação
Todas as requisições devem incluir o header Authorization com uma API Key gerada em Dashboard → API Keys. As chaves têm o prefixo sk_.
Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Scopes
Cada API Key tem um conjunto de permissões (scopes). Configure os scopes ao criar a chave.
| Scope | Permissão |
|---|---|
documents:read | Listar e consultar documentos |
documents:write | Criar, enviar e excluir documentos |
webhooks:read | Listar webhooks registrados |
webhooks:write | Criar e atualizar webhooks |
Documentos
/api/v1/documentsscope: documents:readLista os documentos criados pela conta autenticada, paginados e ordenados por data de criação decrescente.
Query params
| Campo | Tipo | Descrição |
|---|---|---|
status | string | Filtrar por status: DRAFT | PENDING | PARTIALLY_SIGNED | SIGNED | REJECTED | EXPIRED | CANCELLED |
limit | integer | Itens por página. Padrão: 20. Máximo: 100. |
page | integer | Número da página. Padrão: 1. |
Resposta 200
{
"data": [
{
"id": "doc_abc123",
"title": "Contrato de Prestação de Serviços",
"status": "PENDING",
"sourceType": "UPLOAD",
"createdAt": "2026-06-01T12:00:00.000Z",
"signers": [
{
"name": "Maria Silva",
"email": "maria@email.com",
"status": "NOTIFIED",
"signedAt": null
}
]
}
],
"total": 42,
"page": 1,
"limit": 20
}/api/v1/documentsscope: documents:writeCria um novo documento com signatários. Suporta importação de PDF via URL, uso de templates salvos e envio imediato.
Body JSON
| Campo | Tipo | Descrição |
|---|---|---|
title | string | Título do documento. Obrigatório quando templateId não é fornecido. Máx 255 caracteres. |
description | string | Descrição opcional. |
templateId | string | ID de um template PDF criado no painel. O documento herda o arquivo e as posições dos campos de assinatura. O title do template é usado como fallback se title não for enviado. |
fileUrl | string (url) | URL pública de um PDF para importar (máx. 20 MB). Quando usado junto com templateId, substitui o arquivo do template mas mantém as posições dos campos. |
signersreq | array | Lista de signatários (mínimo 1). Ver estrutura abaixo. |
sendImmediately | boolean | Se true, notifica os signatários imediatamente e consome 1 crédito. Padrão: false (cria como DRAFT). |
Estrutura de cada signatário
| Campo | Tipo | Descrição |
|---|---|---|
namereq | string | Nome completo. |
email | string | E-mail do signatário. Obrigatório se authMethod = EMAIL_OTP ou se phone não for fornecido. |
phone | string | Telefone no formato internacional (+5511999999999). Obrigatório se authMethod = WHATSAPP_OTP. |
authMethod | enum | EMAIL_OTP (padrão) | WHATSAPP_OTP | GOV_BR |
notifyByEmail | boolean | Notificar via e-mail ao enviar. Padrão: true se email fornecido. |
notifyByWhatsapp | boolean | Notificar via WhatsApp ao enviar. Padrão: true para WHATSAPP_OTP se phone fornecido. |
Regras de validação dos signatários:
- Pelo menos email ou phone deve ser fornecido.
EMAIL_OTPexige email.WHATSAPP_OTPexige phone.
Exemplo 1: PDF via URL, envio imediato
POST /api/v1/documents
Content-Type: application/json
Authorization: Bearer sk_...
{
"title": "Proposta Solar: João Pereira",
"fileUrl": "https://r2.suaempresa.com/propostas/prop_123.pdf",
"signers": [
{
"name": "João Pereira",
"email": "joao@email.com",
"authMethod": "EMAIL_OTP"
}
],
"sendImmediately": true
}Exemplo 2: Usando template salvo
POST /api/v1/documents
Content-Type: application/json
Authorization: Bearer sk_...
{
"templateId": "tmpl_abc123",
"title": "Contrato Franquia: Maria Silva",
"signers": [
{
"name": "Maria Silva",
"email": "maria@email.com",
"phone": "+5511999990000",
"authMethod": "WHATSAPP_OTP",
"notifyByWhatsapp": true
}
],
"sendImmediately": true
}Exemplo 3: Template + PDF diferente (mantém campos)
POST /api/v1/documents
Content-Type: application/json
Authorization: Bearer sk_...
{
"templateId": "tmpl_abc123",
"fileUrl": "https://cdn.seuapp.com/docs/prop_456.pdf",
"signers": [
{ "name": "Carlos Lima", "email": "carlos@email.com" }
]
}Resposta 201
{
"data": {
"id": "doc_xyz789",
"title": "Proposta Solar: João Pereira",
"status": "PENDING",
"sourceType": "UPLOAD",
"createdAt": "2026-06-01T12:00:00.000Z",
"signers": [
{
"id": "sig_aaa111",
"name": "João Pereira",
"email": "joao@email.com",
"phone": null,
"token": "abc123...",
"status": "NOTIFIED",
"authMethod": "EMAIL_OTP",
"notifyByEmail": true,
"notifyByWhatsapp": false,
"order": 0,
"signedAt": null
}
]
}
}Créditos: 1 crédito é consumido no momento do envio (sendImmediately: true ou POST /api/v1/documents/:id para enviar um DRAFT). Contas novas recebem um período de teste com um pacote de créditos; depois disso, é necessário assinar um plano ou ter créditos avulsos disponíveis.
/api/v1/documents/:idscope: documents:readRetorna os detalhes completos de um documento, incluindo signatários com hashes de assinatura, eventos de auditoria e o hash do documento certificado (quando assinado).
Resposta 200
{
"data": {
"id": "doc_xyz789",
"title": "Proposta Solar: João Pereira",
"status": "SIGNED",
"sourceType": "UPLOAD",
"documentHash": "a3f2c1...64chars",
"createdAt": "2026-06-01T12:00:00.000Z",
"signers": [
{
"id": "sig_aaa111",
"name": "João Pereira",
"email": "joao@email.com",
"phone": null,
"authMethod": "EMAIL_OTP",
"signerRole": "SIGNER",
"status": "SIGNED",
"signedAt": "2026-06-01T14:32:00.000Z",
"signatureHash": "b7d9e2...64chars",
"order": 0
}
],
"events": [
{ "type": "DOCUMENT_SIGNED", "actorEmail": null, "createdAt": "2026-06-01T14:32:00.000Z" },
{ "type": "SIGNER_SIGNED", "actorEmail": "joao@email.com", "createdAt": "2026-06-01T14:32:00.000Z" },
{ "type": "SIGNER_OTP_VERIFIED","actorEmail": "joao@email.com", "createdAt": "2026-06-01T14:31:00.000Z" },
{ "type": "DOCUMENT_SENT", "actorEmail": "voce@email.com", "createdAt": "2026-06-01T12:00:00.000Z" }
]
}
}Campos de destaque na resposta
| Campo | Tipo | Descrição |
|---|---|---|
documentHash | string | null | SHA-256 do PDF certificado final (com página de certificação). Disponível após status SIGNED. Use para verificação de autenticidade em /verify. |
signerRole | string | Papel do signatário no documento. Ex: SIGNER, WITNESS, CONTRATANTE, FRANQUEADO, etc. |
signatureHash | string | null | SHA-256 da imagem da assinatura do signatário. Disponível após assinar. |
/api/v1/documents/:idscope: documents:writeEnvia um documento em status DRAFT, notificando todos os signatários e consumindo 1 crédito. Não é possível enviar um documento já enviado.
Resposta 200
{ "ok": true }| Campo | Tipo | Descrição |
|---|---|---|
400 Document already sent | | O documento não está em status DRAFT. |
402 Insufficient credits | | Créditos insuficientes para o envio. |
/api/v1/documents/:idscope: documents:writeExclui permanentemente um documento. Só é possível excluir documentos em status DRAFT ou CANCELLED.
Resposta 200
{ "ok": true }| Campo | Tipo | Descrição |
|---|---|---|
400 Cannot delete active document | | O documento está em andamento (PENDING, SIGNED, etc.). |
Webhooks
Webhooks permitem que sua aplicação receba notificações em tempo real sobre eventos de documentos. O Signer assina cada entrega com HMAC-SHA256 usando o secret configurado no webhook.
/api/v1/webhooksscope: webhooks:readLista todos os webhooks registrados na conta.
{
"data": [
{
"id": "wh_abc123",
"name": "CRM: Assinatura de Propostas",
"url": "https://seucrm.com/api/webhooks/esignature?token=...",
"events": ["document.signed", "document.declined", "document.viewed", "document.expired"],
"isActive": true,
"createdAt": "2026-06-01T12:00:00.000Z"
}
]
}/api/v1/webhooksscope: webhooks:writeCria ou atualiza um webhook. Se já existir um webhook com a mesma URL para esta conta, ele é atualizado em vez de duplicado (idempotente por URL).
Body JSON
| Campo | Tipo | Descrição |
|---|---|---|
namereq | string | Nome identificador do webhook. |
urlreq | string (url) | URL que receberá os eventos via POST. |
eventsreq | string[] | Lista de eventos a receber. Veja a seção Eventos. |
secret | string | Secret para assinar as entregas (mín. 16 chars). Se omitido, um secret aleatório é gerado, mas ele não é retornado na resposta, então passe o seu próprio. |
Exemplo
POST /api/v1/webhooks
Content-Type: application/json
Authorization: Bearer sk_...
{
"name": "Minha Integração",
"url": "https://meuapp.com/webhooks/signer",
"events": ["document.signed", "document.declined"],
"secret": "meu-secret-hmac-sha256-com-mais-de-16-chars"
}Resposta 201 (criado) ou 200 (atualizado)
{
"data": {
"id": "wh_abc123",
"name": "Minha Integração",
"url": "https://meuapp.com/webhooks/signer",
"events": ["document.signed", "document.declined"],
"isActive": true,
"createdAt": "2026-06-01T12:00:00.000Z"
}
}secret nunca é retornado na resposta. Guarde-o no momento em que enviá-lo.Eventos de Webhook
Quando um evento ocorre, o Signer faz um POST para a URL do webhook com o corpo abaixo e headers de assinatura HMAC.
Headers enviados
| Campo | Tipo | Descrição |
|---|---|---|
X-Signature | string | HMAC-SHA256(rawBody, secret) em hex. |
X-Signer-Signature | string | Mesmo valor (alias nativo do Signer). |
X-Signer-Event | string | Nome do evento (ex: document.signed). |
Content-Type | string | application/json |
Corpo do evento document.signed
Para document.signed, o payload inclui documentHash: o SHA-256 do PDF certificado. Use-o para verificar a autenticidade do arquivo na rota /verify.
{
"event": "document.signed",
"documentId": "doc_xyz789",
"signerName": "João Pereira",
"signerEmail": "joao@email.com",
"reason": null,
"documentHash": "a3f2c1d4e5...64chars",
"timestamp": "2026-06-01T14:32:00.000Z",
"data": {
"documentId": "doc_xyz789",
"title": "Proposta Solar: João Pereira",
"signerName": "João Pereira",
"signerEmail": "joao@email.com",
"reason": null,
"documentHash": "a3f2c1d4e5...64chars"
}
}Corpo dos demais eventos
{
"event": "document.declined",
"documentId": "doc_xyz789",
"signerName": "João Pereira",
"signerEmail": "joao@email.com",
"reason": null,
"timestamp": "2026-06-01T14:32:00.000Z",
"data": { ... }
}| Evento | Quando ocorre | documentHash? |
|---|---|---|
document.signed | Todos os signatários assinaram | Sim |
document.declined | Um signatário recusou o documento | Não |
document.viewed | Signatário clicou em 'Li e entendi' | Não |
document.expired | O documento expirou sem assinaturas | Não |
Verificando a assinatura HMAC
// Node.js / TypeScript
import { createHmac, timingSafeEqual } from "crypto";
function verifySignature(rawBody: string, signature: string, secret: string): boolean {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
try {
return timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
} catch {
return false;
}
}
// No seu handler:
const rawBody = await req.text();
const sig = req.headers.get("x-signature") ?? "";
const verified = verifySignature(rawBody, sig, process.env.WEBHOOK_SECRET!);
if (!verified) return new Response("Unauthorized", { status: 401 });Verificação de Autenticidade
Documentos assinados recebem uma página de certificação com trilha de auditoria e um hash SHA-256 único (documentHash). Qualquer alteração no arquivo, mesmo mínima, produz um hash diferente, invalidando a verificação.
Verificação via página pública
Acesse /verify e faça upload do PDF assinado. O hash é calculado localmente no navegador: o arquivo nunca é enviado ao servidor.
https://signer.suaempresa.com/verify
Verificação via API
Endpoint público: não requer autenticação. Envie o SHA-256 do PDF e receba os metadados de assinatura se o documento for autêntico.
Requisição
POST /api/public/verify
Content-Type: application/json
{ "hash": "a3f2c1d4e5b6...64chars" }Resposta: documento encontrado
{
"found": true,
"document": {
"id": "doc_xyz789",
"title": "Proposta Solar: João Pereira",
"documentHash": "a3f2c1d4e5b6...64chars",
"signedAt": "2026-06-01T14:32:00.000Z",
"signerCount": 2
}
}Resposta: não encontrado
{ "found": false }Calcular o hash do PDF (Node.js)
import { createHash } from "crypto";
import { readFileSync } from "fs";
const buffer = readFileSync("documento-assinado.pdf");
const hash = createHash("sha256").update(buffer).digest("hex");
const res = await fetch("https://signer.suaempresa.com/api/public/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ hash }),
});
const data = await res.json();
console.log(data.found ? "Autêntico ✓" : "Não encontrado ✗");documentHash retornado no webhook document.signedpara auditar documentos sem precisar re-baixar o arquivo.Erros
Todos os erros retornam JSON com o campo error e, quando aplicável, detailscom informações de validação.
| Status | Código | Causa |
|---|---|---|
| 400 | Validation error | Body inválido. Verifique o campo details na resposta. |
| 400 | Document already sent | Tentativa de enviar um documento que não está em DRAFT. |
| 400 | Cannot delete active document | Tentativa de excluir documento em andamento. |
| 400 | title is required when templateId is not provided | Omitiu title sem usar templateId. |
| 401 | Unauthorized | Header Authorization ausente ou API Key inválida/expirada. |
| 402 | Insufficient credits | Créditos insuficientes para enviar o documento. |
| 403 | Forbidden | API Key sem o scope necessário. |
| 404 | Not found | Documento ou template não encontrado, ou não pertence a esta conta. |
| 422 | Failed to download fileUrl | Não foi possível acessar fileUrl ou o arquivo excede 20 MB. |
Exemplo de erro de validação
{
"error": "Validation error",
"details": {
"fieldErrors": {
"signers": [
"Signer must have email or phone"
]
},
"formErrors": []
}
}