O webhook é o único recurso em que o cliente digita um endereço num formulário e os seus servidores vão lá chamá-lo. Todas as outras requisições do seu backend vão para lugares que você escolheu. Esta vai para onde o cliente mandar, sempre que o seu produto gerar um evento, enquanto o endpoint existir.
Esta semana o Kookee ganhou webhooks e, em cima deles, integrações com Slack, Discord, Telegram, GitHub, Jira, Asana e Freshdesk. Você publica um post e o seu site estático é reconstruído; um visitante deixa um feedback e ele cai no canal do Slack da sua equipe ou vira uma tarefa no Jira.

O formulário com o campo de URL levou uma tarde. Tudo o que vem abaixo levou o resto da semana, e alguns destes pontos eu só percebi ao revisar o plano, antes de existir uma linha de código.
Oito coisas que quem envia webhooks precisa acertar
1. Verifique o endereço no envio, não ao salvar
A verificação óbvia é recusar http://localhost e 10.0.0.5 quando alguém salva um endpoint. E também é a que não serve para nada. A URL é verificada uma vez, mas o nome dentro dela é resolvido de novo a cada entrega, e nada impede que hooks.example.com aponte para um endereço público na segunda-feira e para 169.254.169.254, o serviço de metadados da nuvem, na terça.
Por isso a verificação de verdade acontece na hora do envio. O Kookee resolve o host, recusa a entrega se algum endereço retornado for privado, de loopback, link-local ou reservado (incluindo IPv4 escrito dentro de IPv6), e então se conecta exatamente ao endereço que verificou, para que uma segunda consulta não possa trocá-lo. Redirecionamentos não são seguidos: um 302 para um endereço interno é só uma resposta.
Uma faixa é fácil de esquecer: os endereços públicos dos seus próprios servidores. Atrás de uma CDN, o IP da sua origem é o que um atacante mais quer alcançar, e um webhook o chamaria sem cerimônia de dentro do seu firewall. Os do Kookee estão na lista de bloqueio.
2. Assine do jeito que todo mundo assina
Eu poderia ter inventado um cabeçalho de assinatura. Em vez disso, o Kookee assina as requisições no padrão Standard Webhooks: um webhook-id, um webhook-timestamp e um HMAC em webhook-signature. Quem escreve o receptor ganha uma biblioteca na sua linguagem, uma janela de cinco minutos contra replay sem esforço nenhum e um formato a menos para aprender.
O segredo de assinatura fica criptografado em repouso e pode ser revelado e regenerado no menu do endpoint. Regenerá-lo invalida o antigo na hora, que é o que você quer depois de um vazamento.
3. Um receptor lento só deve atrasar a si mesmo
Toda entrega espera pelo servidor de outra pessoa. Se o endpoint de um cliente leva nove segundos para responder e os seus workers processam poucas entregas por vez, um único cliente vira, sem alarde, a fila de todo mundo.
Dei a cada requisição um prazo rígido de 10 segundos, incluindo a consulta DNS, e as entregas rodam 25 por vez em cada processo. Só isso não basta, porque um endpoint lento, com novas tentativas o dia inteiro, continua ocupando essas vagas. Então cada endpoint ganha 2 minutos e meio de espera por hora, e cada conta 7 minutos e meio somando todos os seus endpoints. Uma entrega que passa desse orçamento é pulada, e o endpoint diz isso com todas as letras. Uma entrega pulada nunca conta como falha: o receptor está lento, não quebrado, e desligá-lo puniria a coisa errada.
Eventos de conteúdo e eventos de visitantes são contados separadamente. Uma enxurrada de feedback de spam nunca deveria ser o motivo de o seu site não ter sido reconstruído depois que você publicou.
4. Tente de novo por horas, depois pare e avise alguém
Servidores caem durante deploys e voltam. Uma entrega que falha ganha dez novas tentativas ao longo de cerca de oito horas e meia, com esperas de 1, 2, 4 e até 256 minutos, sempre com o mesmo webhook-id, para que o receptor possa descartar duplicatas.
O que eu não queria era um endpoint falhando em silêncio desde março. Uma resposta 410 Gone desliga o endpoint na hora. Cinco eventos seguidos que falham em todas as tentativas também o desligam. Nos dois casos, o proprietário do projeto recebe uma notificação e um e-mail dizendo qual endpoint e por quê, e religá-lo leva um clique.
5. Envie o evento quando a página muda, não quando a linha muda
“Entrada atualizada” parece simples até você contar o que dispara esse evento. O salvamento automático, um erro de digitação corrigido num rascunho, uma tradução sendo vinculada: um hook ingênuo reconstrói um site estático quarenta vezes numa tarde.
Os eventos de conteúdo do Kookee seguem o que o visitante consegue ver. entry.published dispara quando uma entrada fica visível, e isso inclui um post agendado que vai ao ar às 9:00 sem ninguém no teclado: cada data futura tem um job esperando por ela. entry.updated dispara só para uma entrada que estava visível e continua visível. Um rascunho salvo dez vezes não envia nada.
6. Cada destino tem seu próprio jeito de chamar as pessoas
As integrações publicam um texto escrito por um desconhecido nas ferramentas da sua equipe, e cada ferramenta tem sua própria marcação para chamar a atenção. Um feedback com o título <!channel> urgent notificaria um canal inteiro do Slack. No Discord, o mesmo truque é @everyone, mais um link mascarado que esconde para onde leva. No GitHub, #123 ou owner/repo#1 no corpo de uma issue deixa uma referência cruzada na issue de outra pessoa, em nome da sua conta.
Por isso cada serviço tem seu próprio escape. Os caracteres especiais do Slack são escapados e o unfurling fica desligado. No Discord, todo caractere de Markdown é escapado, as menções ficam desativadas e os embeds, suprimidos. O texto para o Telegram passa por escape de HTML. Numa issue do GitHub, o texto do visitante fica num bloco de código com um delimitador mais longo que qualquer sequência de crases dentro dele. O Freshdesk recebe texto com escape de HTML, porque o corpo do ticket é HTML. E o Jira recusa uma descrição com parágrafo vazio, então as linhas em branco são removidas antes de chegar a ele.
Nada disso aparece quando funciona. É a diferença entre uma ferramenta de feedback e um jeito de qualquer pessoa mandar mensagem para a empresa inteira.
7. Credenciais nunca viajam na fila
Uma URL de webhook do Slack é uma senha: quem a tem pode publicar no seu canal. O mesmo vale para o token de um bot do Telegram, que fica bem no meio da URL da API. O design tentador coloca a configuração descriptografada no job de entrega, para o worker ter tudo de que precisa. Aí cada token fica no Redis em texto puro, em cada nova tentativa, por horas.
Os jobs do Kookee levam um id de integração, um id de evento e o evento. O worker carrega a linha, descriptografa a configuração no último momento e nunca grava a URL de uma requisição em log. Os tokens também ficam fora do log de auditoria, que registra quem mudou o quê, mas não o segredo que a pessoa digitou.
8. Leia as regras do outro lado antes de escrever as suas
Meu primeiro plano tratava 403 Forbidden como “esta integração nunca vai funcionar” e desligava a integração. O cliente HTTP do Node não envia o cabeçalho User-Agent a menos que você defina um, e o GitHub responde com 403 a uma requisição sem ele. Toda integração com o GitHub teria se desligado na primeira entrega.
A revisão do plano pegou isso, e agora toda requisição leva content-type e um User-Agent. A lição foi além de um cabeçalho: o GitHub também responde com 403 quando o limite de requisições estoura, então esse caso tenta de novo, e o Telegram informa um chat apagado como um 400 com uma mensagem, então esse caso desliga. Cada serviço ganha uma lista curta de exceções a uma regra simples, escrita ao lado do código que envia para ele.
Se você constrói webhooks
Resolva o host na hora do envio, recuse endereços privados e reservados, conecte-se ao endereço que verificou e não siga redirecionamentos.
Coloque os endereços públicos dos seus próprios servidores na lista de bloqueio.
Assine com Standard Webhooks em vez de um formato próprio.
Dê a cada entrega um prazo rígido, e a cada endpoint e conta um orçamento de tempo, para que um receptor lento não segure todo mundo.
Tente de novo por horas com um id de mensagem estável, depois desligue o endpoint e avise o dono dele.
Dispare eventos de conteúdo pelo que os visitantes veem, não a cada salvamento.
Escape o texto para a marcação de cada destino antes de publicá-lo.
Mantenha as credenciais fora da fila, dos logs e da trilha de auditoria.
Os webhooks estão em todos os planos do Kookee, e as integrações no Pro e no Ultra. Envie eventos para o seu site com webhooks cobre os eventos, o payload e a verificação da assinatura.
Continue lendo
Conecte Slack, Discord, Telegram, GitHub, Jira, Asana ou Freshdesk: o que cada integração precisa e o que ela envia.
Seu widget de feedback não deveria obrigar as pessoas a criar conta primeiro: como o feedback dos visitantes chega até você.
Seu servidor MCP não deveria obrigar as pessoas a colar um token: a outra metade de conectar o Kookee às suas ferramentas.