O que é um webhook

Um webhook é uma requisição HTTP POST que um serviço envia para uma URL sua quando um evento acontece, como um pagamento aprovado. Assim você recebe o aviso na hora, sem consultar a API sem parar.

Atualizado em 5 min de leitura

Objetivos de aprendizado

Depois de ler este artigo, você vai conseguir:

  • Entender o que é um webhook e como ele difere de consultar uma API
  • Receber um webhook e verificar a assinatura HMAC em Node.js
  • Evitar os erros típicos: resposta lenta, evento duplicado e URL sem proteção
Nesta página

Imagine que você vende um produto e quer liberar a conta do cliente assim que ele pagar. Você pode perguntar ao gateway de pagamento a cada dez segundos se o pagamento já caiu, ou pode pedir para ele avisar quando acontecer. Essa segunda opção é um webhook.

Webhooks estão em quase tudo: pagamentos (inclusive confirmação de Pix), notificações do GitHub, mensagens de bots do Telegram, formulários, alertas de monitoramento. Entender como eles funcionam poupa requisições inúteis e evita alguns problemas de segurança.

PollingSeu appServiçoalgo novo?nãoalgo novo?nãoalgo novo?simalgo novo?nãoeventoatrasoWebhookSeu appServiçosem requisiçõeseventoPOST200
No polling você pergunta e só descobre na próxima rodada; no webhook o serviço avisa na hora com um POST.

Como um webhook funciona?#

