Ir para o conteúdo

4.1. Visão Geral

Descrição

Quando uma consulta ao SCR solicitada pela API termina, VeTrust envia uma notificação via WebHook para a URL configurada para o seu grupo econômico. Assim você não precisa ficar consultando o status em laço.

A notificação é um gatilho: ela traz só o idConsulta e o status. O resultado é lido em 3.3. Consultar Resultado.

O webhook acelera, mas não substitui a leitura

Uma entrega pode falhar de vez — o seu servidor fora do ar durante todas as tentativas, por exemplo. Mantenha o polling descrito em 1.3. Operações Assíncronas como plano B para toda consulta que não recebeu evento.


Tipos de Eventos

Slug (tipoEvento) Página Quando é disparado
bureau.scr.concluida 4.2 A consulta terminou Concluída — pode ter meses em falha
bureau.scr.falhou 4.2 A consulta terminou em Falha

Só as consultas solicitadas pela API geram eventos. As feitas pela tela da plataforma não geram.

Uma mesma configuração de webhook pode estar inscrita em um ou mais tipos de evento — os de consulta SCR e os de outros produtos que você contrate. Todos os eventos inscritos são entregues na mesma URL: use o campo tipoEvento (ou o header X-Event-Type) para rotear o processamento.


Envelope Padrão

Todos os eventos compartilham o mesmo envelope. O conteúdo específico fica em dados.

{
  "idWebhook": "019e0892-8d99-778c-9fa5-47bd07cd9ffb",
  "tipoEvento": "bureau.scr.concluida",
  "dataHora": "2026-10-23T13:00:41.512Z",
  "grupoEconomico": "MeuGrupo",
  "dados": { },
  "etiquetas": null
}

Detalhamento dos Campos do Envelope

Campo Tipo Descrição
idWebhook string (GUID) Identificador único do disparo. Use para deduplicação. Também enviado no header X-Idempotency-Key
tipoEvento string Slug do evento. Sempre ASCII. Também no header X-Event-Type
dataHora string (ISO 8601, UTC) Momento em que o evento foi gerado
grupoEconomico string Grupo econômico da consulta
dados object Payload específico do tipo de evento
etiquetas object / null Pares chave/valor para extensão. Em disparos reais vem null; em disparos de teste vem { "isTeste": "true" }

Autenticação da entrega

A requisição de entrega chega ao seu servidor autenticada conforme o modo combinado na configuração:

Tipo Valor Headers adicionados Observações
SemAutenticacao 1 nenhum Use apenas em endpoints protegidos por outra camada
ApiKey 2 X-Api-Key: <segredo> Chave fixa, simples e amplamente compatível
Basic 3 Authorization: Basic base64(clientId:clientSecret) Padrão HTTP Basic
BearerTokenFixo 4 Authorization: Bearer <segredo> Token estático configurado uma vez
ClientIdClientSecret 6 X-Client-Id e X-Client-Secret Headers separados

OAuth2 não é suportado como modo de entrega: não é possível configurar a obtenção de token por client_credentials no seu provedor. Use um dos modos acima — de preferência combinado com a assinatura HMAC.


Assinatura HMAC (opcional, recomendada)

Quando o webhook é configurado com um segredo HMAC (no mínimo 16 caracteres), cada disparo é assinado com HMAC-SHA256 sobre o corpo bruto da requisição, e o hash chega no header:

X-Webhook-Signature: sha256=<hex_lowercase>

Validação no consumidor (Node.js)

const crypto = require('crypto');

function validarAssinatura(corpoBruto, headerAssinatura, segredo) {
  const esperado = crypto
    .createHmac('sha256', segredo)
    .update(corpoBruto, 'utf8')
    .digest('hex');

  const recebido = (headerAssinatura || '').replace(/^sha256=/, '');
  if (recebido.length !== esperado.length) {
    return false;
  }
  return crypto.timingSafeEqual(
    Buffer.from(esperado, 'hex'),
    Buffer.from(recebido, 'hex')
  );
}

Validação no consumidor (C#)

using System.Security.Cryptography;
using System.Text;

bool ValidarAssinatura(string corpoBruto, string headerAssinatura, string segredo)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(segredo));
    var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(corpoBruto));
    var esperado = Convert.ToHexString(hash).ToLowerInvariant();
    var recebido = (headerAssinatura ?? string.Empty).Replace("sha256=", string.Empty);
    return CryptographicOperations.FixedTimeEquals(
        Encoding.UTF8.GetBytes(esperado),
        Encoding.UTF8.GetBytes(recebido));
}

Importante: valide a assinatura sobre o corpo bruto recebido, antes de qualquer parsing JSON. Frameworks que reformatam o JSON antes da validação causam divergência.


Headers Padrão

Header Exemplo Descrição
Content-Type application/json; charset=utf-8 Sempre JSON UTF-8
X-Idempotency-Key 019e0892-8d99-778c-9fa5-47bd07cd9ffb Mesmo valor de idWebhook no envelope
X-Event-Type bureau.scr.concluida Slug do evento, útil para roteamento
X-Webhook-Signature sha256=fa1be9... Presente apenas quando há segredo HMAC configurado
Authorization ou afins depende do modo Ver a tabela de autenticação

