Adal

Permanent webhook endpoints with visibility and replay.

Visit Website
July 20, 2026 We needed reliable local webhooks, not more infrastructure

While building webhook integrations, we kept running into the same mismatch.

External services can send events at any time. Local development environments are available only when a developer is actively working.

Your laptop may be offline. Docker may be restarting. The local application may not be running yet. But the webhook provider does not know that—it simply sends the request.

The obvious solutions came with trade-offs

A tunneling service can expose a local port to the internet, but the tunnel still depends on the laptop, internet connection, and application being available when the webhook arrives.

We could also run our own VPS. That would mean configuring DNS, HTTPS, a reverse proxy, request storage, retries, monitoring, and security updates.

And yes, we could deploy the whole thing to Kubernetes.

All of those approaches are valid in the right context. But they felt excessive when the original requirement was simply:

Receive the webhook now and deliver it to the local application later.

The idea behind Adal

This problem shaped one of the core ideas behind Adal: webhook receipt and local delivery should be separate operations.

The resulting flow is simple:

Webhook provider → Adal Server → Adal CLI → local application

An Adal Server provides a permanent public HTTPS endpoint. When it accepts a webhook, it stores the request in the selected region.

Adal CLI connects the Server to a local or private application. If the laptop is offline when the webhook arrives, the request can wait until the CLI reconnects, subject to the configured delivery settings and retention period.

This means developers do not have to keep a tunnel running continuously or repeat an external action just to generate the same webhook again.

For example, if a payment provider sends a payment.completed event while the developer is offline, Adal can receive it first and deliver it to the local handler later.

What we learned

The useful part of this approach is not replacing every tunnel, VPS, or Kubernetes cluster.

It is recognizing when infrastructure is solving a larger problem than the one you actually have.

A tunnel is useful when you need temporary public access to a running local port. A VPS or Kubernetes makes sense when you need to host and operate persistent applications.

But when the requirement is delayed delivery to a local environment, separating receipt from delivery can remove a surprising amount of infrastructure.

We are still developing Adal and refining this workflow. We would be interested to hear how other founders and developers handle webhooks during local development:

  • Do you rely on provider retries?

  • Do you keep a tunnel running?

  • Have you built your own webhook receiver?

  • Or do you simply repeat the original event when something gets lost?

Read the complete article: When you need webhooks but not kubernetes

Comment

July 13, 2026 What happens to your webhooks when the receiving service stays offline?

One of the infrastructure problems we’re working on with Adal sounds simple at first:

What happens when a webhook arrives, but the service that needs to process it is offline?

Most webhook integrations connect the provider directly to the receiving application:

Provider → Your application

This works until the receiver is deploying, restarting, behind an unstable network route, or unavailable for longer than the provider’s retry window.

At that point, the outcome depends entirely on the sender. Some providers retry for hours. Some make only a few attempts. Others give you a dashboard where you can trigger another delivery manually—but not all of them do.

The receiving application has very little control over this.

The approach we took

Adal puts a persistent receiving layer between the provider and the final destination:

Provider → Adal → Your application

Adal accepts and stores the webhook first. Delivery to the application happens separately.

If the destination is temporarily unavailable, Adal retries automatically. If all automatic attempts are exhausted, the user can restart the delivery manually after the service comes back online—as long as the webhook is still within its retention period.

There are two delivery options:

  • Direct HTTP for services with public HTTPS endpoints.

  • Adal CLI for local or private services without public IP addresses or open inbound ports.

The CLI establishes an outbound WebSocket connection, so a webhook can be delivered to an application running behind NAT, inside a private network, or even on a developer’s laptop.

The bigger lesson

The important part isn’t the retry algorithm itself. It’s separating two events that are often unnecessarily coupled:

  1. receiving the webhook;

  2. processing the webhook.

Once the request is stored independently, an outage becomes a recoverable delivery problem instead of an immediately lost event.

Of course, retries introduce another issue: duplicate delivery. A receiver may process a webhook successfully but fail to return a response before the request times out. Any retry system can then send the same event again.

