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 headerX-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_credentialsno 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
3xxnã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:
- Ao receber, persista
idWebhookem uma tabela com unique constraint. - Se o
INSERTfalhar com violação de unicidade, responda 200 OK sem reprocessar. - 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:
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.