Pular para o conteúdo

Webhooks

O Cotejar avisa o seu sistema quando um catálogo é aplicado, uma análise termina ou as divergências mudam. Cada entrega é assinada com HMAC-SHA256 e fica registrada com todas as tentativas.

Um destino pertence à empresa e exige webhook:gerenciar. Configurar, testar e reenviar exigem plano pago ativo; sem ele, a API responde 409 ASSINATURA_INATIVA.

Entrega de um evento1. Cotejar → Seu receptor: POST · Cotejar-Signature 2. Seu receptor → Seu receptor: confere HMAC e instante 3. Seu receptor → Sua fila: grava event_id e enfileira 4. Seu receptor → Cotejar: 204 5. Cotejar → Seu receptor: Sem 2xx: até oito tentativas em 24 hCotejarSeu receptorSua filaPOST · Cotejar-Signaturecorpo JSON assinadoconfere HMAC e instantegrava event_id e enfileira204Sem 2xx: até oito tentativas em 24 h
Entrega de um evento
Cartão Destino do webhook com estado Ativo, assinatura Ativa, versão 1, o endereço https://example.com/cotejar/eventos e os botões Atualizar destino e trocar segredo, Enviar evento de teste e Revogar destino.

Pelo painel, use Integrações › Webhooks. Pela API:

PUT/v1/webhookwebhook:gerenciarReferência

Crie com If-None-Match: * e Idempotency-Key. Para alterar, rotacionar ou reativar, consulte GET /v1/webhook e envie o ETag completo em If-Match.

Corpo
{ "url": "https://integracao.example/cotejar/eventos" }

A resposta traz o segredo (ctj_whsec_...), exibido nesta operação e na sua repetição idempotente. Guarde-o no gerenciador de segredos: as consultas seguintes não o mostram.

O destino precisa ser HTTPS público na porta 443, sem credenciais nem fragmento. Cada alteração gera um segredo novo e invalida novas entregas da versão anterior. A configuração não testa a conexão: use o evento de teste.

DELETE/v1/webhookwebhook:gerenciarReferência

Revoga o destino com If-Match e Idempotency-Key. Uma requisição já em trânsito ainda pode chegar; novas entregas daquela versão deixam de ser autorizadas.

Requisição do Cotejar
POST /cotejar/eventos HTTP/1.1
Content-Type: application/json; charset=utf-8
User-Agent: Cotejar-Webhooks/1
Cotejar-Event-Id: 10cad8ab-68b8-48a6-a296-868597fb7ccb
Cotejar-Timestamp: 1792000000
Cotejar-Signature: v1=<hmac-sha256-hexadecimal>
{"id":"10cad8ab-68b8-48a6-a296-868597fb7ccb","versao":1,"empresa":"12345678","ocorridoEm":"2026-10-13T16:48:03.875Z","dados":{"tipo":"catalogo.aplicado","catalogoId":"7c7b354f-07b6-4461-9240-263418c81cdb","versao":2}}
Cabeçalho Valor
Cotejar-Event-Id UUID estável do evento, para deduplicação. Igual a id no corpo.
Cotejar-Timestamp Segundos Unix desta tentativa.
Cotejar-Signature v1= seguido do HMAC-SHA256 hexadecimal.

O envelope tem sempre id, versao (hoje 1), empresa (CNPJ raiz), ocorridoEm e dados. O tipo do evento está em dados.tipo.

A entrada do HMAC é timestamp + "." + event_id + "." + bytes_exatos_do_corpo, com o segredo completo, inclusive o prefixo ctj_whsec_. Confira a assinatura antes de interpretar o JSON, recuse instantes com diferença maior que cinco minutos e compare em tempo constante.

Depois da conferência, grave o ID do evento na mesma transação que aceita o processamento. Outra tentativa pode trazer novo instante e nova assinatura para o mesmo evento: não aplique o efeito de novo.

dados.tipo Quando Campos em dados
catalogo.aplicado Uma ingestão terminou em aplicada. catalogoId, versao (nova versão do catálogo)
catalogo.revisao_necessaria Uma validação terminou em precisa_revisao. ingestaoId, catalogoId (pode ser null)
analise.concluida Uma análise terminou em concluida. analiseId, regulamentoDegradado
analise.falhou Uma análise terminou em falhou. analiseId
divergencias.alteradas A vigilância encontrou mudança nos achados. vigilanciaId, novas, reabertas, resolvidas
vigilancia.degradada A vigilância deixou de estar em dia. ultimaVarreduraEm, ultimaColetaUtilEm, varreduraVencida, regulamentoDegradado
vigilancia.restabelecida A vigilância voltou a estar em dia. Os mesmos de vigilancia.degradada
webhook.teste Teste pedido no painel ou pela API. destinoId
analise.concluida
{ "id": "73bbd6da-e275-4051-8c16-9bc7b861c5ce", "versao": 1, "empresa": "12345678", "ocorridoEm": "2026-10-13T16:47:03.109Z", "dados": { "tipo": "analise.concluida", "analiseId": "e0da8bf7-0252-4556-9d52-856cc1349507", "regulamentoDegradado": false } }

O evento é um aviso, não o dado completo. Ao receber analise.concluida, leia o resultado em GET /v1/analises/{analiseId}. Não há garantia de ordem de entrega. Somente uma resposta HTTP 2xx concluída confirma a tentativa. Redirecionamentos não são seguidos.

Histórico de eventos com um Teste de webhook pendente, ocorrido em 13/10/2026 13:24, zero tentativas, sem resposta HTTP e o botão Ver tentativas.
Rota Uso
GET /v1/webhook/eventos Eventos mais recentes e estado das entregas.
GET /v1/webhook/eventos/{id} Envelope original e metadados sanitizados.
GET /v1/webhook/eventos/{id}/tentativas Tentativas em ordem crescente.
POST /v1/webhook/testes Agenda um evento webhook.teste.
POST /v1/webhook/eventos/{id}/reenvios Abre novo ciclo para evento encerrado.

O estado de cada evento é pendente, executando, entregue, falhou ou cancelado. Cada tentativa registra status_http e codigo_falha quando houver.

Teste e reenvio exigem destino ativo, plano pago ativo, ETag atual e idempotência. O 202 confirma o aceite na fila, não a conexão. Os dois POSTs compartilham o teto de cinco pedidos por minuto por empresa. O passo a passo do teste está em Testar com segurança.

O reenvio aceita evento entregue, falhou ou cancelado com menos de 30 dias. Ele preserva ID e corpo, abre outro ciclo de até oito tentativas em 24 horas e autoriza explicitamente o destino atual, inclusive depois de uma rotação. O receptor continua deduplicando pelo ID.

Eventos encerrados há mais de 30 dias ficam elegíveis para limpeza. Eventos pendentes ou em execução são preservados. Trinta dias não prometem exclusão instantânea se houver fila acumulada ou processamento indisponível.

Depois da retirada, a consulta devolve 404. Um recibo idempotente preservado ainda pode referenciar esse ID expirado; repetir o pedido não recria o evento nem outra entrega.