Siged
Agendar demonstração

Webhooks

O Siged avisa o seu sistema a cada evento relevante do envelope, com uma requisição POST assinada. O corpo não leva dado pessoal: use o identificador para consultar o envelope pela API.

Cabeçalhos

CabeçalhoDescrição
X-Siged-Event Tipo do evento (ex.: envelope.concluded).
X-Siged-Event-Id Id único do evento — use para descartar duplicatas (entrega pelo menos uma vez).
X-Siged-Delivery-Id Id da entrega (o mesmo nas novas tentativas e no reenvio).
X-Siged-Timestamp Momento da assinatura em segundos Unix (UTC).
X-Siged-Signature v1=<HMAC-SHA256 em hexadecimal de "{timestamp}.{corpo cru}" com o segredo de assinatura>.

Como verificar a assinatura

Recalcule o HMAC-SHA256 de "{X-Siged-Timestamp}.{corpo cru}" com o segredo de assinatura (whsec_…), compare com X-Siged-Signature em tempo constante e recuse carimbo com mais de 5 minutos de diferença (replay). Responda 2xx em até 10 s; processe depois (fila). Redirecionamentos não são seguidos.

Node.js
const crypto = require('crypto');

// app.use('/webhooks/siged', express.raw({ type: 'application/json' }));
function sigedWebhookValido(req, segredo) {
  const ts = req.header('X-Siged-Timestamp');
  const recebida = req.header('X-Siged-Signature') || '';
  if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // replay
  const esperada = 'v1=' + crypto.createHmac('sha256', segredo)
    .update(`${ts}.${req.body.toString('utf8')}`) // corpo CRU
    .digest('hex');
  const a = Buffer.from(esperada), b = Buffer.from(recebida);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
C#
using System.Security.Cryptography;
using System.Text;

static bool SigedWebhookValido(string corpoCru, string? timestamp, string? assinatura, string segredo) {
  if (!long.TryParse(timestamp, out var ts) || assinatura is null) return false;
  if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - ts) > 300) return false; // replay
  using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(segredo));
  var esperada = "v1=" + Convert.ToHexString(
    hmac.ComputeHash(Encoding.UTF8.GetBytes($"{ts}.{corpoCru}"))).ToLowerInvariant();
  return CryptographicOperations.FixedTimeEquals(
    Encoding.ASCII.GetBytes(esperada), Encoding.ASCII.GetBytes(assinatura));
}

Entrega e novas tentativas

Sem 2xx (ou timeout de 10 s): novas tentativas em 1, 5, 15, 60 e 360 minutos; depois disso a entrega fica FALHOU e pode ser reenviada pela tela. A ordem de chegada não é garantida: use o createdAt e consulte o envelope pela API se precisar do estado atual.

Eventos

Envelope criado

envelope.created

Um envelope foi criado (pela tela ou pela API).

Exemplo de corpo
{
  "id": "c0ffee00-1234-4abc-9def-0123456789ab",
  "type": "envelope.created",
  "createdAt": "2026-10-01T13:00:00Z",
  "data": {
    "envelopeId": "7d3c1f0e-2b4a-4c55-9e1f-0a9b8c7d6e5f",
    "protocol": "ENV-20261001-0042",
    "externalRef": "PROP-2026-000123",
    "typeCode": "CONTA-PJ",
    "status": "COLLECTING",
    "stageKey": "COLETA",
    "stageName": "Coleta de documentos",
    "completeness": "DOCS_COMPLETE",
    "missingCount": 0
  }
}

Mudança de etapa

envelope.stage_changed

O envelope avançou ou voltou de etapa (ex.: devolvido para a coleta).

Exemplo de corpo
{
  "id": "c0ffee00-1234-4abc-9def-0123456789ab",
  "type": "envelope.stage_changed",
  "createdAt": "2026-10-01T13:00:00Z",
  "data": {
    "envelopeId": "7d3c1f0e-2b4a-4c55-9e1f-0a9b8c7d6e5f",
    "protocol": "ENV-20261001-0042",
    "externalRef": "PROP-2026-000123",
    "typeCode": "CONTA-PJ",
    "status": "IN_REVIEW",
    "stageKey": "CURADORIA",
    "stageName": "Curadoria",
    "completeness": "DOCS_COMPLETE",
    "missingCount": 0
  }
}

Envelope concluído

envelope.concluded

O envelope chegou à etapa final (concluído).

Exemplo de corpo
{
  "id": "c0ffee00-1234-4abc-9def-0123456789ab",
  "type": "envelope.concluded",
  "createdAt": "2026-10-01T13:00:00Z",
  "data": {
    "envelopeId": "7d3c1f0e-2b4a-4c55-9e1f-0a9b8c7d6e5f",
    "protocol": "ENV-20261001-0042",
    "externalRef": "PROP-2026-000123",
    "typeCode": "CONTA-PJ",
    "status": "COMPLETED",
    "stageKey": "CONCLUIDO",
    "stageName": "Concluído",
    "completeness": "COMPLETED",
    "missingCount": 0
  }
}

