Como Gerar o PDF do Contrato Antes de Enviar para Assinatura via API

A API da SignDocs Brasil assina o documento que você envia. Ela não monta o documento: não há templates, campos mesclados nem editor. Isso é deliberado (o contrato é regra de negócio sua, e cada sistema já tem os dados dele), mas transfere para o integrador uma etapa que costuma dar mais trabalho do que a chamada de assinatura em si: gerar um PDF correto, dentro do limite de tamanho, com o conteúdo final, antes de enviar. Este guia percorre essa etapa de ponta a ponta.

O roteiro é o mesmo para qualquer stack: escolher a fonte (HTML ou DOCX), renderizar, manter o arquivo abaixo de 10 MB, guardar o hash, e só então criar a sessão ou o envelope. Se você ainda não fez a primeira chamada, comece por como enviar um documento para assinatura via API; aqui o foco é o que acontece antes dela.

O que a API aceita

Antes de escolher a ferramenta de geração, vale saber o que chega do outro lado. A API aceita o documento em duas formas: em base64 no corpo da criação (document.content e document.filename em POST /v1/signing-sessions ou POST /v1/envelopes) ou por upload direto (um fluxo de presign que veremos adiante). Em ambas, o limite é de 10 MB.

O formato não precisa ser PDF. As extensões aceitas são:

Família Extensões Observação
PDF .pdf O caminho direto: o que você gera é o que será assinado
Office e OpenDocument .docx .doc .xlsx .xls .pptx .ppt .odt .ods .odp .rtf Convertidos para PDF antes da assinatura
Texto e dados .txt .csv .xml .json .html Convertidos para PDF; a diagramação resultante é básica
Imagens .jpg .jpeg .png .tiff .tif .bmp .svg Convertidas para PDF; úteis para termos escaneados

Aceitar DOCX é conveniente para começar, mas para um fluxo em produção a recomendação é clara: gere o PDF você mesmo e envie o PDF. Assim o que o signatário vê é exatamente o que você renderizou e revisou, sem depender de como uma conversão automática tratou fontes, tabelas ou quebras de página. A lista completa de tipos e cenários está em documentos que podem ser assinados digitalmente.

Caminho 1: HTML para PDF

É o caminho natural para quem já tem uma aplicação web: o contrato vira um template HTML com os dados do seu domínio interpolados, e um renderizador gera o PDF. As opções mais usadas:

  • Puppeteer / Playwright (Node): renderiza com o motor do Chromium, então CSS moderno (grid, flex, @page) funciona. É a escolha mais previsível para layouts elaborados; o custo é manter um navegador headless no ambiente de execução.
  • WeasyPrint (Python): não depende de navegador, tem bom suporte a CSS de impressão (@page, numeração, quebras) e é leve para rodar em containers e funções serverless.
  • wkhtmltopdf: maduro e amplamente empacotado, mas baseado em um motor WebKit antigo; evite CSS recente e teste bem as quebras de página.
// Node + Puppeteer: renderiza o contrato e devolve um Buffer PDF import puppeteer from 'puppeteer'; export async function renderContractPdf(html: string): Promise<Buffer> { const browser = await puppeteer.launch({ headless: true }); try { const page = await browser.newPage(); await page.setContent(html, { waitUntil: 'networkidle0' }); return Buffer.from(await page.pdf({ format: 'A4', printBackground: true, margin: { top: '20mm', right: '18mm', bottom: '22mm', left: '18mm' }, })); } finally { await browser.close(); } }

Dois cuidados específicos desse caminho. Use waitUntil: 'networkidle0' (ou embuta os recursos) para não renderizar antes de fontes e imagens carregarem. E não inclua recursos externos por URL em produção: um logo hospedado em um CDN que falha vira um contrato com um quadrado vazio, e você só descobre quando o cliente reclama. Embuta como data: URI ou sirva de um caminho local.

Caminho 2: DOCX como template

Quando o jurídico mantém os modelos em Word e não quer aprender HTML, o template DOCX com marcadores é o caminho de menor atrito. Bibliotecas como docxtemplater (Node) ou python-docx e docxtpl (Python) preenchem os marcadores; depois você converte para PDF com LibreOffice em modo headless (soffice --headless --convert-to pdf) e envia o PDF.