O fluxo tem três passos:

  1. Você cadastra uma URL no serviço emissor (por exemplo https://seuapp.com/webhooks/pagamentos) e escolhe quais eventos quer receber.
  2. Quando um desses eventos acontece, o serviço envia uma requisição HTTP POST para a sua URL. O corpo costuma ser um JSON com o tipo do evento e os dados.
  3. O seu servidor responde com um código 2xx para confirmar que recebeu.

Se você não responder com 2xx, o emissor considera que a entrega falhou. O Stripe, por exemplo, também trata redirecionamentos 3xx como falha.

Um evento típico se parece com isto:

json
{
  "id": "evt_123",
  "type": "payment.succeeded",
  "data": { "order_id": "ped_42", "amount": 4990 }
}

Os nomes dos campos mudam de um serviço para outro. O que se mantém é a ideia: um POST com um identificador do evento, um tipo e os dados.

Qual a diferença entre webhook e polling?#

No polling, o seu app pergunta de tempos em tempos se há algo novo. É simples, mas a maioria das respostas é "nada novo": você gasta requisições e descobre cada evento com um atraso de até um intervalo inteiro.

No webhook, o aviso chega quando o evento acontece, e só nessa hora. Há menos tráfego e menos latência. Em troca, o seu servidor precisa estar acessível pela internet e pronto para receber requisições de terceiros.

Na prática, os dois se combinam: o webhook avisa e, se você desconfiar que perdeu algum, uma tarefa periódica consulta a API REST para reconciliar.

Como receber um webhook em Node.js?#

Este exemplo usa Express 5 com ES modules ("type": "module" no package.json) e verifica a assinatura do evento. O esquema é o que o GitHub usa: um HMAC-SHA256 do corpo, calculado com um segredo compartilhado e enviado no cabeçalho X-Hub-Signature-256 com o prefixo sha256=. Outros serviços usam nomes de cabeçalho e formatos diferentes (o Stripe usa Stripe-Signature, que também inclui um timestamp), então confira sempre a documentação deles.

js
import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.WEBHOOK_SECRET;

function assinaturaValida(corpo, cabecalho) {
  // se o Content-Type não bater, o express.raw deixa req.body indefinido
  if (!cabecalho || !Buffer.isBuffer(corpo)) return false;
  const esperada = Buffer.from(
    "sha256=" + createHmac("sha256", SECRET).update(corpo).digest("hex")
  );
  const recebida = Buffer.from(cabecalho);
  // timingSafeEqual lança um erro se os tamanhos forem diferentes
  return recebida.length === esperada.length && timingSafeEqual(recebida, esperada);
}

// express.raw guarda o corpo exato em bytes, necessário para a assinatura
app.post("/webhooks/pagamentos", express.raw({ type: "application/json" }), (req, res) => {
  if (!assinaturaValida(req.body, req.get("X-Hub-Signature-256"))) {
    return res.sendStatus(401);
  }

  const evento = JSON.parse(req.body.toString("utf8"));
  console.log("Evento recebido:", evento.type);

  res.sendStatus(200); // responde primeiro, processa depois
});

app.listen(process.env.PORT ?? 3000);

Para testar sem depender de um serviço externo, calcule a assinatura você mesmo:

bash
CORPO='{"id":"evt_123","type":"payment.succeeded"}'
ASSINATURA="sha256=$(printf '%s' "$CORPO" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | awk '{print $NF}')"

curl -i -X POST http://localhost:3000/webhooks/pagamentos \
  -H "Content-Type: application/json" \
  -H "X-Hub-Signature-256: $ASSINATURA" \
  -d "$CORPO"

Se você mudar uma única letra do corpo sem recalcular a assinatura, vai receber 401.

Por que verificar a assinatura?#

A sua URL é pública. Sem verificação, qualquer pessoa que a conheça pode mandar um evento falso de "pagamento aprovado" e fazer o seu sistema liberar uma conta sem cobrar. A documentação do Stripe alerta exatamente para isso.

Três regras para verificar a assinatura:

  • Use o corpo cru. Se um middleware converte o JSON em objeto e serializa de novo, os bytes mudam e a assinatura deixa de bater. O Stripe aponta isso como causa frequente de falha na verificação.
  • Compare em tempo constante. O GitHub recomenda crypto.timingSafeEqual em vez de == para evitar ataques de temporização.
  • Guarde o segredo numa variável de ambiente, nunca no código nem no repositório.

Alguns emissores incluem um timestamp dentro do conteúdo assinado. O Stripe faz isso, e as bibliotecas dele rejeitam por padrão eventos com mais de cinco minutos de diferença, o que barra ataques de repetição. Como camada extra, alguns emissores publicam uma lista de IPs de origem que você pode liberar no firewall.

Quais erros são comuns com webhooks?#

Demorar demais para responder. Se você faz o trabalho pesado antes de responder, o emissor pode dar a entrega como falha por timeout e reenviar. O Stripe recomenda devolver o 2xx antes de qualquer lógica complexa e processar numa fila assíncrona.

Processar o mesmo evento duas vezes. Os emissores podem entregar um evento mais de uma vez, principalmente quando tentam de novo. Guarde o identificador de cada evento processado e descarte os repetidos. Assim o seu handler fica idempotente.

Supor uma ordem. O Stripe diz explicitamente que não garante entregar os eventos na ordem em que foram gerados. Se você precisa do estado atual, consulte a API em vez de confiar na ordem de chegada.

Aceitar todos os tipos de evento. Assine só os que você usa. Menos eventos, menos carga e menos superfície para erro.

Testar só em produção. Use um túnel como o ngrok ou a CLI do emissor, e acompanhe o painel de entregas, onde costumam aparecer o código de status e o horário da próxima tentativa.

Quando não vale usar webhook?#

Um webhook precisa de um servidor acessível e sempre disponível. Se o seu cliente é uma página web ou um app de celular que precisa de dados ao vivo, uma conexão persistente como um WebSocket encaixa melhor. Se o emissor não oferece webhooks, ou se você precisa ler o estado completo de tempos em tempos, o polling continua válido.

Quando perder um evento sai caro, não dependa só do webhook: combine com uma reconciliação periódica pela API.

Cuidado também com o nome. Os webhooks do Discord são uma URL para publicar mensagens num canal: ali você envia, enquanto aqui você recebe.

Como fazer isso na Square Cloud#

No fundo, um webhook é uma rota HTTP da sua aplicação web com uma URL pública e HTTPS. Uma aplicação web na Square Cloud ganha um subdomínio *.squareweb.app com HTTPS em qualquer plano, então você pode cadastrar https://seu-app.squareweb.app/webhooks/pagamentos no emissor sem montar proxy nem certificado.

Passos para o exemplo acima:

  1. Faça o deploy da aplicação Node.js pelo painel, pela CLI, pelo GitHub ou pela extensão do VS Code. O arquivo de configuração squarecloud.app é opcional: o painel o gera durante o deploy, ou você pode escrevê-lo na raiz do projeto.
  2. Defina WEBHOOK_SECRET nas variáveis de ambiente da aplicação. A mudança vale no próximo restart, sem rebuild.
  3. Cadastre a URL pública no emissor e registre um log a cada entrega no seu código: os logs em tempo real do painel mostram o que a sua aplicação imprime.

As aplicações ficam sempre ligadas, sem modo de suspensão. Isso importa porque um emissor que não recebe resposta marca a entrega como falha. Bots do Telegram também podem receber atualizações por webhook em vez de long polling, usando um subdomínio HTTPS.

Veja a página de hospedagem de APIs para conhecer runtimes, planos e limites.

Hospedagem de APIs

APIs em containers sempre ligados, sem cold start

Saiba mais

Perguntas frequentes

Qual a diferença entre webhook e API?

Numa API, você faz a requisição quando quer os dados. No webhook, é o outro serviço que chama você quando algo acontece. Os dois se completam: o webhook avisa e a API serve para buscar os detalhes.

Webhook precisa de HTTPS?

Em produção, sim. O Stripe, por exemplo, exige uma URL HTTPS pública para os endpoints de produção. Além disso, o HTTPS impede que o conteúdo do evento seja lido no caminho.

O que acontece se meu servidor estiver fora do ar quando o webhook chegar?

Depende do emissor. Muitos tentam de novo: o Stripe repete por até três dias com espera exponencial em produção. Outros não repetem, então vale ler a documentação deles e conseguir reconciliar pela API.

Como testar um webhook no meu computador?

Você precisa de uma URL pública que chegue à sua máquina. Dá para usar um túnel como o ngrok, ou a ferramenta do emissor, como o `stripe listen`, que encaminha os eventos para o localhost.

Webhook e WebSocket são a mesma coisa?

Não. Um webhook é uma requisição HTTP pontual do emissor para o seu servidor. Um WebSocket é uma conexão persistente e bidirecional que você abre entre cliente e servidor.

Próximo artigoAPI Gateway vs load balancer: diferençasLoad balancer divide o tráfego entre cópias da mesma aplicação. API gateway é a porta de entrada que autentica, limita e roteia chamadas. Veja quando usar cada um.