Ways to deliver webhooks reliably
In the previous post, I wrote that “just receiving webhooks” is not simple at all. This time, I want to talk about the tools I’ve tried in practice and where their limits are.
For example, tunneling services like ngrok are great when you need to quickly receive a webhook on your local machine.
But a tunnel is just a tunnel.
If your laptop is off for 10 minutes, the webhook is gone.
If the endpoint is temporarily unavailable, the webhook is gone.
If the destination returns a 500, what happens next depends on whether you even noticed it.
Sure, you could say, “Well, just run a proper server.”
And that is usually exactly how it starts.
Then you add a queue.
Then retries.
Then logging.
Then replay.
Then multiple destinations.
Then you try to understand why a webhook “sometimes doesn’t arrive.”
Then you discover that the provider sends requests inconsistently.
Then it turns out that retries without idempotency break your business logic.
Then race conditions appear.
Then someone says, “Let’s also keep a request history.”
And suddenly, a “simple webhook receiver” has turned into a separate infrastructure system.
And this is not some unique problem.
SMTP, for example, is also a simple protocol. But companies still use Postmark, SendGrid, and other email delivery services.
Because “sending an HTTP request” is easy. Doing it reliably, transparently, and predictably in production is a very different task.
I also tried the “let’s just store everything in RabbitMQ” approach.
But a queue by itself does not solve observability.
It does not answer questions like:
— what exactly came in;
— where it was sent;
— how many attempts were made;
— what response the destination returned;
— whether a specific delivery can be retried;
— why some destinations received the webhook and others did not.
Then come custom tables, statuses, retry policies, retention, cleanup jobs, and a UI for debugging. In other words, you end up building a separate reliability layer anyway. That was the moment I realized the problem is not “receiving a webhook.” The problem is predictable delivery.
So that a request:
— does not disappear;
— is not lost during temporary issues;
— is visible;
— can be retried;
— is delivered consistently and transparently.
No magic. No hidden modifications. No AI “optimizations.” No black boxes.
Just a clear and reliable webhook delivery system.
That is exactly why I started building Adal.
No AI. No surprises. Predictable webhook delivery.