Sinais confiáveis para trabalhos que levam tempo
Requisições HTTPS assinadas quando trabalhos, domínios, publicações ou aprovações mudam, entregues pelo menos uma vez.
Registre um endpoint
Adicione um endpoint na página Desenvolvedores ou com a permissão webhooks:write:
curl -s -X POST "$BASE/workspaces/$WS/webhooks" \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: $(openssl rand -hex 16)" \
-H "Content-Type: application/json" \
-d '{"url":"https://hooks.example.com/mini-casa","events":["job.updated","publication.updated"]}'A resposta contém signing_secret (whsec_...), mostrado uma única vez. A URL precisa ser HTTPS na porta 443 e resolver só para endereços públicos; destinos privados, locais e internos são recusados, a verificação se repete antes de cada entrega e redirecionamentos nunca são seguidos. Um espaço pode ter até 10 endpoints ativos.
Prove que você o controla
Endpoints novos começam como pending_verification. O botão Verificar (ou POST /webhooks/{id}/verify) envia um webhook.challenge assinado. Responda 2xx com {"challenge": "<data.challenge>"} e o endpoint passa a active.
Eventos
job.updated: um trabalho ou execução em lote muda de estado.job.item.updated: um item de uma execução em lote muda de estado.domain.updated: um domínio próprio avança nas verificações.publication.updated: uma publicação começa, dá certo, falha ou é substituída.approval.updated: uma aprovação é decidida, expira ou é usada.billing.entitlement.updated: o plano ou os direitos mudam.
Os corpos levam só ids e um estado, nunca credenciais, conteúdo ou dados pessoais. Consulte o estado atual pela API.
Verifique cada requisição
Cada requisição traz SmallWorld-Webhook-Id, SmallWorld-Webhook-Timestamp (segundos unix) e SmallWorld-Webhook-Signature (v1=<hex>,kid=<id da chave>). A assinatura é o HMAC-SHA256 de <timestamp>.<corpo original> com o seu segredo. Use o corpo original antes de interpretar o JSON, compare em tempo constante e recuse carimbos de tempo com mais de cinco minutos de diferença do seu relógio.
import crypto from "node:crypto";
export function verify(secret, timestamp, rawBody, header) {
const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const given = /v1=([a-f0-9]+)/.exec(header)?.[1] ?? "";
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
return fresh && given.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}Garantias de entrega
- Pelo menos uma vez. Uma requisição se repete até você responder
2xx. O id do evento é o mesmo em cada nova tentativa e reenvio, então guarde-o e ignore ids já vistos. - As novas tentativas se espaçam de um minuto a um dia, por 72 horas no total. Depois disso a entrega fica como morta e pode ser reenviada pela página Desenvolvedores ou com
redeliver_webhook. - Um endpoint com 10 entregas mortas seguidas é pausado até você retomá-lo.
- A ordem não é garantida: trate o estado como pista e leia o recurso.