Se você já integrou com gateways de pagamento, serviços de notificação ou plataformas de e-commerce, provavelmente já lidou com webhooks. Eles são o mecanismo padrão para comunicação assíncrona entre sistemas — mas debugar webhooks pode ser uma experiência frustrante. Neste artigo, vamos entender como webhooks funcionam, por que são tão difíceis de depurar e quais práticas e ferramentas podem tornar esse processo muito mais eficiente.
O que são Webhooks?
Webhooks são callbacks HTTP que um sistema externo envia para sua aplicação quando um evento acontece. Diferente de uma API tradicional onde você faz polling (consultando periodicamente), com webhooks o provedor notifica você proativamente.
┌─────────────┐ Evento ocorre ┌──────────────────┐
│ Provedor │ ──── POST HTTP ────> │ Sua Aplicação │
│ (Stripe, │ │ (endpoint que │
│ GitHub) │ <─── 200 OK ──────── │ recebe o hook) │
└─────────────┘ └──────────────────┘
Exemplos reais de webhooks
| Provedor | Evento | O que o webhook envia |
|---|---|---|
| Stripe | Pagamento aprovado | Objeto payment_intent com status, valor e metadados |
| GitHub | Push no repositório | Commits, branch, autor e diff summary |
| Mercado Pago | Pagamento atualizado | ID do pagamento e novo status |
| Twilio | SMS recebido | Número de origem, destino e conteúdo |
| Shopify | Pedido criado | Detalhes do pedido, itens, cliente e valor |
O conceito é simples: em vez de sua aplicação perguntar “aconteceu algo?” a cada 5 segundos, o provedor avisa quando algo relevante acontece. Isso é mais eficiente, mais rápido e consome menos recursos.
Por que depurar webhooks é tão difícil?
Na teoria, webhooks são elegantes. Na prática, debugar integrações com webhooks apresenta desafios únicos que não existem em chamadas de API tradicionais.
1. Fluxo invertido
Em uma API REST comum, você faz a request e vê a response imediatamente. Com webhooks, o fluxo é invertido — o provedor envia a request e sua aplicação precisa estar disponível para recebê-la. Se sua aplicação falha silenciosamente, o provedor não sabe.
2. Ambiente local inacessível
Quando você está desenvolvendo localmente, sua máquina não tem IP público. O provedor não consegue enviar o webhook para localhost:3000. Ferramentas como ngrok resolvem isso parcialmente, mas criam tunnels temporários que expiram e precisam ser reconfigurados a cada sessão.
3. Sem replay nativo
Se um webhook chegou e seu código tinha um bug, você precisa que o provedor reenvie o evento. Muitos provedores não oferecem retry automático ou demoram para fazê-lo. Você fica preso esperando ou tentando simular manualmente a mesma request com cURL.
4. Payloads imprevisíveis
Cada provedor tem seu próprio formato de payload. A documentação nem sempre é completa, e payloads reais podem conter campos que não aparecem nos exemplos. Sem visibilidade do que realmente chega, você fica debugando às cegas.
5. Sem histórico
Se algo falhou ontem à noite em produção, onde estão os logs? Muitas aplicações não logam o body completo dos webhooks recebidos. Sem histórico, você não consegue reproduzir o problema.
Boas práticas para depuração de webhooks
Logue tudo no recebimento
A primeira regra é nunca descartar informação. Antes de processar o webhook, logue o request completo — headers, body, query params, método HTTP e timestamp.
[HttpPost("webhook/stripe")]
public async Task<IActionResult> HandleStripeWebhook()
{
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
_logger.LogInformation(
"Webhook recebido: {Method} {Path} | Headers: {Headers} | Body: {Body}",
Request.Method,
Request.Path,
JsonSerializer.Serialize(Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString())),
body
);
// Processar o webhook...
return Ok();
}
Valide a assinatura antes de processar
A maioria dos provedores assina os webhooks com HMAC ou outra técnica criptográfica. Sempre valide a assinatura antes de processar o conteúdo. Isso protege contra payloads forjados.
var signature = Request.Headers["Stripe-Signature"];
try
{
var stripeEvent = EventUtility.ConstructEvent(body, signature, webhookSecret);
// Assinatura válida — processar
}
catch (StripeException)
{
_logger.LogWarning("Assinatura de webhook inválida");
return BadRequest();
}
Responda rápido, processe depois
Webhooks geralmente têm timeout curto (5-30 segundos). Se seu processamento é pesado, retorne 200 imediatamente e processe o evento de forma assíncrona usando filas (RabbitMQ, SQS, Azure Service Bus).
[HttpPost("webhook/payment")]
public IActionResult HandlePaymentWebhook([FromBody] WebhookPayload payload)
{
// Enfileirar para processamento assíncrono
_messageQueue.Publish(new ProcessWebhookCommand(payload));
// Retornar imediatamente
return Ok();
}
Implemente idempotência
Provedores podem enviar o mesmo webhook mais de uma vez (retry em caso de falha, duplicação de rede). Seu handler deve ser idempotente — processar o mesmo evento duas vezes não deve causar efeitos duplicados.
public async Task ProcessWebhook(WebhookPayload payload)
{
// Verificar se já processou este evento
if (await _eventStore.ExistsAsync(payload.EventId))
{
_logger.LogInformation("Evento {EventId} já processado, ignorando", payload.EventId);
return;
}
// Processar e marcar como processado
await ProcessEvent(payload);
await _eventStore.MarkProcessedAsync(payload.EventId);
}
Ferramentas para depuração de webhooks
cURL e Postman
Para simular webhooks manualmente, cURL e Postman permitem enviar requests HTTP com headers e body customizados. O problema é que você precisa saber exatamente o formato do payload, o que nem sempre é óbvio.
curl -X POST http://localhost:5000/webhook/stripe \
-H "Content-Type: application/json" \
-H "Stripe-Signature: t=1234,v1=abc..." \
-d '{"type":"payment_intent.succeeded","data":{"object":{"id":"pi_123"}}}'
ngrok
O ngrok cria um tunnel entre um endpoint público e seu localhost. Funciona bem para testes rápidos, mas tem limitações: URLs temporárias que mudam, sem histórico persistente e sem replay nativo.
HookScope
O HookScope é uma ferramenta brasileira projetada especificamente para o ciclo de vida de webhooks. Diferente de soluções genéricas de tunneling, ele oferece:
- URL permanente por endpoint — configure uma vez, use para sempre
- Visualização em tempo real de cada request via WebSocket
- Replay com 1 clique — reenvie qualquer webhook sem depender do provedor
- Forward automático com retry e logs para seu ambiente local ou produção
- CLI para debug local — receba webhooks da nuvem direto no localhost
- Mock responses — teste integrações antes de ter o backend pronto
- Histórico pesquisável com filtros por status, método e data
Para quem trabalha com integrações de pagamento (Stripe, PagSeguro, Mercado Pago), notificações (Twilio, SendGrid) ou pipelines de CI/CD (GitHub, GitLab), é uma ferramenta que economiza horas de debug.
Checklist: configurando depuração de webhooks
- Logue o request completo — headers, body, query params
- Valide a assinatura do provedor antes de processar
- Retorne 200 rápido e processe de forma assíncrona
- Implemente idempotência usando event ID
- Use uma ferramenta de inspeção para ver o que realmente chega
- Mantenha histórico dos webhooks recebidos para debug retroativo
- Documente o formato esperado de cada provedor que você integra
Conclusão
Webhooks são fundamentais para integrações modernas, mas a experiência de desenvolvimento com eles ainda tem muito atrito. A combinação de boas práticas de código (logging, validação, idempotência) com ferramentas especializadas como o HookScope elimina o “debug às cegas” e transforma a depuração de webhooks em um processo visual, previsível e eficiente.
Se você trabalha com integrações e ainda está usando console.log para debugar webhooks, considere investir em tooling adequado — seu eu do futuro vai agradecer.