SignXP Docs

Assinatura dentro do seu sistema

Por padrão, o signatário recebe um link e assina numa página nossa. Quando o seu software já é a tela em que o cliente final está, mandá-lo para fora é uma quebra no meio do fluxo — e cada saída é uma chance de a pessoa não voltar.

Há três formas de resolver isso, da mais barata para a mais trabalhosa:

Quem faz a tela O que você escreve Evidência
Domínio próprio nós nada nossa responsabilidade
Assinador embutido nós, dentro da sua tela um <script> nossa responsabilidade
Fluxo próprio você a interface inteira depende do seu fluxo

O assinador embutido é o caminho recomendado: o cliente não sai do seu sistema, a marca em volta é a sua, e o fluxo de assinatura continua sendo nosso — correções e etapas novas chegam sozinhas, sem você republicar nada.

Assinador embutido

Carregue o widget e abra a signing_url do signatário numa janela modal:

<script src="https://app.signxp.com.br/embed/signer.js"></script>
<script>
  SignXP.openSigner({
    url: signingUrl,
    onSigned: function () {
      // Assinou. Atualize a sua tela.
    },
    onRejected: function () {
      // Recusou. O documento foi encerrado.
    },
    onCancel: function () {
      // Fechou sem decidir. O link continua valendo.
    }
  });
</script>

Para embutir sem modal, dentro de um elemento seu:

SignXP.mountSigner({
  url: signingUrl,
  container: '#area-de-assinatura',
  onSigned: function () { /* ... */ }
});
Método Parâmetros
SignXP.openSigner url (obrigatório), showDocument, onSigned, onRejected, onCancel. Devolve { close() }.
SignXP.mountSigner url e container (obrigatórios), showDocument, onSigned, onRejected. Devolve { destroy() }.

O endereço https://app.signxp.com.br/embed/signer.js é fixo e versionado por nós — não copie o arquivo para o seu servidor, ou deixará de receber correções. Ele convive com o positioner.js: os dois penduram métodos no mesmo objeto SignXP.

Quando você já mostra o documento

Por padrão o assinador abre numa etapa de revisão, com o PDF dentro dele. Se a sua tela já exibe o documento — num visualizador ao lado, numa aba própria, num painel do seu sistema —, essa etapa vira uma segunda cópia do mesmo PDF e empurra a assinatura para baixo. Desligue-a:

SignXP.mountSigner({
  url: signingUrl,
  container: '#area-de-assinatura',
  showDocument: false,
  onSigned: function () { /* ... */ }
});

O que muda:

  • a etapa de revisão sai do fluxo, e o assinador abre direto na etapa seguinte (consentimento, localização, fotos — o que a sua licença coletar);
  • o PDF não é baixado pelo iframe, então a tela carrega mais leve;
  • na hora de assinar continua havendo um link discreto para abrir o documento. Ele fica: quem assina precisa poder conferir o que assina, e o seu visualizador pode estar fora do campo de visão naquele momento.

Só vale embutido. Se o signatário abrir a signing_url direto no navegador, a etapa de revisão volta — fora do seu sistema não existe outra tela mostrando o documento, e assinar sem ele à vista não é opção.

De onde sai a signing_url

Da consulta de status, no campo signing_url de cada signatário.

Se a assinatura vai acontecer na sua tela, o nosso convite por e-mail só atrapalha — a pessoa recebe um link que leva para fora do seu sistema. Duas formas de calá-lo, as duas descritas em enviar um documento:

  • delivery: "none" no envio, que suprime o convite de todos os signatários daquele documento;
  • omitir o email do signatário, quando ele simplesmente não tem um.

Autorize a sua origem

Por padrão a página pode ser embutida em qualquer site: o segredo é o token da URL. Para restringir — recomendado —, cadastre as origens autorizadas da sua licença em Configurações, no portal. O navegador passa a recusar o iframe em qualquer outro lugar.

Uma origem por linha, com esquema:

