--- title: 7.1. Visão Geral url: https://docs.vehub.com.br/API/Integra%C3%A7%C3%A3o%20FIDC/7.%20Notifica%C3%A7%C3%B5es%20-%20WebHook/7.1.%20Vis%C3%A3o%20Geral/ --- # 7.1. Visão Geral ## 🧾 Descrição O sistema envia notificações via **WebHook** para informar parceiros e integradores sobre **mudanças de estado** ocorridas em lotes e títulos. A entrega é tentada com **retry exponencial com jitter** em caso de falhas transitórias e cada disparo carrega uma **chave de idempotência** que permite ao consumidor deduplicar entregas repetidas com segurança. --- ## 🔔 Tipos de Eventos | Id | Slug (`tipoEvento`) | Página | Quando é disparado | |----|----------------------------|---------------------|----------------------------------------------------------------------------------------------------------| | 4 | `lote.status_alterado` | [7.2](7.2.%20Status%20do%20Lote%20alterado.md) | Coluna `status` da tabela `lote` é alterada (qualquer transição da máquina de estados) | | 5 | `titulo.cedido` | [7.3](7.3.%20Título%20Cedido.md) | `LoteTitulo.Executado` transiciona de `false` → `true` em uma operação de **Cessão** | | 6 | `titulo.recomprado` | [7.4](7.4.%20Título%20Recomprado.md) | Ocorrência **RecompraComAdiantamento** ou **RecompraComSubstituicao** é executada | | 7 | `titulo.liquidado` | [7.5](7.5.%20Título%20Liquidado.md) | Ocorrência **LiquidacaoCedente** ou **LiquidacaoSacado** é executada | | 8 | `titulo.repassado` | [7.6](7.6.%20Título%20Repassado.md) | Ocorrência **Repasse** é executada | | 9 | `titulo.resolvido` | [7.7](7.7.%20Título%20Resolvido.md) | Ocorrência **Resolucao** é executada | | 10 | `titulo.rejeitado` | [7.8](7.8.%20Título%20Rejeitado.md) | `LoteTitulo.Status` transiciona para **Rejeitado** — independe da ocorrência | > Uma mesma **configuração de webhook** pode estar inscrita em **um ou mais** tipos de evento (aba "Configuração" do webhook no painel). 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 do evento fica em `dados`. ```json { "idWebhook": "019e0892-8d99-778c-9fa5-47bd07cd9ffb", "tipoEvento": "lote.status_alterado", "dataHora": "2026-05-08T14:23:11.512Z", "grupoEconomico": "MeuGrupo", "dados": { /* payload específico do evento */ }, "etiquetas": null } ``` ### 🧾 Detalhamento dos Campos do Envelope | Campo | Tipo | Descrição | |------------------|----------------------------|-------------------------------------------------------------------------------------------------| | `idWebhook` | string (GUID v7) | Identificador único do disparo. **Use para deduplicação.** Também enviado no header `X-Idempotency-Key`. | | `tipoEvento` | string | Slug do evento (ex: `lote.status_alterado`). Sempre ASCII. Também no header `X-Event-Type`. | | `dataHora` | string (ISO 8601, UTC) | Momento em que o evento foi gerado pela plataforma. | | `grupoEconomico` | string | Identificador do grupo econômico que originou o evento. | | `dados` | object | Payload específico do tipo de evento (ver páginas 7.2 a 7.8). | | `etiquetas` | object (`Dictionary`) — null | Pares chave/valor para extensão futura. Em disparos reais é enviado como `null`. Em disparos de **teste** vem preenchido com `isTeste = "true"` e `origem = "painel-vhub"`. | --- ## 🔐 Autenticação VeHub envia a requisição já autenticada conforme o **modo de autenticação** que você configurou no painel de webhooks. Os modos suportados são: | Tipo | Valor | Headers/Body adicionados | Observações | |--------------------------|-------|---------------------------------------------------------------------------|------------------------------------------------------------------------| | `SemAutenticacao` | 1 | nenhum | Use apenas em endpoints públicos protegidos por outra camada (ex: VPN) | | `ApiKey` | 2 | `X-Api-Key: ` | Chave fixa, simples e amplamente compatível | | `Basic` | 3 | `Authorization: Basic base64(clientId:clientSecret)` | Padrão HTTP Basic | | `BearerTokenFixo` | 4 | `Authorization: Bearer ` | Token estático configurado uma vez | | `OAuth2` | 5 | `Authorization: Bearer ` — token obtido via `client_credentials` | VeHub busca o token na URL de autenticação configurada antes de enviar | | `ClientIdClientSecret` | 6 | `X-Client-Id: ` e `X-Client-Secret: ` | Modelo customizado com headers separados | > **A URL de envio precisa ser HTTPS.** VeHub recusa configurações com URL em `http://`. --- ## ✍️ Assinatura HMAC (opcional, recomendada) Quando o webhook é configurado com um **segredo HMAC**, VeHub assina cada disparo com **HMAC-SHA256** sobre o **corpo bruto da requisição** e envia o hash no header: ```http X-Webhook-Signature: sha256= ``` ### Validação no consumidor (exemplo Node.js) ```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=/, ''); return crypto.timingSafeEqual( Buffer.from(esperado, 'hex'), Buffer.from(recebido, 'hex') ); } ``` ### Validação no consumidor (exemplo C#) ```csharp 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 JSON antes da validação podem causar mismatch. --- ## 📨 Headers Padrão Toda requisição enviada pela plataforma inclui: | Header | Exemplo | Descrição | |-------------------------|----------------------------------------------------|--------------------------------------------------------------------| | `Content-Type` | `application/json; charset=utf-8` | Sempre JSON UTF-8 | | `X-Idempotency-Key` | `019e0892-8d99-778c-9fa5-47bd07cd9ffb` | UUID v7 — mesmo valor de `idWebhook` no envelope | | `X-Event-Type` | `lote.status_alterado` | Slug do evento — útil para roteamento | | `X-Webhook-Signature` | `sha256=fa1be9...` | Presente apenas quando há segredo HMAC configurado | | `Authorization` ou afins | depende do modo de autenticação | Ver tabela acima | --- ## 🔁 Retry e Entrega VeHub considera entrega bem-sucedida quando o consumidor responde com **status HTTP 2xx**. Em caso de falha transitória (**5xx**, **408**, **429**, timeout, DNS, conexão recusada), há **retry exponencial com jitter**. Os demais erros **4xx não são retentados**. | Tentativa | Delay 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 é mais retentado automaticamente**. > Para garantir entregas duráveis, **o consumidor deve responder com 2xx assim que receber a mensagem** (idealmente em menos de 5s) e processar de forma assíncrona. --- ## 🪞 Idempotência Como 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 o webhook, persista `idWebhook` em uma tabela com unique constraint. 2. Se o `INSERT` falhar com violação de unicidade, retorne **200 OK** sem reprocessar. 3. Senão, processe normalmente. --- ## 🧪 Disparo de Teste Pelo painel **Cadastros → Webhooks → editar webhook → botão "Testar agora"**, é possível disparar um payload **realístico** do tipo de evento configurado, com IDs fictícios negativos (ex: `idLote = -1001`) e a etiqueta `isTeste = "true"`. Características do disparo de teste: - Single-shot (sem retry). - Persiste em `log_webhook_disparo` com `EhTeste = true` (visível na aba "Logs de disparo" com a tag **Teste**). - Mesmos headers de autenticação e HMAC do disparo real. - Envelope com `etiquetas` preenchidas: `isTeste = "true"` e `origem = "painel-vhub"`. > Recomendado deixar o consumidor preparado para identificar `etiquetas.isTeste === "true"` e ignorar/ramificar o processamento durante validações. --- ## 🛠️ Configuração no Painel A configuração de webhooks está em **Cadastros → Webhooks** (requer permissão `cadastros:webhook:escrita`). Crie uma configuração com: - **Tipos de evento** que deseja receber (um ou mais dos 7 listados acima) - **URL de envio** (HTTPS obrigatório) - **Tipo de autenticação** + credenciais conforme o modo escolhido - **Segredo HMAC** (opcional, mas recomendado — mínimo 16 caracteres) --- ## 📜 Logs de Disparo Cada tentativa de envio fica registrada em **`log_webhook_disparo`** com: - Data, número da tentativa, duração, status HTTP, sucesso/falha - Headers e payload enviados - Headers e body da resposta recebida - Identificador idempotente (`webhookId`) - Flag `ehTeste` indicando se foi disparo manual de teste Os últimos disparos podem ser consultados na aba **Logs de disparo** dentro da edição do webhook, com **drawer de detalhe** mostrando payload e response completos. --- ## 🕒 Observações - Todos os timestamps estão em **UTC** (formato ISO 8601 com `Z` no final). - Os payloads usam **camelCase**. - VeHub não envia campos sensíveis (CPF/CNPJ completos só aparecem onde já são públicos — ex: documento do sacado de uma duplicata mercantil). - A ordem das entregas **não é garantida**: use `dataHora` e o estado atual do recurso na sua base para resolver conflitos.