Assinatura Digital em Next.js: Guia Completo com App Router
Next.js coloca servidor e cliente no mesmo repositório — o que torna a integração de assinatura digital ao mesmo tempo mais simples e mais traiçoeira: simples porque uma Server Action resolve o que antes pedia um backend separado; traiçoeira porque a fronteira entre o que roda no servidor e o que vai para o navegador decide se a sua credencial está segura ou embutida no bundle. Este guia monta a integração completa do jeito Next.js: cliente singleton server-only, criação de sessão em Server Action, webhook em Route Handler com corpo bruto — e os erros específicos do framework que custam tardes de debugging.
Este guia assume o App Router (Next.js 13+). A base conceitual — OAuth2, sessões, webhooks — é a mesma do guia de Node.js/TypeScript; aqui o foco é o que muda dentro do framework.
Regra zero: a credencial nunca atravessa a fronteira
NEXT_PUBLIC_ para "resolver" um undefined. Variáveis com esse prefixo são embutidas no JavaScript enviado a todo visitante — o seu client_secret vira público no momento do build. Credenciais da SignDocs ficam em variáveis sem prefixo (SIGNDOCS_CLIENT_ID, SIGNDOCS_CLIENT_SECRET), acessíveis apenas em código de servidor. Se um componente cliente "precisa" delas, o desenho está errado — ele deve chamar uma Server Action que as usa do lado certo.
Instale o SDK oficial e crie o cliente num módulo que só o servidor importa:
O pacote server-only transforma um vazamento silencioso em erro de build — barato demais para não usar.
Criar a sessão: Server Action (ou Route Handler)
Para um botão "Enviar para assinatura" no seu próprio front-end, a Server Action é o caminho idiomático — sem endpoint público extra:
Prefira um Route Handler (app/api/assinaturas/route.ts) quando o gatilho vier de fora do seu front-end — um CRM, um job agendado — ou quando você quiser um contrato HTTP explícito para testar. O corpo da função é idêntico; muda só a casca.
Redirect ou embutido? O retorno acima serve aos dois: redirecione o usuário para o signingLink (página hospedada + returnUrl de volta), ou passe o clientSecret a um componente cliente que abre o checkout em pop-up com a biblioteca @signdocs-brasil/js — o segredo da sessão pode ir ao cliente; a credencial da conta, nunca. O trade-off completo está em checkout hospedado vs assinatura incorporada.
O webhook: corpo bruto ou nada
A verificação HMAC-SHA256 é calculada sobre o corpo bruto — leia com request.text() antes de qualquer parse:
Os três tropeços específicos de Next.js/serverless: (1) parsear antes de verificar — re-serializar muda bytes e a assinatura nunca bate; (2) runtime Edge — declare runtime = 'nodejs'; (3) timeout serverless — o handler confirma rápido e delega o trabalho pesado (download do PDF, geração de PDF, e-mails) a uma fila ou job, porque a plataforma pode encerrar a função logo após a resposta.
Desenvolvimento local e teste
- Sandbox: tudo acima contra
api-hml.signdocs.com.brcom credenciais de homologação — gratuito, sem efeito jurídico, TTL de 7 dias. Detalhes. - Webhook no localhost: exponha a porta com um túnel e registre a URL pública como endpoint.
- Evento de teste sob demanda:
POST /v1/webhooks/{webhookId}/testenvia um payload assinado de verdade ao seu endpoint — valide o handler sem criar uma sessão inteira. - Variáveis por ambiente:
SIGNDOCS_BASE_URLtroca HML→produção sem tocar em código; credenciais de produção só existem no ambiente de produção.
Perguntas Frequentes
Posso chamar a API de assinatura direto de um componente React?
Não — e o Next.js torna fácil fazer certo. As credenciais OAuth2 (client_id e client_secret) nunca podem chegar ao navegador, então toda chamada à API de assinatura acontece no servidor: numa Server Action ou num Route Handler. O componente cliente apenas dispara a ação e recebe de volta o link de assinatura pronto. A armadilha clássica é prefixar a credencial com NEXT_PUBLIC_ para 'fazer funcionar' — isso embute o segredo no bundle JavaScript enviado a todo visitante. Variáveis sem o prefixo ficam apenas no servidor, que é exatamente onde elas devem viver.
Server Action ou Route Handler — qual usar para criar a sessão?
Os dois funcionam e usam o mesmo código de integração. A Server Action é o caminho idiomático do App Router quando o gatilho é um formulário ou botão do seu próprio front-end — menos boilerplate, sem endpoint público extra. O Route Handler (app/api/.../route.ts) é a escolha quando outros sistemas também precisam disparar o fluxo (um CRM, um job) ou quando você quer um contrato HTTP explícito e testável. Em ambos, o padrão é o mesmo: instancie o cliente uma vez num módulo server-only e importe onde precisar.
Como recebo webhooks de assinatura no Next.js?
Com um Route Handler POST — e um cuidado essencial: a verificação HMAC-SHA256 é calculada sobre o corpo bruto da requisição, então leia com await request.text() e só depois faça o JSON.parse. Se você parsear primeiro e re-serializar, a assinatura não bate. Use o verifyWebhookSignature do SDK @signdocs-brasil/api (comparação timing-safe + checagem anti-replay do timestamp), responda 200 rapidamente e processe o evento de forma assíncrona. Garanta runtime Node no handler; e lembre que em plataformas serverless o handler precisa concluir o trabalho crítico antes de responder — para processamento pesado, enfileire.
Funciona na Vercel e em outras plataformas serverless?
Sim — a integração é HTTP puro e roda em qualquer lugar onde Next.js roda. Os pontos de atenção serverless: configure as credenciais como variáveis de ambiente do projeto (sem NEXT_PUBLIC_), use runtime Node nos handlers que fazem criptografia, e trate o tempo de execução — funções serverless têm timeout, então o webhook deve confirmar rápido e delegar trabalho pesado. O token OAuth2 de 15 minutos é obtido e cacheado pelo SDK por instância; em ambiente serverless com cold starts, isso significa apenas uma chamada extra de token ocasional, sem impacto prático.
Como abro o fluxo de assinatura na interface — redirect ou embutido?
Dois caminhos. O mais simples é o redirect: a Server Action devolve o link (url + clientSecret como ?cs=) e você redireciona ou mostra o botão — o usuário assina na página hospedada e volta pelo returnUrl. Para manter o usuário dentro do seu app, use o pop-up da biblioteca @signdocs-brasil/js no componente cliente: o servidor cria a sessão e passa o clientSecret ao componente, que abre o checkout embutido e recebe o callback de conclusão. O clientSecret dessa sessão específica pode ir ao cliente — ele é o segredo da sessão, não a credencial da sua conta.
Como testo tudo isso antes de produção?
No sandbox de homologação: aponte a base URL para api-hml.signdocs.com.br, use credenciais de homologação e rode o fluxo completo — sessão, assinatura, webhook — sem custo e sem efeito jurídico. Para o webhook em desenvolvimento local, exponha seu localhost com um túnel (a URL pública do túnel vira o endpoint registrado) e dispare um evento de teste com POST /v1/webhooks/{webhookId}/test, que envia um payload assinado de verdade ao seu endpoint. As entidades de homologação expiram em 7 dias — recrie os cenários conforme necessário.
Do npm install à primeira assinatura, ainda hoje
Credenciais de homologação gratuitas, SDK oficial com OAuth2 automático e webhook de teste sob demanda — a integração Next.js completa no sandbox, sem cartão.
Criar credenciais de homologação Falar com o time comercial