Requisitos do seu endpoint

  • HTTPS com certificado válido. URLs http:// são recusadas na configuração.
  • Endereço público. A entrega só é feita para IPs públicos: endereços de rede interna, loopback e similares são bloqueados, inclusive quando o nome resolve para eles depois.
  • Sem redirecionamento. Um 3xx não é seguido: aponte a configuração para a URL final.
  • Se o seu firewall exige lista de origem, peça ao nosso time os IPs de saída do ambiente.

Retry e Entrega

A entrega é considerada bem-sucedida quando o seu servidor responde com status HTTP 2xx.

Em falha transitória — 5xx, 408, 429, tempo esgotado, DNS ou conexão recusada — há retry exponencial com jitter. Os demais erros 4xx não são retentados.

Tentativa Intervalo aproximado
1 imediato
2 ~1s + jitter
3 ~2s + jitter
4 ~4s + jitter
5 ~8s + jitter

Após 5 tentativas sem sucesso, o disparo é registrado como falho e não é reenviado — nem automaticamente, nem a pedido. A consulta continua disponível para leitura: recupere o resultado pelo polling.

Cada tentativa espera a resposta por no máximo 10 segundos. O que decide o resultado é o status HTTP; o corpo da sua resposta é guardado só como diagnóstico no log de disparo, até 4 KB.

Responda 2xx assim que receber, idealmente em poucos segundos, e processe de forma assíncrona. Ler a consulta dentro do handler do webhook alonga a resposta e leva a timeout e retry desnecessários.


Deduplicação

Como os retries podem entregar a mesma notificação mais de uma vez, o consumidor deve deduplicar usando o idWebhook (ou o header X-Idempotency-Key).

Padrão recomendado:

  1. Ao receber, persista idWebhook em uma tabela com unique constraint.
  2. Se o INSERT falhar com violação de unicidade, responda 200 OK sem reprocessar.
  3. Senão, processe normalmente.

Cada consulta gera no máximo um evento terminal — bureau.scr.concluida ou bureau.scr.falhou. Por isso, idConsulta + tipoEvento também serve como chave de deduplicação no seu domínio.


A ordem das entregas não é garantida

Eventos de consultas diferentes podem chegar fora da ordem em que as consultas terminaram, e um evento pode chegar antes de você processar a resposta 202 da solicitação.

Padrão recomendado: ao receber um evento, leia a consulta (GET /bureaus/scr/consultas/{idConsulta}) e use o estado devolvido. Use o evento como gatilho, não como fonte da verdade.


Configuração

A configuração do webhook — URL, eventos inscritos, modo de autenticação e segredo HMAC — é feita pelo nosso time, a seu pedido. Para cadastrar ou alterar, envie ao seu contato comercial a URL, os eventos desejados e o modo de autenticação; os segredos são combinados por canal seguro.

A configuração vale para o grupo econômico: se o seu grupo usa mais de uma credencial, a mesma URL recebe os eventos das consultas de todas elas. Como o evento só leva o idConsulta e o status, o GET de uma consulta de outra credencial responde 404.

Endpoints de consulta

A API Pública oferece rotas somente de leitura para você conferir a configuração e diagnosticar entregas:

Método URL Descrição
GET /webhooks/eventos Catálogo de eventos, com o slug de cada um e o campo publicado
GET /webhooks Configurações do seu grupo econômico, sem os segredos
GET /webhooks/{idWebhook} Uma configuração: URL, eventos inscritos, modo de autenticação e se há segredo HMAC (temSegredoHmac)
GET /webhooks/{idWebhook}/disparos Tentativas de entrega, paginadas, com data, tentativa, duração, status HTTP, payload enviado e resposta recebida

Aqui, {idWebhook} é o identificador numérico da configuração devolvido em GET /webhooks — não o idWebhook (GUID) do envelope de um disparo.

Logs de disparo

curl -X GET "https://api.vehub.com.br/public/v1/webhooks/12/disparos?pagina=1&quantidade=50" \
  -H "Authorization: Bearer {seu_token}" \
  -H "GrupoEconomico: {seu_grupo_economico}"

Use esta rota quando um evento não chegou: cada tentativa é uma linha, e as cinco tentativas de um mesmo evento aparecem com o mesmo identificador de disparo. Os headers de autenticação e a assinatura vêm mascarados.

Disparo de teste

Durante a homologação, peça ao nosso time um disparo de teste: ele envia um payload realístico do evento escolhido, com identificadores fictícios e a etiqueta isTeste = "true" no envelope, numa tentativa só, sem retry.

Prepare o seu consumidor para identificar etiquetas.isTeste === "true" e não tratar o evento como real: o idConsulta do teste é fictício — não o leia nem o processe.


Observações

  • Todos os timestamps estão em UTC (ISO 8601 com Z).
  • Os payloads usam camelCase.
  • Os eventos de consulta SCR não transportam o documento, o período nem o resultado. Para isso, leia a consulta.