Aqui a tentação é pular a conversão e enviar o .docx direto, já que a API aceita. Funciona, e para volumes pequenos pode ser suficiente. Mas convertendo você mesmo, o PDF que sai do seu pipeline pode ser revisado, versionado e hasheado antes de qualquer pessoa vê-lo, e isso importa na próxima seção.

Ficando abaixo de 10 MB

Um contrato de texto puro raramente passa de algumas centenas de kilobytes. Quando um PDF estoura 10 MB, a causa está quase sempre em um destes pontos:

Causa Sintoma Correção
Imagens em resolução de câmera Logo de 4.000 px, fotos anexadas sem tratamento Redimensionar para 150-200 dpi no tamanho impresso; JPEG para fotos, PNG só para gráficos
Fontes completas embutidas Cada família adiciona centenas de KB Deixar o renderizador fazer subset (padrão em Chromium e WeasyPrint); usar poucas famílias
Páginas escaneadas Anexos digitalizados em cores a 600 dpi Escala de cinza ou preto e branco, 200-300 dpi, compressão adequada
PDF/A com anexos e metadados Arquivo maior que o esperado PDF/A é válido, mas embute mais; use só se a sua política de arquivo exigir
Base64 no corpo O corpo da requisição fica ~33% maior que o arquivo Para arquivos grandes, prefira o upload direto por presign

Ferramentas como qpdf e Ghostscript reduzem PDFs já gerados, mas o melhor lugar para tratar tamanho é na origem: imagens tratadas antes de entrar no template poupam um passo no pipeline e evitam um arquivo com aparência degradada.

Guarde o seu hash antes de enviar

Ao receber o documento, a plataforma calcula e registra o SHA-256 do arquivo armazenado, e esse hash acompanha a transação e as evidências. Você deveria calcular o mesmo hash no seu lado, no instante em que o PDF sai do pipeline de geração, e gravá-lo junto do registro que originou o contrato.

O motivo não é desconfiar da transmissão; é ter uma âncora independente. Se um dia alguém perguntar "o documento que foi assinado é exatamente o que o nosso sistema gerou na data X?", a resposta vem de comparar o hash que você guardou com o da transação, sem depender de ninguém. Também é a forma mais simples de detectar um bug no pipeline (uma versão do template que mudou entre a geração e o envio, por exemplo).

// Hash na origem, gravado antes de qualquer chamada à API import { createHash } from 'node:crypto'; const pdf = await renderContractPdf(html); const sha256 = createHash('sha256').update(pdf).digest('hex'); await db.contracts.update(contractId, { pdfSha256: sha256, pdfBytes: pdf.length, renderedAt: new Date() }); if (pdf.length > 10 * 1024 * 1024) throw new Error('PDF acima de 10 MB; revise imagens e fontes');

Base64 no corpo ou upload direto

Para a maioria dos contratos, enviar o PDF em base64 no próprio POST /v1/signing-sessions é o caminho mais curto: uma chamada, e a resposta já traz url, clientSecret e expiresAt. Quando o arquivo é grande, ou quando você prefere não carregar bytes de documento no mesmo serviço que cria sessões, use o fluxo de presign:

  1. Crie a sessão sem o campo document. A transação nasce no estado CREATED.
  2. Chame POST /v1/transactions/{transactionId}/document/presign com { "filename": "contrato.pdf", "contentType": "application/pdf" }. A resposta traz uploadUrl, uploadToken e expiresIn: 600.
  3. Faça um PUT dos bytes do PDF em uploadUrl, dentro dos 600 segundos, com o mesmo Content-Type.
  4. Confirme com POST /v1/transactions/{transactionId}/document/confirm enviando { "uploadToken": "..." }. O servidor calcula o SHA-256 e a transação passa a DOCUMENT_UPLOADED.
