Assinatura Incorporada (Embedded / iframe) na Sua Aplicação via API
Redirecionar o usuário para uma página externa de assinatura funciona, mas quebra a experiência do seu produto. A assinatura incorporada (embedded signing) resolve isso: o checkout de assinatura é aberto de dentro da sua aplicação, pelo SDK oficial, mantendo o usuário no seu contexto e no seu fluxo. Este guia mostra, passo a passo, como criar a signing session, obter o clientSecret, abrir o checkout com o SDK @signdocs-brasil/js, tratar os callbacks de conclusão, confirmar tudo pelo webhook — e por que o modelo de popup do SignDocs substitui o iframe clássico que dá nome a esta página.
O ponto de partida é a mesma chamada da Assinatura Expressa: uma única requisição POST /v1/signing-sessions. A diferença entre checkout hospedado e embedded não está em endpoints distintos, e sim em onde você apresenta a URL devolvida. Este artigo é parte do cluster prático da nossa API de assinatura digital.
Vamos do conceito ao código: requisição, abertura segura do checkout (com a regra crítica do clientSecret), os callbacks de evento, a finalização e a confirmação server-to-server. A criptografia profunda (PAdES, ICP-Brasil, evidence pack) é tratada na página da Expressa; aqui o foco é a experiência incorporada.
Embedded vs. redirecionamento: quando incorporar faz sentido
Existem dois jeitos de levar um signatário até a tela de assinatura a partir da sua aplicação. No redirecionamento (checkout hospedado), você manda o usuário para uma página hospedada pela SignDocs, em outro domínio, e o traz de volta no fim. Na assinatura incorporada, essa mesma tela é aberta pelo SDK a partir da sua própria página — um checkout em popup controlado pela sua aplicação — e o usuário não abandona a jornada do seu produto.
| Critério | Checkout hospedado (redirect) | Assinatura incorporada (embedded) |
|---|---|---|
| Onde o usuário assina | Página hospedada, em outro domínio | A partir do seu app, em popup controlado por ele |
| Branding / UX | Personalização limitada à página hospedada | Controle total do layout ao redor do widget |
| Esforço de integração | Mínimo: pegar a URL e redirecionar | Baixo: chamar o SDK + tratar callbacks |
| Continuidade de sessão | Usuário sai e volta para o seu app | Usuário permanece na mesma página |
| Confirmação do resultado | Webhook + URL de retorno | Webhook + callbacks do SDK (UX) |
| Ideal para | Fluxos simples, e-mails, links avulsos | SaaS, dashboards, jornadas de produto |
POST /v1/signing-sessions. Escolha embedded quando a assinatura faz parte de uma jornada dentro do seu produto e você não quer que o usuário perceba que saiu dele.
Passo 1 — Criar a signing session
Tudo começa com uma chamada autenticada à API. Você precisa de um bearer token obtido via OAuth2 client-credentials — veja o detalhamento em autenticação OAuth2. Em homologação, use o host api-hml.signdocs.com.br; lembre que entidades de HML têm TTL de 7 dias.
A requisição declara o documento, o signatário e a política de autenticação (profile). No exemplo abaixo, criamos uma sessão de assinatura simples com aceite (clickwrap) e desafio OTP por e-mail:
Repare que não há nenhum campo de "modo": a mesma sessão serve para o checkout hospedado e para o embed — a diferença está só em como você apresenta o resultado no passo 3. Note também que policy.profile aceita valores como CLICK_PLUS_OTP ou DIGITAL_CERTIFICATE (certificado ICP-Brasil) — e nunca DIGITAL_SIGN_A1, que existe apenas como step.type na resposta.
Passo 2 — Obter a embed URL e o clientSecret
A resposta da API traz a sessão criada. Dois campos são essenciais: url (a página de assinatura hospedada) e clientSecret (a credencial daquela sessão — é ela que o SDK usa para abrir o checkout):
url sozinho não é o link de assinatura. No modo hospedado, o link final é url + "?cs=" + clientSecret; no embed, é o clientSecret que você entrega ao SDK. Sem ele, a sessão não autentica e a tela de assinatura não carrega.
Em código, a montagem fica assim:
O clientSecret é sensível: trate-o como segredo, não o registre em logs públicos nem o exponha em URLs compartilháveis. Ele só deve viajar do seu backend até o navegador do signatário no momento de abrir o checkout.
Passo 3 — Abrir o checkout com o SDK (com a guarda de segurança do remetente)
Antes de abrir o checkout, há uma checagem de segurança que você não pode pular. O clientSecret dá acesso de assinatura àquela sessão. Se a sua aplicação abrir o checkout automaticamente para quem está enviando o documento, o remetente acabará assinando no lugar do signatário — destruindo a validade jurídica da prova.
signerEmail === senderEmail. Se forem diferentes, não abra a sessão no navegador do remetente — entregue o link ao signatário correto por e-mail ou outro canal. Esse padrão vale para qualquer integração onde quem cria a sessão pode não ser quem assina.
Com a guarda passada, abrir o checkout é direto — instale o SDK (npm install @signdocs-brasil/js) e chame checkout() em resposta a um clique do usuário:
O popup roda no domínio seguro da SignDocs, o que resolve sozinho as permissões de câmera e microfone quando o perfil usa biometria facial — sem atributos allow nem ajustes de Content-Security-Policy do seu lado. Duas atenções práticas: chame checkout() em resposta direta a um clique (bloqueadores de popup barram aberturas espontâneas) e sirva sua aplicação sobre HTTPS.
E o iframe? Por que o embed é um popup
Se você chegou aqui procurando "assinatura por iframe": o SDK do SignDocs já ofereceu um mount() em elemento da página, hoje depreciado — internamente ele também abre o popup. A razão é técnica: dentro de um iframe cross-origin, as permissões de câmera/microfone exigidas pela biometria dependem de atributos e políticas frágeis, e as proteções modernas contra clickjacking tornam o embed em moldura cada vez mais restrito. O popup preserva o mesmo efeito de produto — o usuário não perde a sua página — com um contexto seguro de primeira classe. E a ponte postMessage que você implementaria à mão no modelo iframe existe por baixo do SDK (eventos como SIGNDOCS_READY, SIGNDOCS_STEP_CHANGE, SIGNDOCS_COMPLETE), só que já validada e abstraída em callbacks.
Passo 4 — Tratar os callbacks do checkout
O checkout conversa com a sua página pelos callbacks do SDK (que encapsulam a ponte postMessage do navegador, incluindo a validação de origem). Conforme o usuário avança, sua aplicação reage para atualizar a UX — exibir uma tela de sucesso, registrar abandono, oferecer nova tentativa.
Callbacks do checkout
| Callback | Quando ocorre | Reação sugerida na UX |
|---|---|---|
onComplete(evt) |
O signatário concluiu a assinatura — evt.evidenceId identifica a evidência |
Exibir sucesso; aguardar confirmação por webhook |
onError(err) |
Erro durante o fluxo (sessão expirada, falha de rede) — err.message/err.code |
Exibir erro; oferecer recriar a sessão |
onClose() |
O signatário fechou o checkout sem concluir | Registrar abandono; manter o botão "assinar" disponível |
Na prática, os callbacks já chegam validados pelo SDK — sem listener manual nem checagem de event.origin do seu lado:
onComplete do checkout.
Passo 5 — Finalização e confirmação por webhook
Quando o signatário conclui todos os passos no checkout, a SignDocs finaliza a transação do lado servidor: gera o PDF assinado (PAdES, nível baseline), registra os carimbos de hora do servidor (timestamps ISO-8601 gravados pelos servidores da SignDocs) e monta o pacote de evidências. A sua aplicação fica sabendo disso de forma confiável pelo webhook — o mesmo mecanismo descrito em webhooks e eventos da API de assinatura.
O fluxo de confirmação combina os dois canais: o onComplete dá um feedback imediato ao usuário; o webhook confirma o estado de forma auditável e dispara as ações de negócio (faturamento, atualização de CRM, liberação de acesso).
Valide a assinatura HMAC-SHA256 do webhook (cabeçalho X-SignDocs-Signature), aplique idempotência pelo id do evento e só então execute a lógica de negócio. O payload não carrega os seus metadados: use o transactionId para buscar a transação (GET /v1/transactions/{id}, que devolve o metadata.internal_ref enviado na criação) e casar o evento com o registro correto no seu sistema. Dali você baixa o PDF assinado e o pacote .p7m (via GET .../evidence) — e qualquer parte pode validar publicamente em verificador.signdocs.com.br.
Métodos de autenticação dentro do checkout
Uma dúvida comum é se o embedding limita os métodos de assinatura. Não limita: os métodos são definidos no profile e nos steps da signing session, independentemente de a tela ser aberta por link hospedado ou pelo SDK. Todos os fluxos rodam normalmente no checkout:
- CLICK_ACCEPT — aceite eletrônico (clickwrap), com registro de consentimento.
- OTP_CHALLENGE — código de uso único por SMS ou e-mail.
- BIOMETRIC_LIVENESS / BIOMETRIC_MATCH — prova de vida e conferência facial (o popup cuida das permissões de câmera sozinho).
- DIGITAL_SIGN_A1 — assinatura com certificado ICP-Brasil A1 (em arquivo; titulares de token A3 usam o assinador desktop do aplicativo).
Esses passos podem ser combinados em perfis (por exemplo, biometria + OTP). O detalhamento de cada método, das combinações possíveis e de como o nível probatório muda está no guia de autenticação multimétodo na API de assinatura. A camada criptográfica (PAdES/CAdES, PKCS#7/CMS, hash SHA-256 e cadeia de certificados ICP-Brasil) é a mesma da assinatura padrão e está coberta na página da Assinatura Expressa.
Boas práticas e armadilhas comuns no embed
Erros frequentes
- Esquecer o
clientSecret: usar só o campourlfaz a tela não carregar. No hospedado, combineurl+?cs=; no embed, entregue o secret ao SDK. - Auto-abrir para o remetente: sem a guarda
signerEmail === senderEmail, quem envia assina no lugar de quem deveria. - Confiar só no callback: tratar
onCompletecomo prova final, em vez do webhook server-to-server. - Abrir o checkout fora de um clique: chamadas espontâneas a
checkout()esbarram no bloqueador de popups — sempre abra em resposta a um gesto do usuário. - Logar o
clientSecret: ele é sensível; vazá-lo em logs ou URLs compartilhadas é um risco. - Confundir sessões: o
clientSecretpertence a uma sessão específica; expirou (SIGNING_SESSION.EXPIRED)? Crie uma nova sessão.
Recomendações
- Desabilite o botão "assinar" enquanto o checkout está aberto e reative-o no
onClose. - Guarde o
handleretornado porcheckout()—handle.close()encerra o fluxo programaticamente se a sua tela mudar. - Sirva sua aplicação sobre HTTPS; o checkout roda no domínio seguro da SignDocs.
- Para enviar documentos a terceiros (sem embed), use o checkout hospedado — mesmo endpoint, entrega por link.
- Em HML, lembre do TTL de 7 dias das entidades ao testar fluxos longos.
Perguntas Frequentes
Qual a diferença entre assinatura incorporada (embedded) e checkout hospedado?
No checkout hospedado, você redireciona o signatário para uma página de assinatura totalmente hospedada pela SignDocs, em outro domínio. Na assinatura incorporada (embedded), o checkout é aberto de dentro da sua própria aplicação pelo SDK oficial @signdocs-brasil/js — um popup controlado pelo seu app, com callbacks de conclusão — mantendo o usuário na sua jornada. Ambos partem da mesma chamada POST /v1/signing-sessions; o que muda é como o resultado é apresentado: redirecionar para url?cs=clientSecret, ou entregar o clientSecret ao SDK. O embedded é ideal quando a assinatura faz parte de uma jornada dentro do produto; o hospedado, quando o link viaja por e-mail ou WhatsApp.
Como monto a URL de assinatura para usar no iframe?
A URL retornada no campo url da signing session NÃO é o link final por si só. No modo hospedado, combine-a com o clientSecret como parâmetro de query: url + '?cs=' + clientSecret. No embed, você nem monta URL — entrega o clientSecret diretamente ao SDK (sd.checkout({clientSecret})), que abre o checkout. Em ambos os casos, sem o clientSecret a sessão não autentica e a assinatura não carrega; trate-o como segredo e nunca o exponha em logs públicos.
Como minha aplicação sabe quando o usuário terminou de assinar dentro do iframe?
Pelos callbacks do SDK: onComplete (com o evidenceId da assinatura), onError (mensagem e código) e onClose (usuário fechou sem concluir). A ponte postMessage do navegador existe por baixo, mas o SDK a valida e abstrai — você não registra listener nem confere event.origin manualmente. Para confirmação definitiva e auditável, porém, nunca confie só no callback do navegador: trate-o como sinal de UX e confirme o estado final pelo webhook HTTPS server-to-server, que é a fonte de verdade.
Por que não devo abrir automaticamente a URL de assinatura para o remetente?
O clientSecret da signing session dá acesso de assinatura àquela sessão. Se a sua aplicação abrir o checkout automaticamente para quem está enviando o documento, o remetente acabaria assinando no lugar do signatário, corrompendo a prova jurídica. A regra de segurança é gatear a abertura: só chame o checkout quando signerEmail for igual a senderEmail (ou seja, quando o próprio usuário logado é o signatário). Caso contrário, entregue o link ao signatário correto por e-mail ou outro canal.
Preciso de algum cabeçalho especial para permitir o iframe?
Não. Como o checkout abre em popup no domínio seguro da SignDocs — e não em um iframe na sua página — você não precisa ajustar Content-Security-Policy nem cabeçalhos de frame do seu lado. Garanta apenas HTTPS na sua aplicação e abra o checkout em resposta a um clique do usuário, para não esbarrar no bloqueador de popups. Em ambiente de homologação, use o host api-hml.signdocs.com.br e lembre que as entidades de HML têm TTL de 7 dias.
O embedded suporta certificado ICP-Brasil e biometria dentro do iframe?
Sim. Os métodos de autenticação são definidos no profile e nos steps da signing session, não na forma de apresentação. Perfis como CLICK_ONLY, CLICK_PLUS_OTP, BIOMETRIC/BIOMETRIC_PLUS_OTP e DIGITAL_CERTIFICATE (certificado ICP-Brasil A1; token A3 usa o assinador desktop do aplicativo) rodam todos no checkout aberto pelo SDK — o popup cuida sozinho das permissões de câmera para a biometria. O detalhamento de cada perfil está na seção de métodos de autenticação de o que é uma API de assinatura digital.
Incorpore a assinatura digital direto no seu app
Crie a signing session em uma chamada, abra o checkout com o SDK oficial usando o clientSecret e confirme tudo por webhook — ICP-Brasil, OTP e biometria, sem tirar o usuário do seu produto. O acesso à API é um plano sob medida, com sandbox de homologação gratuito.
Fale com o time comercial Fale com nossa equipe sobre a API