That means the receiving application should still be idempotent and keep track of event IDs wherever possible. Reliable transport does not replace good webhook-handler design.

A question for other founders

I’m curious how other indie hackers handle this in their products:

  • Do you rely entirely on the webhook provider’s retry policy?

  • Have you built your own queue and redelivery interface?

  • Do your users ever need to receive webhooks inside private networks?

  • How long do you retain failed webhook deliveries?

I wrote a more detailed breakdown of the architecture, automatic retries, manual redelivery, and the differences between Direct HTTP and CLI-based delivery here:

Full article: https://adal.cloud/blog/2026-07-13-reliable-webhook-delivery-over-unstable-networks

I’d be especially interested in hearing about failure cases you’ve encountered in production.

Comment

July 6, 2026 Why we added idempotency keys to Adal

One of the less obvious problems with webhooks is that successful processing and successful delivery confirmation are not the same thing.

A webhook provider may send a request, your service may process it successfully, and then the HTTP response may be lost because of a timeout or network issue.

From the sender’s perspective, the delivery failed.

From your service’s perspective, the business action already happened.

The sender retries.

Now your system may create another order, send another email, create another CRM record, trigger another deployment, or charge another payment.

That is why webhook consumers should not assume that a webhook will arrive exactly once.

They should assume that the same logical event may be delivered multiple times.

The tempting but unreliable approach

A common idea is to detect duplicates by comparing request bodies.

For example:

  • save the webhook payload;

  • calculate a hash;

  • reject the next request with the same contents.

The problem is that identical payloads do not necessarily represent the same event.

Two invoices can have the same amount and currency. Two users can submit the same form. Two separate events can contain identical JSON.

The payload is data. It is not a reliable event identity.

The better approach: an explicit key

The usual solution is an idempotency key.

Instead of guessing whether two requests are the same, the sender provides a unique identifier for one logical operation.

The receiver stores that key and treats any later request with the same key as a repeat.

A simple implementation can look like this:

INSERT INTO processed_webhooks (idempotency_key, processed_at) VALUES ($1, NOW()) ON CONFLICT (idempotency_key) DO NOTHING;

If the insert succeeds, the webhook is new.

If the key already exists, the service can safely return success without repeating the side effect.

The important detail is that this needs to be atomic. A “check first, then insert” flow can still create duplicates when two deliveries arrive at the same time.

Why this belongs in Adal

Not every webhook provider gives you a useful event ID or idempotency key.

Some include an identifier inside the payload. Some expose it in headers. Some provide no stable identity at all.

So we added an optional idempotency header to Adal.

When enabled for a destination, Adal adds:

X-Adal-Idempotency: <unique-key>

The receiving service can use that value to recognize repeated deliveries safely.

The setting is enabled per destination, not globally.

That was deliberate.

Some destinations need the original request to remain untouched. Others are internal services where an idempotency key is exactly what prevents duplicate work. We did not want to force one behavior on every integration.

Retry and replay are different operations

The key behavior is intentionally explicit:

Automatic retry of the same request → same idempotency key Manual retry of the same delivery → same idempotency key Replay of an older webhook → new idempotency key

A retry is another attempt to deliver the same logical request.

A replay is a deliberate creation of a new request based on an older webhook.

Even if the payload is identical, replaying it is a new action. It should not be silently ignored by the receiver as if it were only another failed delivery attempt.

That distinction makes retries safe without making replay useless.

A small feature with a large effect

Idempotency keys are often discussed in the context of payment APIs, but they matter anywhere a webhook causes a side effect:

  • creating orders;

  • provisioning infrastructure;

  • sending emails;

  • triggering background jobs;

  • synchronizing CRM records;

  • creating tickets;

  • calling external APIs;

  • processing queue-like workflows.

The more expensive or irreversible the action is, the less acceptable duplicate execution becomes.

Reliable webhook delivery is not only about retrying when something fails.

