Signer/API Referencev1
Gerar API Key →

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
Atenção: nunca exponha sua API Key no frontend. Faça todas as chamadas server-side.

Scopes

Cada API Key tem um conjunto de permissões (scopes). Configure os scopes ao criar a chave.

ScopePermissão
documents:readListar e consultar documentos
documents:writeCriar, enviar e excluir documentos
webhooks:readListar webhooks registrados
webhooks:writeCriar e atualizar webhooks

Documentos

GET/api/v1/documentsscope: documents:read

Lista os documentos criados pela conta autenticada, paginados e ordenados por data de criação decrescente.

Query params

CampoTipoDescrição
statusstringFiltrar por status: DRAFT | PENDING | PARTIALLY_SIGNED | SIGNED | REJECTED | EXPIRED | CANCELLED
limitintegerItens por página. Padrão: 20. Máximo: 100.
pageintegerNú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
}
POST/api/v1/documentsscope: documents:write

Cria um novo documento com signatários. Suporta importação de PDF via URL, uso de templates salvos e envio imediato.

Body JSON

CampoTipoDescrição
titlestringTítulo do documento. Obrigatório quando templateId não é fornecido. Máx 255 caracteres.
descriptionstringDescrição opcional.
templateIdstringID 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.
fileUrlstring (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.
signersreqarrayLista de signatários (mínimo 1). Ver estrutura abaixo.
sendImmediatelybooleanSe true, notifica os signatários imediatamente e consome 1 crédito. Padrão: false (cria como DRAFT).

Estrutura de cada signatário

CampoTipoDescrição
namereqstringNome completo.
emailstringE-mail do signatário. Obrigatório se authMethod = EMAIL_OTP ou se phone não for fornecido.
phonestringTelefone no formato internacional (+5511999999999). Obrigatório se authMethod = WHATSAPP_OTP.
authMethodenumEMAIL_OTP (padrão) | WHATSAPP_OTP | GOV_BR
notifyByEmailbooleanNotificar via e-mail ao enviar. Padrão: true se email fornecido.
notifyByWhatsappbooleanNotificar 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_OTP exige email.
  • WHATSAPP_OTP exige 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.

GET/api/v1/documents/:idscope: documents:read

Retorna 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

CampoTipoDescrição
documentHashstring | nullSHA-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.
signerRolestringPapel do signatário no documento. Ex: SIGNER, WITNESS, CONTRATANTE, FRANQUEADO, etc.
signatureHashstring | nullSHA-256 da imagem da assinatura do signatário. Disponível após assinar.
POST/api/v1/documents/:idscope: documents:write

Envia 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 }
CampoTipoDescrição
400 Document already sentO documento não está em status DRAFT.
402 Insufficient creditsCréditos insuficientes para o envio.
DELETE/api/v1/documents/:idscope: documents:write

Exclui permanentemente um documento. Só é possível excluir documentos em status DRAFT ou CANCELLED.

Resposta 200

{ "ok": true }
CampoTipoDescrição
400 Cannot delete active documentO 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.

GET/api/v1/webhooksscope: webhooks:read

Lista 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"
    }
  ]
}
POST/api/v1/webhooksscope: webhooks:write

Cria 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

CampoTipoDescrição
namereqstringNome identificador do webhook.
urlreqstring (url)URL que receberá os eventos via POST.
eventsreqstring[]Lista de eventos a receber. Veja a seção Eventos.
secretstringSecret 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"
  }
}
Nota de segurança: o 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

CampoTipoDescrição
X-SignaturestringHMAC-SHA256(rawBody, secret) em hex.
X-Signer-SignaturestringMesmo valor (alias nativo do Signer).
X-Signer-EventstringNome do evento (ex: document.signed).
Content-Typestringapplication/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": { ... }
}
EventoQuando ocorredocumentHash?
document.signedTodos os signatários assinaramSim
document.declinedUm signatário recusou o documentoNão
document.viewedSignatário clicou em 'Li e entendi'Não
document.expiredO documento expirou sem assinaturasNã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 ✗");
Dica: guarde o 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.

StatusCódigoCausa
400Validation errorBody inválido. Verifique o campo details na resposta.
400Document already sentTentativa de enviar um documento que não está em DRAFT.
400Cannot delete active documentTentativa de excluir documento em andamento.
400title is required when templateId is not providedOmitiu title sem usar templateId.
401UnauthorizedHeader Authorization ausente ou API Key inválida/expirada.
402Insufficient creditsCréditos insuficientes para enviar o documento.
403ForbiddenAPI Key sem o scope necessário.
404Not foundDocumento ou template não encontrado, ou não pertence a esta conta.
422Failed to download fileUrlNã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": []
  }
}