Integre assinatura eletrônica em 1 chamada de API. Escolha o perfil de verificação ideal para seu caso de uso.
Assinatura Expressa (Signing Sessions) encapsula todo o fluxo de assinatura em uma única chamada de API. Em vez de 8+ chamadas sequenciais (criar transação, upload, listar etapas, iniciar, concluir, finalizar, verificar), você cria uma sessão e uma página hospedada cuida de tudo.
A API oferece 8 perfis de política para diferentes níveis de verificação — de um simples aceite por clique até biometria facial contra a base do SERPRO. Cada perfil configura automaticamente as etapas necessárias, sem que você precise orquestrá-las manualmente.
| Perfil | Descrição | Campos obrigatórios | Caso de uso |
|---|---|---|---|
CLICK_ONLY |
Aceite simples por clique | name, cpf ou cnpj |
Termos de uso, contratos simples |
CLICK_PLUS_OTP |
Aceite + código OTP | email (ou phone) |
Contratos com confirmação |
BIOMETRIC |
Verificação facial | cpf |
Verificação de identidade |
BIOMETRIC_PLUS_OTP |
Biometria + OTP | cpf + email |
Alta segurança |
DIGITAL_CERTIFICATE |
Certificado ICP-Brasil A1 | cpf |
Assinatura digital qualificada |
BIOMETRIC_SERPRO |
Biometria contra base SERPRO | cpf + birthDate |
Verificação governamental |
BIOMETRIC_SERPRO_AUTO_FALLBACK |
NT65 consignado | cpf + birthDate |
INSS consignado (NT65) |
CUSTOM |
Etapas customizadas | varia | Integrações avançadas |
O diagrama abaixo ilustra o fluxo completo, válido para todos os perfis:
SEU FRONTEND SEU BACKEND SIGNDOCS API
───────────── ──────────── ────────────
Usuário clica "Assinar"
│
├──> POST /api/sign ──────>
Cria sessão ──> POST /v1/signing-sessions
(perfil, signatário, PDF) Cria transação + etapas
<── { sessionId, clientSecret }
<── { clientSecret }
<── { clientSecret }
│
sd.checkout({ clientSecret }) ──────────────────────> Página hospedada
Adapta UI ao perfil
Etapas completadas
<── onComplete(evidenceId) Auto-finalização
│
├──> Verificar backend ───>
Poll status OU webhook
GET /v1/verify/{evidenceId}
Estes passos são realizados uma única vez e compartilhados por todos os perfis.
Todas as chamadas assumem estas variáveis configuradas:
export SIGNDOCS_CLIENT_ID="your_client_id"
export SIGNDOCS_CLIENT_SECRET="your_client_secret"
export SIGNDOCS_BASE_URL="https://api-hml.signdocs.com.br" # HML
# export SIGNDOCS_BASE_URL="https://api.signdocs.com.br" # Produção
| HML (Sandbox) | Produção | |
|---|---|---|
| Base URL | api-hml.signdocs.com.br |
api.signdocs.com.br |
| Signing Domain | sign-hml.signdocs.com.br |
sign.signdocs.com.br |
| Dados reais? | Não | Sim |
npm install @signdocs-brasil/api
import { SignDocsBrasilClient } from '@signdocs-brasil/api';
const client = new SignDocsBrasilClient({
clientId: process.env.SIGNDOCS_CLIENT_ID!,
clientSecret: process.env.SIGNDOCS_CLIENT_SECRET!,
baseUrl: process.env.SIGNDOCS_BASE_URL,
});
pip install signdocs-brasil
import os
from signdocs_brasil import SignDocsBrasilClient, ClientConfig
client = SignDocsBrasilClient(ClientConfig(
client_id=os.environ['SIGNDOCS_CLIENT_ID'],
client_secret=os.environ['SIGNDOCS_CLIENT_SECRET'],
base_url=os.environ.get('SIGNDOCS_BASE_URL', 'https://api-hml.signdocs.com.br'),
))
go get github.com/signdocsbrasil/signdocsbrasil-go
import signdocs "github.com/signdocsbrasil/signdocsbrasil-go"
client, err := signdocs.NewClient(
signdocs.WithCredentials(os.Getenv("SIGNDOCS_CLIENT_ID"), os.Getenv("SIGNDOCS_CLIENT_SECRET")),
signdocs.WithBaseURL(os.Getenv("SIGNDOCS_BASE_URL")),
)
if err != nil { log.Fatal(err) }
<dependency>
<groupId>com.signdocsbrasil</groupId>
<artifactId>signdocsbrasil-api</artifactId>
<version>1.0.0</version>
</dependency>
import com.signdocsbrasil.SignDocsBrasilClient;
SignDocsBrasilClient client = SignDocsBrasilClient.builder()
.clientId(System.getenv("SIGNDOCS_CLIENT_ID"))
.clientSecret(System.getenv("SIGNDOCS_CLIENT_SECRET"))
.baseUrl(System.getenv("SIGNDOCS_BASE_URL"))
.build();
composer require signdocs-brasil/signdocs-brasil-php
use SignDocsBrasil\Api\SignDocsBrasilClient;
$client = new SignDocsBrasilClient([
'clientId' => getenv('SIGNDOCS_CLIENT_ID'),
'clientSecret' => getenv('SIGNDOCS_CLIENT_SECRET'),
'baseUrl' => getenv('SIGNDOCS_BASE_URL') ?: 'https://api-hml.signdocs.com.br',
]);
dotnet add package SignDocsBrasil.Api
using SignDocsBrasil.Api;
var client = SignDocsBrasilClient.CreateBuilder()
.ClientId(Environment.GetEnvironmentVariable("SIGNDOCS_CLIENT_ID")!)
.ClientSecret(Environment.GetEnvironmentVariable("SIGNDOCS_CLIENT_SECRET")!)
.BaseUrl(Environment.GetEnvironmentVariable("SIGNDOCS_BASE_URL")
?? "https://api-hml.signdocs.com.br")
.Build();
Todos os perfis exigem o PDF codificado em base64. Veja como ler e codificar:
import { readFileSync } from 'fs';
const pdfBase64 = readFileSync('contrato.pdf').toString('base64');
import base64
from pathlib import Path
pdf_base64 = base64.b64encode(Path('contrato.pdf').read_bytes()).decode()
import (
"encoding/base64"
"os"
)
pdfBytes, err := os.ReadFile("contrato.pdf")
if err != nil { log.Fatal(err) }
pdfBase64 := base64.StdEncoding.EncodeToString(pdfBytes)
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
byte[] pdfBytes = Files.readAllBytes(Path.of("contrato.pdf"));
String pdfBase64 = Base64.getEncoder().encodeToString(pdfBytes);
$pdfBase64 = base64_encode(file_get_contents('contrato.pdf'));
var pdfBase64 = Convert.ToBase64String(File.ReadAllBytes("contrato.pdf"));
O widget JavaScript é idêntico para todos os perfis. A página hospedada adapta a interface automaticamente com base no perfil escolhido no backend.
<script src="https://cdn.signdocs.com.br/v1/signdocs-brasil.js"></script>
npm install @signdocs-brasil/js
Este template funciona para todos os perfis. O frontend não precisa saber qual perfil foi escolhido — a página hospedada adapta a UI automaticamente.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Assinar Documento</title>
</head>
<body>
<h1>Assinatura de Contrato</h1>
<button id="btn-assinar">Assinar agora</button>
<script src="https://cdn.signdocs.com.br/v1/signdocs-brasil.js"></script>
<script>
const sd = SignDocsBrasil.init({ locale: 'pt-BR' });
document.getElementById('btn-assinar').addEventListener('click', async () => {
// 1. Seu backend cria a sessão e retorna o clientSecret
const res = await fetch('/api/signing-session', { method: 'POST' });
const { clientSecret } = await res.json();
// 2. Abre o popup de assinatura
sd.checkout({
clientSecret,
onComplete(event) {
alert('Assinatura concluída! Evidence ID: ' + event.evidenceId);
},
onError(event) {
console.error('Erro:', event.message, event.code);
},
onClose() {
console.log('Popup fechado — verifique o status no backend');
},
});
});
</script>
</body>
</html>
Popup bloqueado? Chame
sd.checkout()dentro de um handler declickdo usuário (como acima). Navegadores bloqueiam popups fora de interações diretas.
Se preferir redirecionar o usuário em vez de abrir um popup, use a URL da sessão diretamente:
<a href="{{ session.url }}">Assinar documento</a>
Use o método waitForCompletion do SDK para aguardar a conclusão da sessão:
const result = await client.signingSessions.waitForCompletion(session.sessionId, {
timeoutMs: 300_000,
intervalMs: 2_000,
});
console.log(result.status); // 'COMPLETED'
console.log(result.evidenceId); // 'ev_ghi789'
result = client.signing_sessions.wait_for_completion(
session.session_id,
timeout_ms=300_000,
interval_ms=2_000,
)
print(result.status) # COMPLETED
print(result.evidence_id)
result, err := client.SigningSessions.WaitForCompletion(ctx, session.SessionID,
signdocs.WithTimeout(300*time.Second),
signdocs.WithInterval(2*time.Second),
)
if err != nil { log.Fatal(err) }
fmt.Println("Status:", result.Status)
fmt.Println("Evidence ID:", result.EvidenceID)
SigningSessionStatusResponse result = client.signingSessions()
.waitForCompletion(session.sessionId, 300_000, 2_000);
System.out.println("Status: " + result.status);
System.out.println("Evidence ID: " + result.evidenceId);
$result = $client->signingSessions->waitForCompletion(
$session->sessionId, ['timeoutMs' => 300000, 'intervalMs' => 2000]
);
echo "Status: " . $result->status . "\n";
echo "Evidence ID: " . $result->evidenceId . "\n";
var result = await client.SigningSessions.WaitForCompletionAsync(
session.SessionId, timeoutMs: 300_000, intervalMs: 2_000);
Console.WriteLine($"Status: {result.Status}");
Console.WriteLine($"Evidence ID: {result.EvidenceId}");
Em produção, use webhooks em vez de polling para evitar chamadas desnecessárias.
curl -X POST "$SIGNDOCS_BASE_URL/v1/webhooks" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/signdocs",
"events": ["signing_session.completed", "signing_session.failed"],
"secret": "whsec_your_webhook_secret"
}'
{
"event": "signing_session.completed",
"sessionId": "ss_abc123",
"transactionId": "tx_def456",
"evidenceId": "ev_ghi789",
"profile": "CLICK_ONLY",
"completedAt": "2026-03-11T14:30:00Z",
"signer": {
"name": "Maria da Silva",
"cpf": "123.456.789-00"
}
}
Verificação de assinatura: Valide o header
X-SignDocs-Signatureusando HMAC-SHA256 com osecretconfigurado no webhook. Rejeite requisições com assinatura inválida para evitar ataques de replay.
Qualquer pessoa com o evidenceId pode verificar a autenticidade da assinatura:
curl "$SIGNDOCS_BASE_URL/v1/verify/{evidenceId}"
Retorna os dados da evidência (signatário, timestamp, hash do documento) sem exigir autenticação.
Para baixar o pacote de evidência completo (.p7m com assinatura ICP-Brasil), use:
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
"$SIGNDOCS_BASE_URL/v1/transactions/{transactionId}/evidence" \
-o evidencia.p7m
O arquivo .p7m contém o JSON de evidência assinado criptograficamente, verificável pelo Validador ITI.
Customize a página hospedada para combinar com a identidade visual da sua marca. Passe o objeto appearance ao criar a sessão:
const session = await client.signingSessions.create({
profile: 'CLICK_ONLY',
// ... outros campos ...
appearance: {
brandColor: '#2563EB',
logoUrl: 'https://yourapp.com/logo.png',
companyName: 'Acme Tecnologia Ltda',
title: 'Assine seu contrato',
submitLabel: 'Concordo e assino',
headerStyle: 'full',
},
});
| Campo | Tipo | Descrição |
|---|---|---|
brandColor |
string |
Cor primária em hex (botões, links) |
logoUrl |
string |
URL do logotipo (recomendado: 200x60px) |
companyName |
string |
Nome exibido no cabeçalho |
title |
string |
Título da página de assinatura |
submitLabel |
string |
Texto do botão de confirmação |
headerStyle |
'full' | 'minimal' |
Estilo do cabeçalho |
Escolha o guia detalhado do perfil que melhor atende ao seu caso de uso:
Ou vá direto para as Receitas Copy-Paste com código pronto para copiar e executar.