It is also about making retries safe for the systems that receive them.

That is the goal behind X-Adal-Idempotency in Adal.

Adal is a webhook delivery and observability platform built around predictable delivery behavior, retries, replays, and clear request history.

Read the full article: https://adal.cloud/blog/2026-07-06-why-webhooks-need-idempotent-processing

Comment

June 29, 2026 Why webhook reliability is harder than it looks

Webhooks are often treated as a simple HTTP problem:

One service sends a request, another service returns 200 OK.

But once webhooks affect payments, orders, subscriptions, CRM updates, or customer access, the actual problem becomes much larger.

A webhook can be lost because of a temporary outage, a deployment issue, an expired TLS certificate, a database problem, or an infrastructure migration.

And migrations can create the opposite issue too.

For example, after changing a DNS record, some webhook senders may still use the old IP address while others already reach the new server. If both servers are active, the same event may be processed twice. If the old server is gone, some events may fail entirely.

That means reliable webhook handling needs to account for both:

  • events that do not arrive;

  • events that arrive more than once.

The usual answer is an at-least-once delivery model combined with idempotent processing on the receiving side.

In practical terms:

  • retry failed deliveries;

  • store events durably before doing expensive work;

  • use event IDs or idempotency keys;

  • make duplicate processing safe;

  • keep delivery history;

  • provide a way to replay events when something fails.

This is one of the reasons Adal Cloud focuses on retained requests, delivery visibility, retries, and replay rather than treating a webhook as a one-shot request.

A reliable webhook system is not just one that can send requests. It is one that helps teams understand what happened when delivery does not go as planned.

We published a deeper technical breakdown here:

https://adal.cloud/blog/2026-06-29-what-does-losing-a-webhook-mean

How does your team currently handle webhook retries and duplicate events?

#webhooks #buildinpublic #saas #backend #devops

Comment

June 24, 2026 Why I made Adal CLI Open Source

When you build a tool that runs on someone else’s machine, trust becomes part of the product.

Adal CLI is a local app. It runs in the user’s environment, which means the trust bar is naturally higher than for many other types of software.

Users have a right to ask:
- What does this app actually do?
- What files does it read?
- Does it send anything over the network?
- Can I build it myself instead of downloading a binary?
- What happens if the project is no longer actively maintained?

For Adal CLI, I did not want the answer to be “just trust me.”

That is one of the reasons I made it open source.

Open source does not automatically make a product trustworthy. It does not replace good documentation, clear communication, reproducible builds, or responsible maintenance.

But it does make trust more verifiable.

Anyone can inspect the code, review how the app works, build it from source, open an issue, suggest an improvement, or fork the project if they need to. Even if most users never read the source code themselves, the possibility matters.

For a local developer tool, that transparency feels important.

There is also a sustainability angle. If a small independent tool becomes useful to people, it should not depend entirely on one person forever. Open source gives the community a way to continue, adapt, or fix the project if needed.

Not every product needs to be open source. But when a product runs locally, touches files, works with terminal commands, or handles sensitive data, openness can become one of the strongest arguments for trust.

That is the bet behind Adal CLI.

Trust is hard to earn through promises. It is easier to build through transparency, verifiability, and respect for the user.

Curious how other indie hackers think about this:

Would you open source a local-first developer tool from day one, or wait until the product is more mature?

Comment

June 13, 2026 A small webhook use case I keep coming back to

While building Adal, I noticed that webhooks are not always about immediate automation.

Sometimes the first task is much simpler: you do not want to process the webhook yet, you just want to see what was actually sent. This happens a lot during early integration work.

A service has webhook support. The documentation shows an example payload. Everything looks clear enough. But before writing the real handler, you still want to check the actual request.

What headers are included? Where is the event type? What does the body look like for this specific event? Are there signatures or timestamps? Does the real payload match the documentation?

Technically, you can solve this in many ways. You can start a local server, expose it with a tunnel, write a temporary handler, log the incoming request, and then format the result so it is readable. That works. But for the "I just want to look at the webhook" stage, it often feels like too much setup.

