webhooks
Receba as novidades por push, sem ficar consultando.
Movimentação nova, publicação no diário, vigília com resultado, busca concluída: cada novidade vira um HTTP POST assinado no endpoint do seu sistema. Este guia documenta os eventos, o formato de entrega, a verificação da assinatura e a política de reentrega.
assinatura
Verifique a origem antes de processar
Toda entrega vai assinada com HMAC-SHA256 no header x-lexnode-signature: t=<unix>,v1=<hex>. O valor v1 é o HMAC do seu secret sobre a string <timestamp>.<corpo cru>. Receptor sério não processa entrega sem validar:
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody: o corpo CRU da requisição (antes de qualquer JSON.parse).
function verifyWebhook(rawBody, signatureHeader, secret) {
// Tolerante a espaços e a chaves futuras (um eventual v2= é ignorado).
const parts = Object.fromEntries(
signatureHeader.split(",").map((part) => {
const [key, ...rest] = part.trim().split("=");
return [key, rest.join("=")];
}),
);
const { t, v1 } = parts;
if (!t || !v1) return false;
// Janela de replay: rejeite entregas com mais de 5 minutos.
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${t}.${rawBody}`, "utf8")
.digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(v1, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}Python
import hashlib
import hmac
import time
def verify_webhook(raw_body: bytes, signature_header: str, secret: str) -> bool:
parts = {}
for part in signature_header.split(","):
key, _, value = part.strip().partition("=")
parts[key] = value # chaves desconhecidas sao ignoradas
t, v1 = parts.get("t"), parts.get("v1")
if not t or not v1:
return False
if abs(time.time() - int(t)) > 300:
return False
expected = hmac.new(
secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, v1)PHP
function verifyWebhook(string $rawBody, string $signatureHeader, string $secret): bool
{
$parts = [];
foreach (explode(',', $signatureHeader) as $part) {
[$key, $value] = array_pad(explode('=', trim($part), 2), 2, null);
$parts[$key] = $value; // chaves desconhecidas são ignoradas
}
$t = $parts['t'] ?? null;
$v1 = $parts['v1'] ?? null;
if (!$t || !$v1) return false;
if (abs(time() - (int) $t) > 300) return false;
$expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
return hash_equals($expected, $v1);
}Sem escrever código
A conferência é uma conta, não um programa: quatro passos que qualquer ferramenta de automação executa com blocos prontos.
1. Guarde o corpo cru
Assine exatamente os bytes que chegaram. Se a ferramenta já converteu para objeto, use o campo de corpo bruto (raw body) — reserializar o JSON troca espaços e ordem de chaves, e a assinatura deixa de bater.
2. Monte a mensagem
Concatene o t do header, um ponto e o corpo cru: t.corpo. O t vem de x-lexnode-timestamp (o mesmo valor está dentro da assinatura).
3. Calcule o HMAC-SHA256
Chave = o signing secret do endpoint. Mensagem = a do passo 2. Saída em hexadecimal minúsculo.
4. Compare e descarte o atrasado
O resultado tem de ser igual ao v1 do header. E descarte a entrega se o t estiver a mais de 300 segundos do relógio atual — é a janela de replay.
| Ferramenta | Onde fica o passo 3 |
|---|---|
| Make | Função sha256 com HMAC key: chave = signing secret, key encoding UTF-8, output encoding Hex. |
| n8n | Nó Crypto, ação Hmac, type SHA256, encoding HEX; o secret fica na credencial do nó. |
| Zapier | Não há passo de HMAC pronto: use um Code step em JavaScript com o exemplo de Node acima. |
Se a assinatura não bater, o suspeito é sempre o passo 1: o corpo foi remontado no caminho. O botão de entrega de teste no portal manda um evento assinado de verdade para conferir sem esperar publicação nova.
Qual mecanismo dispara o quê?
Três mecanismos de acompanhamento alimentam os webhooks — cada um responde a uma pergunta diferente:
| Quero… | Use | Evento que chega |
|---|---|---|
| Quero as publicações da minha OAB | POST /v1/monitors (tipo OAB) | PUBLICATION_CREATED |
| Quero toda menção a um termo no diário (tese, substância, empresa) | POST /v1/monitors (tipo TERM) | PUBLICATION_CREATED |
| Quero saber quando um processo específico anda | POST /v1/subscriptions | PROCESS_MOVEMENT_CREATED |
| Quero acompanhar um tema ou tese na jurisprudência | POST /v1/publications/text-alerts | PUBLICATION_TEXT_ALERT_MATCHED |
O monitor de processo (tipo PROCESS) complementa a assinatura: emite MONITOR_SYNC_SUCCEEDED a cada varredura (com o campo changed) e MONITOR_SYNC_FAILED quando a varredura falha. Um alvo que a fonte ainda não indexou não falha: o monitor fica em vigília de aparição (o evento de varredura sai com awaitingAppearance) e a primeira aparição chega com firstAppearance, seguida das movimentações do processo.
catálogo de eventos
Os eventos e seus payloads
Cada endpoint escolhe quais tipos quer receber no campo eventTypes. Os exemplos abaixo mostram o conteúdo de payload de cada tipo.
As operações de webhooks (GET/POST/PATCH/DELETE /v1/webhooks/endpoints, rotação de secret, entrega de teste e replay) estão na referência oficial.