Guias de Desenvolvimento

Política e Conformidade

Signing Sessions

Sessão de Confiança

Modelos de Solução

Guias dos SDKs

Proposta e Apólice de Seguro com Assinatura Eletrônica — Modelo de Solução

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.


⚡ Caminho de 15 minutos

  1. Obtenha credenciais HML em app.signdocs.com.br/admin/api-clients e troque por um token em POST /oauth2/token.
  2. Crie uma sessão de assinatura (POST /v1/signing-sessions) com o PDF da proposta, perfil CLICK_PLUS_OTP e metadata com o número da proposta.
  3. Envie ao proponente o link url + "?cs=" + clientSecret — por e-mail automático (campo owner) ou pelo canal do corretor.
  4. Receba o webhook SIGNING_SESSION.COMPLETED e dispare a emissão da apólice no seu core.
  5. Baixe o pacote de evidências (GET /v1/evidence/{evidenceId}/download) e arquive junto à apólice — documento assinado + prova criptográfica em mãos.

1. O problema de negócio

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.

2. Arquitetura do fluxo

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.

3. Pré-requisitos

🟠 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.

4. Autenticação OAuth2

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.

5. Criar a sessão de assinatura da proposta

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:

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 BIOMETRIC e inclua signer.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:

Dica — em HML o domínio de assinatura é sign-hml.signdocs.com.br e o código OTP volta em sandbox.otpCode na resposta, para você testar o fluxo de ponta a ponta sem depender de e-mail/SMS reais.

7. Acompanhar a assinatura e baixar a evidência

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.

8. Alçada de subscrição: aprovação por sessão de confiança

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 evidenceId da 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.

9. Renovação de apólice: reaproveite o userExternalId

Na 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.


Webhooks: reagindo à conclusão

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 localhost ou IP privado são bloqueados na borda (403). Em desenvolvimento, use um túnel (ex.: webhook.site).

Escolhendo o perfil de política

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.

Caminho sem código

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:

Erros comuns

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.

Perguntas frequentes

Proposta de seguro assinada eletronicamente tem validade jurídica?

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.

DPS pode ser assinada online?

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.

O proponente precisa de certificado digital para assinar a proposta?

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.

Como automatizo a renovação da apólice?

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.

E se o proponente não assinar no prazo?

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.

Próximos passos