# Presign com curl (o access_token vem de POST /oauth2/token) curl -X POST https://api-hml.signdocs.com.br/v1/transactions/$TX/document/presign \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"filename":"contrato-CT-2026-0917.pdf","contentType":"application/pdf"}' # → { "uploadUrl": "...", "uploadToken": "...", "expiresIn": 600, ... } curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @contrato-CT-2026-0917.pdf curl -X POST https://api-hml.signdocs.com.br/v1/transactions/$TX/document/confirm \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d "{\"uploadToken\":\"$UPLOAD_TOKEN\"}"

Se o PUT falhar ou o token expirar, peça um novo presign; o fluxo é seguro para repetir enquanto a transação não tiver documento confirmado. Os códigos de erro dessas chamadas seguem o formato RFC 7807 descrito em códigos de erro e tratamento de falhas.

Nunca envie um PDF que já foi assinado

Um erro comum em migrações: pegar um PDF que já saiu de outra plataforma (ou de uma assinatura anterior) e reenviá-lo para "colher mais uma assinatura". Não faça isso. A assinatura anterior está embutida naquele arquivo, e uma nova assinatura sobre ele cria um documento cuja cadeia de evidências mistura dois processos, dois conjuntos de signatários e, muitas vezes, dois hashes de referência. Quem for validar depois não conseguirá dizer com clareza o que foi assinado por quem.

Se há várias pessoas assinando o mesmo contrato, o caminho é um envelope: um documento, vários signatários, em paralelo ou em sequência, com uma trilha de auditoria só. Se há um aditivo a um contrato já assinado, o aditivo é um novo documento (que pode referenciar o original pelo número e pelo hash) e um novo envelope. O guia de ordem de assinatura com múltiplos signatários mostra os dois modos.

Um documento por envelope: junte antes

Cada envelope carrega exatamente um documento. Se o seu fluxo tem contrato + anexo I + anexo II, você tem duas opções honestas: um envelope por arquivo (cada um assinado e evidenciado separadamente) ou um único PDF combinado, montado antes do envio. A segunda costuma ser o que o negócio quer, já que "o contrato" para as partes é o conjunto.

Juntar PDFs é trivial em qualquer stack (pdf-lib em Node, pypdf em Python, qpdf --empty --pages ... -- out.pdf na linha de comando). Ao combinar, mantenha uma ordem fixa e documentada, adicione numeração contínua de páginas se o template não tiver, e calcule o hash do arquivo combinado, que é o que será assinado.

Onde a assinatura aparece no PDF

Vale dizer com clareza, porque é uma pergunta frequente de quem vem de outras ferramentas: não há API de posicionamento de campos. Você não indica coordenadas nem marca "assine aqui" no PDF. A plataforma aplica o seu próprio carimbo de assinatura ao documento assinado, combinando os signatários da transação, e a prova do ato está na trilha de auditoria e no pacote de evidências, não no desenho sobre a página.

Na prática, isso simplifica o seu template: reserve espaço no fim do documento para o carimbo, não desenhe linhas de "assinatura" e não peça ao signatário para "rubricar" páginas. A validação de quem assinou, quando e com qual método é feita pelo verificador público a partir do evidenceId, e isso vale igual para um contrato de uma página ou de cem.

Higiene de nomes de arquivo e metadados

  • Extensão coerente com o conteúdo: é a extensão de filename que determina como o arquivo será tratado. Um PDF nomeado contrato.docx passará por conversão desnecessária; um DOCX nomeado .pdf falhará.
  • Nomes previsíveis e sem dados pessoais: contrato-CT-2026-0917.pdf, não contrato-maria-silva-cpf-123.pdf. O nome aparece para o signatário e em logs.
  • Metadados do PDF: preencha título e produtor com algo útil (o nome do seu sistema e a versão do template), e limpe campos herdados de arquivos de origem.
  • Sem proteção por senha ou restrições de edição no PDF: um PDF protegido por senha ou criptografado é rejeitado na validação do documento; gere-o limpo, sem permissões travadas.