This is one of the small scenarios I want Adal to handle well.

You create an endpoint, get a permanent URL, paste it into the external service, and then inspect incoming requests in the interface. It is not meant to replace your backend. It is not trying to make decisions for you or transform the request into something else. The goal is simpler: show the real request clearly.

Method, path, headers, body, size, and other useful details should be visible before you start building assumptions around them. I think this matters because a lot of integration work starts with uncertainty. Documentation helps, but the real request is the source of truth.

Once you can see the webhook, the next steps become much clearer: do I need this event? Where is the identifier I should store? Which field should trigger the business logic? How should I verify the signature? Does my handler need to support multiple event shapes? For me, this is becoming an important part of Adal's positioning.

It is not only about delivering webhooks to local development or production destinations. It is also about visibility. Sometimes visibility starts with a very simple question: what did the service actually send?

Comment

June 11, 2026 Adal is public now. Looking for feedback from developers who work with webhooks

Adal is now public, and I'm looking for early feedback from developers who work with webhooks.

I'm Yuriy, the founder of Adal and a developer with 10+ years of commercial experience. Over the years, I kept running into the same problem: receiving real webhook requests from the internet on a developer machine is often more complicated than it should be.

Documentation does not always match the actual webhook payload. Test data is often too clean and repetitive. Real requests are usually much more useful for debugging because they reveal unexpected fields, headers, formats, and edge cases.

That is the problem Adal is trying to solve.

Adal gives developers permanent webhook endpoint URLs, stores incoming requests, shows delivery history, and lets requests be replayed or retried when needed. With the open-source Adal CLI, requests can also be delivered to a local machine when the developer connects.

A few days ago, I launched Adal on Product Hunt and got almost no feedback: a few upvotes, almost no comments, and no big launch moment.

At first, that felt strange. But it also made one thing very clear: launch is not distribution. Publishing a product is only the beginning. The harder part is finding the people who actually feel the problem.

So I'm posting this first update here.

If you build, test, debug, or operate webhook-based systems, I would really appreciate your feedback:

Is the positioning clear?

Does "permanent webhook endpoints + request visibility + replay" sound useful?

Where would you look for the first users of a developer tool like this?

Adal: https://adal.cloud

CLI: https://github.com/adal-cloud/adal-cli

5 Comments

  1. 1

    One thing I'd be careful with:

    The interesting question may not be whether developers need better webhook visibility.

    It's what they're actually trying to reduce by using it.

    Those sound similar, but they can lead to very different positioning decisions.

    1. 1

      That's a very useful distinction.

      I think you are right: "better webhook visibility" is not the final outcome. It is more of a mechanism.

      What developers and teams are probably trying to reduce is the time and uncertainty around webhook failures: figuring out whether the provider sent the request, whether the app received it, what was delivered, what failed, and whether the same case can be reproduced.

      Adal approaches this by recording the full delivery path — from the incoming request to each delivery attempt and retry — but the outcome is not just "more logs". The outcome should be fewer black-box moments when something fails and nobody can clearly explain why.

      Thanks for framing it this way. It gives me a better way to think about the positioning.

      1. 1

        Possibly.

        The reason I stopped short earlier is that I don't think the interesting part is the framing itself.

        I think it's the decision that follows from it.

        I wouldn't try to unpack that properly in a thread.

        If you'd like the tighter version, drop your email and I'll put it together properly.

        1. 1

          That makes sense, and I agree — the useful part is not just the framing, but what decisions it should lead to.

          Indie Hackers does not allow me to post contact details yet, but my email is listed publicly on my Indie Hackers profile page.

          I’d be glad to read your tighter version. Thanks for taking the time to think about this properly.

          1. 1

            Sent you a note by email.

            I think the decision matters more than the framing itself right now.

About

Webhook delivery is often opaque and hard to debug. Adal exists to make it predictable: permanent endpoints, full request visibility, delivery history, and replay.