https://sistema.meu-erp.com.br
https://app.meu-erp.com.br

Isso não exige apontar domínio nenhum para nós: autorizar uma origem e ter um domínio de white-label são coisas separadas.

Câmera e geolocalização

Quando a sua licença coleta foto do documento, selfie ou localização, o navegador precisa que a página de fora delegue essas permissões ao iframe. O widget já faz isso (allow="camera; geolocation"). O que você precisa garantir é que a sua própria página seja servida em HTTPS — sem isso o navegador bloqueia a câmera antes de chegar em nós.

Quando o cliente está no computador

Se a licença coleta imagens e a pessoa abriu no desktop, o fluxo mostra um QR Code para ela continuar no celular, e a tela embutida fica aguardando. O onSigned dispara quando ela conclui pelo telefone — você não precisa tratar o handoff.

Fluxo próprio, sobre a API de sessão

Se você quer desenhar a experiência inteira, a mesma API que a nossa página consome está aberta para você. A autenticação é o próprio signing_token da URL — não use a sua chave de API aqui: essas chamadas saem do navegador do signatário.

GET  /api/signing/{token}                 estado inicial da sessão
GET  /api/signing/{token}/status          o que já foi coletado (usado no handoff)
GET  /api/signing/{token}/document        redireciona para o PDF
POST /api/signing/{token}/consent         registra o aceite do termo
POST /api/signing/{token}/tax-id          CPF/CNPJ autodeclarado
POST /api/signing/{token}/geo             latitude, longitude, accuracy, denied
POST /api/signing/{token}/photo           kind + image (multipart)
POST /api/signing/{token}/validate-image  pede o veredito da IA para uma imagem
POST /api/signing/{token}/signature       image + type (drawn|typed)
POST /api/signing/{token}/initials        image + type (drawn|typed)
POST /api/signing/{token}/progress        salva a etapa, para retomar depois
POST /api/signing/{token}/complete        conclui a assinatura
POST /api/signing/{token}/reject          recusa, com motivo opcional

O GET inicial devolve tudo o que a tela precisa decidir:

{
  "document": { "title": "Contrato", "is_test": false, "verification_url": "...", "download_enabled": true },
  "signer": { "name": "Maria Souza", "email": "maria@empresa.com.br", "tax_id": "529.982.247-25", "status": "pending" },
  "brand": { "name": "Acme", "color": "#00a3ff", "logo": "..." },
  "state": "open",
  "collect": { "document": true, "selfie": true, "geo": true, "initials": false, "tax_id": false },
  "consent": { "required": true, "version": "1", "text": "Declaro, para os devidos fins..." },
  "progress": { "current_step": "geo", "photos": {}, "has_signature": false, "has_initials": false }
}

O que merece atenção:

  • collect manda. Ele já combina o que o documento pediu com o que a licença contratou. Coletar menos do que ele diz enfraquece a prova; coletar mais não adianta, porque o motor só guarda o que conhece.
  • state diferente de open significa que não é a vez desta pessoa, que ela já assinou, ou que o documento acabou. Pare o fluxo e mostre o motivo.
  • O texto do consentimento é o do consent.text, com a versão que vem junto. Escrever o seu próprio texto invalida o casamento entre o que a pessoa leu e o que o Termo de Evidências afirma que ela leu.
  • progress.current_step é o que permite retomar de onde parou. Salvá-lo é opcional; não salvar faz a pessoa recomeçar a cada recarga.
  • A ordem é sua, o complete é o fim. Só chame quando tiver coletado o que o collect pediu — depois dele o documento segue para selagem.

Para o navegador poder falar com o motor a partir do seu domínio, cadastre a sua origem em Configurações → origens autorizadas, como no widget. Sem isso o navegador barra as chamadas por CORS.

A evidência passa a depender do seu fluxo. O motor continua validando tudo do lado dele e é a única fonte da prova, mas quem garante que a pessoa passou por cada etapa é a sua tela. É o preço de desenhar a experiência inteira — e a razão de o widget existir.