Automatize o aceite da proposta de seguro e da DPS (Declaração Pessoal de Saúde) com assinatura eletrônica de validade jurídica fundada na MP 2.200-2/2001, tratamento de dados alinhado à LGPD e, quando o risco exigir, verificação biométrica facial do proponente. O resultado de negócio: a proposta volta assinada em minutos — não em dias —, a subscrição aprova exceções com trilha de auditoria completa e a apólice digital é emitida no mesmo fluxo, sem papel e sem redigitação.
Público-alvo: corretoras de seguros, insurtechs e seguradoras que querem ligar o multicálculo, o CRM do corretor ou o core de emissão a um fluxo de assinatura eletrônica via API.
POST /oauth2/token.POST /v1/signing-sessions) com o PDF da proposta, perfil CLICK_PLUS_OTP e metadata com o número da proposta.url + "?cs=" + clientSecret — por e-mail automático (campo owner) ou pelo canal do corretor.SIGNING_SESSION.COMPLETED e dispare a emissão da apólice no seu core.GET /v1/evidence/{evidenceId}/download) e arquive junto à apólice — documento assinado + prova criptográfica em mãos.O ciclo clássico de contratação de seguro ainda trava no aceite do proponente. O corretor fecha a cotação no multicálculo, gera a proposta em PDF, envia por e-mail ou WhatsApp e espera: impressão, assinatura à caneta, foto ou scan de volta. Em ramos com DPS — vida, saúde, prestamista — o atrito dobra, porque a declaração pessoal de saúde precisa de aceite individual e datado do proponente, e qualquer rasura invalida o documento. Cada dia de espera é risco de o cliente esfriar, fechar com o concorrente ou o início de vigência escorregar.
Com proposta de seguro com assinatura eletrônica, o fluxo inverte: o sistema do corretor (multicálculo, CRM) chama a API no momento em que a cotação é aceita, o proponente assina no celular em minutos e a seguradora recebe o evento de conclusão para emitir a apólice digital automaticamente. A DPS com assinatura online sai no mesmo fluxo, com verificação por código OTP — ou biometria facial quando o capital segurado justifica prova de identidade mais forte.
O segundo gargalo é interno: quando o risco foge da régua de aceitação automática (idade, capital, agravamento na DPS), a proposta precisa de aprovação da alçada de subscrição. Hoje isso vira e-mail, planilha ou aprovação verbal — sem evidência auditável. A sessão de confiança de aprovação resolve exatamente isso: o subscritor autoriza a exceção com autenticação verificada, e a decisão fica registrada em um pacote de evidências criptográfico, sem precisar materializar mais um PDF.
Multicálculo / CRM do corretor
│ cotação aceita
▼
POST /v1/signing-sessions ───────────► Proponente assina
(proposta + DPS, CLICK_PLUS_OTP, (link url + ?cs=...,
metadata: nº da proposta) e-mail / canal do corretor)
│
▼
Webhook SIGNING_SESSION.COMPLETED
│
├── risco dentro da régua ──► Emitir apólice + arquivar evidência
│
└── risco fora da régua
│
▼
POST /v1/trust-sessions ─────► Subscritor aprova exceção
(aprovação de alçada)
│
▼
Emitir apólice + arquivar as duas evidências
| Primitiva | Quando usar neste cenário |
|---|---|
Sessão de assinatura (/v1/signing-sessions) |
Aceite do proponente sobre a proposta/DPS: 1 signatário, 1 documento, link pronto em uma chamada. É o coração deste fluxo. |
Sessão de confiança (/v1/trust-sessions) |
Aprovação da alçada de subscrição: autoriza uma ação (aceitar risco fora da régua) sem precisar de PDF. Gera evidência auditável da decisão. |
Envelope (/v1/envelopes) |
Só se a proposta exigir múltiplos signatários no mesmo PDF (ex.: estipulante + proponente em seguro coletivo). Para o aceite individual, a sessão de assinatura é mais simples. |
transactions:read transactions:write steps:write evidence:read webhooks:write.@signdocs-brasil/api (npm) ou signdocs-brasil (PyPI). Para outras linguagens, veja os guias de SDK.🟠 HML vs Produção — desenvolva sempre em HML (
https://api-hml.signdocs.com.br, com hífen). Credenciais HML não consomem cota do plano e os dados expiram em 7 dias. Produção:https://api.signdocs.com.br.
A API usa OAuth2 client_credentials: troque client_id + client_secret por um access_token de curta duração.
export SIGNDOCS_BASE_URL="https://api-hml.signdocs.com.br"
ACCESS_TOKEN=$(curl -s -X POST "$SIGNDOCS_BASE_URL/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=$SIGNDOCS_CLIENT_ID&client_secret=$SIGNDOCS_CLIENT_SECRET" \
| jq -r '.access_token')
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, // https://api-hml.signdocs.com.br
});
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'),
))
O SDK renova o token automaticamente. Em HTTP puro, cacheie o access_token até perto do expires_in.
Uma única chamada cria a transação, anexa o PDF da proposta (com a DPS, se houver) e devolve o link de assinatura. Três campos merecem atenção neste cenário:
metadata — grave o número da proposta, o ramo e o corretor. Esses dados voltam nos webhooks e amarram a assinatura ao seu core de emissão.signer.userExternalId — use o ID do segurado no seu sistema (ex.: CPF ou ID do CRM). Na renovação da apólice, reaproveite o mesmo valor: o histórico do signatário fica encadeado entre vigências.owner — com owner.email diferente de signer.email, a API envia o convite por e-mail ao proponente automaticamente. Sem owner, o link não é enviado: seu sistema (ou o corretor) entrega por conta própria.PDF_B64=$(base64 -w0 proposta-seguro.pdf)
curl -s -X POST "$SIGNDOCS_BASE_URL/v1/signing-sessions" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: proposta-2026-004512" \
-d '{
"purpose": "DOCUMENT_SIGNATURE",
"policy": {"profile": "CLICK_PLUS_OTP"},
"signer": {
"name": "Maria Souza",
"email": "maria@example.com",
"userExternalId": "segurado-78421"
},
"owner": {"name": "Corretora Exemplo", "email": "operacao@corretoraexemplo.com.br"},
"document": {"content": "'$PDF_B64'", "filename": "proposta-seguro.pdf"},
"metadata": {
"proposta_numero": "2026-004512",
"ramo": "vida-individual",
"corretor_susep": "10.123456-7",
"capital_segurado_cents": "50000000"
},
"returnUrl": "https://portal.corretoraexemplo.com.br/proposta/concluida",
"locale": "pt-BR",
"expiresInMinutes": 4320
}'
import { readFileSync } from 'fs';
const pdfBase64 = readFileSync('proposta-seguro.pdf').toString('base64');
const session = await client.signingSessions.create({
purpose: 'DOCUMENT_SIGNATURE',
policy: { profile: 'CLICK_PLUS_OTP' },
signer: {
name: 'Maria Souza',
email: 'maria@example.com',
userExternalId: 'segurado-78421', // reaproveite na renovação
},
owner: { name: 'Corretora Exemplo', email: 'operacao@corretoraexemplo.com.br' },
document: { content: pdfBase64, filename: 'proposta-seguro.pdf' },
metadata: {
proposta_numero: '2026-004512',
ramo: 'vida-individual',
corretor_susep: '10.123456-7',
capital_segurado_cents: '50000000',
},
returnUrl: 'https://portal.corretoraexemplo.com.br/proposta/concluida',
locale: 'pt-BR',
expiresInMinutes: 4320, // 3 dias para o proponente assinar
});
console.log('Session ID:', session.sessionId);
console.log('Link de assinatura:', `${session.url}?cs=${session.clientSecret}`);
import base64
with open('proposta-seguro.pdf', 'rb') as f:
pdf_base64 = base64.b64encode(f.read()).decode()
session = client.signing_sessions.create(CreateSigningSessionRequest(
purpose='DOCUMENT_SIGNATURE',
policy=Policy(profile='CLICK_PLUS_OTP'),
signer=Signer(
name='Maria Souza',
email='maria@example.com',
user_external_id='segurado-78421', # reaproveite na renovação
),
owner=Owner(name='Corretora Exemplo', email='operacao@corretoraexemplo.com.br'),
document=InlineDocument(content=pdf_base64, filename='proposta-seguro.pdf'),
metadata={
'proposta_numero': '2026-004512',
'ramo': 'vida-individual',
'corretor_susep': '10.123456-7',
'capital_segurado_cents': '50000000',
},
return_url='https://portal.corretoraexemplo.com.br/proposta/concluida',
locale='pt-BR',
expires_in_minutes=4320,
))
print('Session ID:', session.session_id)
print('Link de assinatura:', f'{session.url}?cs={session.client_secret}')
⚠️ DPS de vida com capital alto? Troque o perfil para
BIOMETRICe incluasigner.cpf(11 dígitos, sem formatação) — o proponente passa por verificação facial com prova de vida antes de assinar. Veja a tabela de perfis mais abaixo.
O link de assinatura é a combinação de dois campos da resposta: url + ?cs= + clientSecret. O clientSecret é de uso único — não fica armazenado e não pode ser recuperado depois; se perder, crie nova sessão.
Você tem duas rotas de entrega:
owner.email diferente de signer.email (como no exemplo acima), a API já enviou o convite ao proponente. Nada a fazer.owner, seu sistema monta o link e o corretor repassa pelo canal que já usa com o cliente: e-mail próprio, SMS ou WhatsApp (via API do WhatsApp Business do próprio corretor ou automação Make/n8n — WhatsApp é só o canal de entrega do link, não uma etapa de assinatura).✅ Dica — em HML o domínio de assinatura é
sign-hml.signdocs.com.bre o código OTP volta emsandbox.otpCodena resposta, para você testar o fluxo de ponta a ponta sem depender de e-mail/SMS reais.
Prefira webhooks (seção adiante) para reagir em tempo real. Para consultas pontuais — tela de status no portal do corretor, reconciliação — use o GET da sessão. Quando o status for COMPLETED, a resposta traz o evidenceId.
curl -s "$SIGNDOCS_BASE_URL/v1/signing-sessions/$SESSION_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '{status, evidenceId}'
# Documento assinado + pacote de evidências (.p7m)
curl -s "$SIGNDOCS_BASE_URL/v1/evidence/$EVIDENCE_ID/download" \
-H "Authorization: Bearer $ACCESS_TOKEN" -o evidencia-proposta-2026-004512.p7m
const result = await client.signingSessions.waitForCompletion(session.sessionId);
console.log('Status:', result.status); // COMPLETED
console.log('Evidence ID:', result.evidenceId);
// Baixe e arquive o .p7m junto à apólice no seu GED
const evidence = await fetch(
`${process.env.SIGNDOCS_BASE_URL}/v1/evidence/${result.evidenceId}/download`,
{ headers: { Authorization: `Bearer ${accessToken}` } },
);
result = client.signing_sessions.wait_for_completion(session.session_id)
print('Status:', result.status) # COMPLETED
print('Evidence ID:', result.evidence_id)
# Baixe e arquive o .p7m junto à apólice no seu GED
evidence = requests.get(
f"{base_url}/v1/evidence/{result.evidence_id}/download",
headers={'Authorization': f'Bearer {access_token}'},
)
O pacote de evidências é um JSON assinado em PKCS#7/CMS (.p7m) com certificado ICP-Brasil A1, contendo hash SHA-256 do documento, timestamps ISO do servidor e a trilha de auditoria completa (IP, aceites, verificação OTP/biométrica). Qualquer terceiro pode conferir a integridade via GET /v1/verify/{evidenceId} ou POST /v1/verify/document. Detalhes em evidências de sessão.
Quando a proposta foge da régua automática — capital acima do limite do corretor, agravamento declarado na DPS, idade fora da faixa —, a decisão do subscritor precisa de registro auditável. A sessão de confiança autoriza essa ação sem PDF: o subscritor recebe um link, autentica-se (aceite + OTP no e-mail corporativo) e a aprovação vira um pacote de evidências, igual ao da assinatura.
curl -s -X POST "$SIGNDOCS_BASE_URL/v1/trust-sessions" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: subscricao-2026-004512" \
-d '{
"action": {
"type": "approve_underwriting_exception",
"description": "Aprovar subscrição da proposta 2026-004512 — capital R$ 500.000,00, DPS com agravamento declarado"
},
"policy": {"profile": "CLICK_PLUS_OTP"},
"signer": {
"name": "Carlos Lima",
"email": "carlos.lima@seguradoraexemplo.com.br",
"otpChannel": "email"
},
"metadata": {
"proposta_numero": "2026-004512",
"alcada": "subscricao-nivel-2",
"capital_segurado_cents": "50000000"
},
"returnUrl": "https://core.seguradoraexemplo.com.br/subscricao/concluida",
"locale": "pt-BR",
"expiresInMinutes": 240
}'
const resp = await fetch(`${process.env.SIGNDOCS_BASE_URL}/v1/trust-sessions`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'X-Idempotency-Key': `subscricao-${proposta.numero}`,
},
body: JSON.stringify({
action: {
type: 'approve_underwriting_exception',
description: `Aprovar subscrição da proposta ${proposta.numero} — capital R$ ${(proposta.capitalCents / 100).toFixed(2)}, DPS com agravamento declarado`,
},
policy: { profile: 'CLICK_PLUS_OTP' },
signer: {
name: 'Carlos Lima',
email: 'carlos.lima@seguradoraexemplo.com.br',
otpChannel: 'email',
},
metadata: {
proposta_numero: proposta.numero,
alcada: 'subscricao-nivel-2',
capital_segurado_cents: String(proposta.capitalCents),
},
returnUrl: 'https://core.seguradoraexemplo.com.br/subscricao/concluida',
locale: 'pt-BR',
expiresInMinutes: 240,
}),
});
const { sessionId, url, clientSecret } = await resp.json();
// Envie ao subscritor: `${url}?cs=${clientSecret}`
resp = requests.post(
f"{base_url}/v1/trust-sessions",
headers={
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json',
'X-Idempotency-Key': f"subscricao-{proposta['numero']}",
},
json={
'action': {
'type': 'approve_underwriting_exception',
'description': f"Aprovar subscrição da proposta {proposta['numero']} — "
f"capital R$ {proposta['capital_cents'] / 100:.2f}, DPS com agravamento declarado",
},
'policy': {'profile': 'CLICK_PLUS_OTP'},
'signer': {
'name': 'Carlos Lima',
'email': 'carlos.lima@seguradoraexemplo.com.br',
'otpChannel': 'email',
},
'metadata': {
'proposta_numero': proposta['numero'],
'alcada': 'subscricao-nivel-2',
'capital_segurado_cents': str(proposta['capital_cents']),
},
'returnUrl': 'https://core.seguradoraexemplo.com.br/subscricao/concluida',
'locale': 'pt-BR',
'expiresInMinutes': 240,
},
timeout=10,
)
resp.raise_for_status()
session = resp.json()
# Envie ao subscritor: f"{session['url']}?cs={session['clientSecret']}"
⚠️ Aprovação é irreversível no seu core — antes de emitir a apólice a partir de uma aprovação, confirme na sua base que o
evidenceIdda sessão de confiança ainda não foi consumido, e marque-o como consumido na mesma transação que emite. Isso evita dupla emissão em retries de webhook. Padrão completo em sessão de confiança: aprovação.
userExternalIdNa renovação, crie a nova sessão de assinatura com o mesmo signer.userExternalId usado na vigência anterior (ex.: segurado-78421). Isso encadeia o histórico do segurado entre vigências no lado da SignDocs — e do seu lado, basta consultar as sessões pelo mesmo ID para montar a linha do tempo do cliente: proposta original, endossos, renovações. Grave a vigência em metadata (ex.: "vigencia": "2027", "apolice_anterior": "AP-2026-9981") para reconciliar sem ambiguidade.
Registre um endpoint HTTPS uma única vez; a API notifica cada evento relevante. Neste cenário:
| Evento | Reação sugerida |
|---|---|
SIGNING_SESSION.COMPLETED |
Proposta assinada — dispare a emissão da apólice (ou a alçada de subscrição, se fora da régua). |
SIGNING_SESSION.EXPIRED |
Proponente não assinou no prazo — notifique o corretor para reengajar o cliente e recriar a sessão. |
TRANSACTION.FAILED |
Verificação falhou (ex.: biometria sem match) — acione o corretor para atendimento assistido. |
TRANSACTION.COMPLETED |
Aprovação da sessão de confiança concluída — libere a emissão da exceção aprovada. |
curl -s -X POST "$SIGNDOCS_BASE_URL/v1/webhooks" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://core.seguradoraexemplo.com.br/webhooks/signdocs",
"events": ["SIGNING_SESSION.COMPLETED", "SIGNING_SESSION.EXPIRED", "TRANSACTION.FAILED", "TRANSACTION.COMPLETED"]
}'
A resposta traz o secret HMAC uma única vez — armazene em cofre. Cada entrega vem assinada (HMAC-SHA256 sobre timestamp + corpo); valide a assinatura antes de processar e trate entregas como at-least-once (idempotência pelo evidenceId). O metadata.proposta_numero volta no payload, então o handler localiza a proposta no seu core sem lookup extra. Especificação completa de validação em webhooks.
🟠 Webhook em HML — a URL registrada precisa ser pública com HTTPS; endpoints
localhostou IP privado são bloqueados na borda (403). Em desenvolvimento, use um túnel (ex.: webhook.site).
| Perfil | Quando usar em seguros | Atrito |
|---|---|---|
CLICK_ONLY |
Endossos simples de baixo risco e aceites internos de baixo valor. | Mínimo |
CLICK_PLUS_OTP |
Default recomendado — proposta e DPS da maioria dos ramos. O proponente está remoto, chegou pelo corretor, e o OTP no e-mail/SMS prova a posse do canal declarado na proposta. | Baixo |
BIOMETRIC |
DPS de seguro de vida com capital alto ou histórico de fraude no ramo: verificação facial com prova de vida amarra a assinatura à identidade do proponente (requer signer.cpf). |
Médio |
DIGITAL_CERTIFICATE |
Signatários corporativos que já possuem certificado ICP-Brasil (ex.: estipulante PJ em apólice coletiva). | Alto |
Recomendação: comece com CLICK_PLUS_OTP e promova para BIOMETRIC por regra de negócio (capital segurado acima do seu limiar, ramo vida/prestamista). O perfil é um campo por sessão — a promoção é um if no seu código, não um novo fluxo. Fundamentos jurídicos de cada nível em perfis de assinatura e níveis legais.
Se a corretora opera com multicálculo ou CRM que já se conecta a plataformas de automação, dá para montar este fluxo sem backend próprio:
signingUrl da saída alimenta o módulo de e-mail/WhatsApp do corretor. O trigger instantâneo reage a SIGNING_SESSION.COMPLETED para atualizar o CRM. Guia: integração Make.422 — perfil OTP sem canal de contato
{
"type": "https://api.signdocs.com.br/problems/validation",
"title": "Unprocessable Entity",
"status": 422,
"detail": "Missing required field: signer.email"
}
CLICK_PLUS_OTP exige signer.email (ou signer.phone + otpChannel: "sms"). Puxe o contato validado do cadastro da proposta.
422 — perfil biométrico sem CPF
{
"type": "https://api.signdocs.com.br/problems/validation",
"title": "Unprocessable Entity",
"status": 422,
"detail": "Missing required field: signer.cpf"
}
BIOMETRIC exige signer.cpf com 11 dígitos, sem pontuação. O CPF da proposta normalmente vem formatado — normalize antes de enviar.
401 — clientSecret expirado
{
"type": "https://api.signdocs.com.br/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "Token expired"
}
O proponente clicou no link depois do prazo. Dimensione expiresInMinutes para a realidade do canal (proposta via corretor: 4320 = 3 dias funciona bem) e trate SIGNING_SESSION.EXPIRED recriando a sessão.
409 — sessão já concluída
{
"type": "https://api.signdocs.com.br/problems/conflict",
"title": "Conflict",
"status": 409,
"detail": "Session is COMPLETED"
}
Retry do seu lado tentou avançar/cancelar uma sessão finalizada. Consulte o status antes de agir e use X-Idempotency-Key estável (ex.: número da proposta) nas criações.
400 — documento grande demais
{
"type": "https://api.signdocs.com.br/problems/bad-request",
"title": "Bad Request",
"status": 400,
"detail": "Document too large"
}
Propostas com condições gerais anexadas estouram o limite de base64 inline. Anexe às condições gerais um link no corpo da proposta, ou comprima o PDF antes do envio.
Sim. A MP 2.200-2/2001 (art. 10, §2º) reconhece a validade de assinaturas eletrônicas quando as partes admitem o meio como válido — e o aceite registrado na plataforma documenta exatamente isso. O pacote de evidências (.p7m assinado com certificado ICP-Brasil) prova a integridade do documento e a autenticação do proponente. Os níveis por perfil estão em perfis de assinatura e níveis legais.
Pode — a DPS é um documento declaratório do proponente e segue o mesmo regime da proposta. A vantagem do fluxo eletrônico é a trilha: data, hora, IP e verificação OTP (ou facial) ficam amarrados à declaração, o que fortalece a posição da seguradora em eventual discussão de má-fé na declaração de saúde.
Não. Nos perfis eletrônicos (CLICK_PLUS_OTP, BIOMETRIC) o proponente só precisa do link — nenhum cadastro, aplicativo ou certificado. DIGITAL_CERTIFICATE fica reservado a signatários que já operam com ICP-Brasil, como o estipulante PJ.
Pode. O link (url + ?cs= + clientSecret) é um URL comum: o corretor entrega pelo canal que já usa com o cliente — WhatsApp, SMS, e-mail. A entrega por WhatsApp é feita pela conta/API do próprio corretor ou por automação Make/n8n; o WhatsApp é apenas o canal de entrega, a assinatura acontece na página hospedada SignDocs.
Agende no seu core a geração da proposta de renovação e crie a sessão com o mesmo signer.userExternalId da vigência anterior, gravando a nova vigência em metadata. O restante do fluxo — link, webhook, emissão — é idêntico ao da contratação original.
A sessão expira sozinha (você controla com expiresInMinutes) e o webhook SIGNING_SESSION.EXPIRED avisa seu sistema. O padrão que funciona: notificar o corretor com o contexto da proposta (o metadata volta no evento) para um contato ativo, e recriar a sessão com um clique no portal do corretor.