Envelope reprovado

envelope.rejected

O envelope foi reprovado numa alçada de aprovação (encerrado).

Exemplo de corpo
{
  "id": "c0ffee00-1234-4abc-9def-0123456789ab",
  "type": "envelope.rejected",
  "createdAt": "2026-10-01T13:00:00Z",
  "data": {
    "envelopeId": "7d3c1f0e-2b4a-4c55-9e1f-0a9b8c7d6e5f",
    "protocol": "ENV-20261001-0042",
    "externalRef": "PROP-2026-000123",
    "typeCode": "CONTA-PJ",
    "status": "REJECTED",
    "stageKey": "FRAUDE",
    "stageName": "Alçada de fraude",
    "completeness": "DOCS_COMPLETE",
    "missingCount": 0
  }
}

Envelope cancelado

envelope.cancelled

O envelope foi cancelado com justificativa.

Exemplo de corpo
{
  "id": "c0ffee00-1234-4abc-9def-0123456789ab",
  "type": "envelope.cancelled",
  "createdAt": "2026-10-01T13:00:00Z",
  "data": {
    "envelopeId": "7d3c1f0e-2b4a-4c55-9e1f-0a9b8c7d6e5f",
    "protocol": "ENV-20261001-0042",
    "externalRef": "PROP-2026-000123",
    "typeCode": "CONTA-PJ",
    "status": "CANCELED",
    "stageKey": "COLETA",
    "stageName": "Coleta de documentos",
    "completeness": "DOCS_COMPLETE",
    "missingCount": 0
  }
}

Documento aprovado

document.approved

Um documento foi aprovado na curadoria (humana ou automática).

Exemplo de corpo
{
  "id": "c0ffee00-1234-4abc-9def-0123456789ab",
  "type": "document.approved",
  "createdAt": "2026-10-01T13:00:00Z",
  "data": {
    "envelopeId": "7d3c1f0e-2b4a-4c55-9e1f-0a9b8c7d6e5f",
    "protocol": "ENV-20261001-0042",
    "externalRef": "PROP-2026-000123",
    "typeCode": "CONTA-PJ",
    "status": "IN_REVIEW",
    "stageKey": "CURADORIA",
    "stageName": "Curadoria",
    "completeness": "DOCS_COMPLETE",
    "missingCount": 0,
    "document": {
      "documentId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "requirementCode": "CONTRATO_SOCIAL",
      "documentTypeCode": "DOC_CONTRATO_SOCIAL",
      "participantId": null,
      "status": "APPROVED",
      "version": 1
    }
  }
}

Documento reprovado / reenvio solicitado

document.rejected

Um documento foi reprovado; o envelope pede um novo envio (motivo mascarado no corpo).

Exemplo de corpo
{
  "id": "c0ffee00-1234-4abc-9def-0123456789ab",
  "type": "document.rejected",
  "createdAt": "2026-10-01T13:00:00Z",
  "data": {
    "envelopeId": "7d3c1f0e-2b4a-4c55-9e1f-0a9b8c7d6e5f",
    "protocol": "ENV-20261001-0042",
    "externalRef": "PROP-2026-000123",
    "typeCode": "CONTA-PJ",
    "status": "RETURNED",
    "stageKey": "COLETA",
    "stageName": "Coleta de documentos",
    "completeness": "DOCS_COMPLETE",
    "missingCount": 0,
    "document": {
      "documentId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "requirementCode": "CONTRATO_SOCIAL",
      "documentTypeCode": "DOC_CONTRATO_SOCIAL",
      "participantId": null,
      "status": "REJECTED",
      "version": 1,
      "rejectionReason": "Documento ilegível. Envie uma nova digitalização."
    }
  }
}

Revisão por pessoa solicitada

document.human_review_requested

Uma decisão automática da IA foi reaberta: o documento voltou para a curadoria humana (sem o motivo no corpo).

Exemplo de corpo
{
  "id": "c0ffee00-1234-4abc-9def-0123456789ab",
  "type": "document.human_review_requested",
  "createdAt": "2026-10-01T13:00:00Z",
  "data": {
    "envelopeId": "7d3c1f0e-2b4a-4c55-9e1f-0a9b8c7d6e5f",
    "protocol": "ENV-20261001-0042",
    "externalRef": "PROP-2026-000123",
    "typeCode": "CONTA-PJ",
    "status": "RETURNED",
    "stageKey": "COLETA",
    "stageName": "Coleta de documentos",
    "completeness": "DOCS_COMPLETE",
    "missingCount": 0,
    "document": {
      "documentId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "requirementCode": "CONTRATO_SOCIAL",
      "documentTypeCode": "DOC_CONTRATO_SOCIAL",
      "participantId": null,
      "status": "REJECTED",
      "version": 1,
      "rejectionReason": "Documento ilegível. Envie uma nova digitalização."
    }
  }
}