Checklist antes de chamar a API

  • PDF gerado pelo seu pipeline a partir de HTML ou DOCX, com recursos embutidos, sem URLs externas.
  • Tamanho abaixo de 10 MB; imagens tratadas na origem.
  • Se há anexos, um único PDF combinado, em ordem fixa.
  • SHA-256 calculado e gravado no seu banco junto do registro de origem.
  • Nome de arquivo com extensão correta e sem dados pessoais.
  • Nenhuma assinatura anterior embutida; aditivos são novos documentos.
  • Espaço reservado para o carimbo; nenhuma linha de assinatura desenhada.
  • Base64 no corpo para o caso comum; presign + PUT + confirm para arquivos grandes.
  • Tudo testado primeiro no sandbox (api-hml.signdocs.com.br), gratuito e sem cartão.

Perguntas Frequentes

A API tem templates de contrato ou campos para preencher?

Não. A API assina o documento que você envia e não oferece templates, mesclagem de campos nem editor. O documento é regra de negócio sua: gere-o no seu pipeline a partir de um template HTML (Puppeteer, Playwright, WeasyPrint) ou de um DOCX com marcadores (docxtemplater, python-docx) convertido para PDF, e só então crie a sessão ou o envelope. Isso mantém o conteúdo, a versão do template e o hash sob seu controle.

Preciso enviar PDF ou a API aceita Word e imagens?

A API aceita PDF, formatos Office e OpenDocument (.docx, .doc, .xlsx, .xls, .pptx, .ppt, .odt, .ods, .odp, .rtf), texto e dados (.txt, .csv, .xml, .json, .html) e imagens (.jpg, .jpeg, .png, .tiff, .tif, .bmp, .svg), sempre até 10 MB. O que não é PDF é convertido para PDF antes da assinatura. Para produção a recomendação é gerar e enviar o PDF você mesmo: o que o signatário vê é exatamente o que você renderizou e revisou.

Qual é o limite de tamanho e como fico abaixo dele?

10 MB, tanto no envio em base64 no corpo da criação quanto no upload direto por presign. Quando um contrato estoura esse limite, a causa quase sempre está em imagens em resolução de câmera, fontes completas embutidas ou páginas escaneadas em alta resolução e em cores. Trate as imagens na origem (150-200 dpi no tamanho impresso), deixe o renderizador fazer subset das fontes e digitalize anexos em escala de cinza. Lembre que base64 aumenta o corpo em cerca de um terço; para arquivos grandes, use o presign.

Para que guardar o hash do PDF se a plataforma já calcula?

A plataforma registra o SHA-256 do documento armazenado e ele acompanha a transação e as evidências. Guardar o seu próprio hash, calculado no instante em que o PDF sai do pipeline, dá uma âncora independente: para confirmar que o documento assinado é exatamente o que o seu sistema gerou naquela data, basta comparar os dois valores, sem depender de ninguém. Também é a forma mais simples de detectar um template que mudou entre a geração e o envio.

Como funciona o upload direto por presign?

Crie a sessão sem o campo document; a transação nasce como CREATED. Chame POST /v1/transactions/{id}/document/presign com filename e contentType e receba uploadUrl, uploadToken e expiresIn de 600 segundos. Faça um PUT dos bytes em uploadUrl com o mesmo Content-Type e confirme com POST /v1/transactions/{id}/document/confirm enviando o uploadToken. O servidor calcula o SHA-256 e a transação passa a DOCUMENT_UPLOADED. Se o PUT falhar ou o token expirar, peça um novo presign.

Posso indicar onde a assinatura aparece no PDF?

Não há API de posicionamento de campos: você não informa coordenadas nem marca lugares para assinar. A plataforma aplica o seu próprio carimbo de assinatura ao documento assinado, combinando os signatários da transação, e a prova do ato está na trilha de auditoria e no pacote de evidências, verificáveis no verificador público pelo evidenceId. No template, reserve espaço no fim do documento para o carimbo e não desenhe linhas de assinatura nem peça rubricas.

Teste o seu pipeline de PDF no sandbox

Gere o contrato no seu sistema, envie em base64 ou por presign e acompanhe o hash e as evidências no ambiente de homologação gratuito, sem cartão e com retenção de 7 dias. O plano de produção é dimensionado sob medida com o time comercial.

Criar credenciais de homologação Fale com o time comercial