FanHook · plain english guide

What FanHook does, and why you would want it

No jargon, and no assumptions about what you already know. If you have ever wondered what a webhook is, or why yours keep going missing, start here.

First, what is a webhook?

Thirty seconds, then we will get to the useful part.

A webhook is a message that one app sends to another the moment something happens. Stripe sends one when a customer pays you. GitHub sends one when somebody pushes code. Shopify sends one when an order comes in. The message arrives at a web address that you choose, and your software decides what to do with it.

It is a genuinely good idea, and it gets awkward the moment you are connected to more than one service.

Every sender behaves differently when delivery fails. Stripe is generous and will keep trying for up to three days. GitHub does not automatically try again at all, so a message that arrives while your server is restarting is simply gone unless you go and find it by hand. Everyone else sits somewhere in between. How safe your data is depends entirely on which company happened to send it, and you are expected to know the answer for each one.

And nothing is in one place. Each sender has its own settings page, its own list of addresses and its own delivery history, tucked somewhere different in each dashboard. Once you are connected to three or four services, answering "did that actually arrive?" means logging into three or four separate places and knowing where each of them hides the answer.

FanHook sits in the middle

You hand the sender one FanHook address instead of your own.

Stripe sends once FanHook check, copy, retry Your live app Staging copy Reporting
One message in, as many copies out as you need. The sender never has to know about any of it.

When a message arrives, FanHook first checks that it genuinely came from who it claims to be from, so nobody can pretend to be Stripe and tell your app about a payment that never happened. Then it passes a copy to every destination you have set up.

If a destination does not answer, FanHook waits and tries again. If it still cannot get through, the message is kept on a holding shelf so you can look at it and send it again, instead of it vanishing. It waits there for 7 days on the free plan and 90 days on Starter.

Why people use it

Three reasons, and they come down to one thing: a single place to look.

One in, many out

Point the sender at FanHook once, then add or remove destinations whenever you like. No code changes, and no going back to the sender's settings page every time something moves.

Nothing disappears quietly

Failed deliveries are retried automatically, and anything that still cannot get through is set aside for you rather than dropped. Every sender gets the same treatment, including the ones that would otherwise give you no second chance at all.

You can see what happened

Every message and every delivery attempt is listed in the dashboard, with the actual contents. When somebody says the webhook did not work, you can check instead of guess.

Common ways people use it

If one of these sounds like your week, FanHook is aimed at you.

Selling something online

A payment lands and three of your own services need to know: the app that unlocks the account, the one that sends the receipt, and the job that tracks revenue. You can wire all three into the sender by hand and then check three separate delivery logs when something looks off, or point it at FanHook once and watch all three in a single list.

Testing a change safely

Add your staging server as a second destination and it receives a copy of real traffic while your live app carries on untouched. You get to see how new code handles genuine messages before it is responsible for them.

Deploying without holding your breath

Your server is unreachable for a few seconds during every deploy. Some senders, GitHub included, never try again, so anything landing in that window is lost for good. FanHook retries, and holds onto whatever still fails.

Moving or renaming a service

Change the destination in FanHook and you are done. No hunting through six different settings pages to update the same URL, and no forgotten integration left quietly pointing at a server that no longer exists.

Working out what went wrong

Somebody reports that an order never arrived. You open the log, find the message, and see exactly what was sent and how each attempt went. Then you resend it with one click.

Watching several services at once

Stripe, GitHub and Shopify each keep their own settings page and their own delivery history, in three different places with three different layouts. Connect them all to FanHook and there is one list, one holding shelf and one resend button covering the lot.

Questions people actually ask

Short answers. Ask us anything that is missing.

Getting started

Do I need to be a developer to use this?

You need to be comfortable pasting a web address into a settings page, and you need somewhere for the messages to go. That is genuinely most of it. Creating an inbox and adding destinations is all done by clicking around the dashboard, with no code involved.

The part that needs a developer is the receiving end, because something has to understand the message once it arrives. FanHook passes the sender's message along exactly as it came, so each destination has to be something built to read that particular sender's format. In practice that means your own services rather than off the shelf tools.

What is a sink?

It is just our word for one inbox. A sink has its own web address that you give to a sender, and its own list of destinations that copies get forwarded to. Most people start with one sink per service they are connecting, so payments and code pushes stay separate and easy to tell apart.

Which services does it work with?

FanHook knows how to verify messages from Stripe, GitHub, Shopify, Clerk, Linear and PagerDuty out of the box, which means it can confirm those messages are authentic without any setup from you beyond pasting in a signing secret. There is a setup page for each one.

What if the service I use is not on that list?

Pick the generic option and it will work with anything that can send a web request. You still get the copying, the retries and the logs. The only thing you lose is the built in authenticity check, because that is specific to how each company signs its messages.

Staying safe and reliable

Could somebody send me fake events?

Not for the supported services. Each one signs its messages with a shared secret, and FanHook checks that signature before accepting anything. A message that fails the check is rejected and never reaches your destinations.

Where the sender also signs a timestamp, which Stripe and Clerk both do, FanHook checks that the message is recent as well, so somebody cannot record a real one and replay it at you later.

What happens if my server is down when a message arrives?

FanHook keeps the message and tries three times in total: immediately, about thirty seconds later, then about two minutes after that. If all three fail, the run is repeated up to twice more for each destination that has not accepted it yet, so a destination gets up to nine attempts across roughly ten minutes. A deploy or a brief wobble is comfortably inside that window.

Worth being straight about the limits. That window covers minutes, not hours. If a destination is down for an afternoon, the message will not keep retrying until it comes back. It goes to the holding shelf described below, where it waits for you rather than disappearing.

Stripe already lets me add several addresses and retries for days. What does this add?

A fair question, and for Stripe on its own the honest answer is: not much. Stripe is the best behaved sender there is. It accepts up to sixteen addresses and keeps retrying a failed one for three days, which is more patient than FanHook is.

The value shows up once Stripe is not the only service you are connected to. GitHub does not retry at all. Others give you a handful of attempts and no way to see what happened. Each one keeps its history in a different place, so you end up learning four dashboards and four sets of rules. FanHook gives every sender the same retry behaviour, the same holding shelf and the same log, so you learn it once.

And if it is still down after all that?

The message goes to the dead letter queue, which is a formal sounding name for a holding shelf. It stays there for 7 days on the free plan and 90 days on Starter. You can open it in the dashboard, see what failed and why, and resend anything once you have fixed the problem at your end.

Can I deliver to a server on my own private network?

No, and that is deliberate. Destinations have to be publicly reachable addresses. Allowing internal ones would let somebody use FanHook to poke around inside private networks, so we block them outright rather than making it an option you could get wrong.

Does adding FanHook slow anything down?

It adds one short hop. FanHook accepts the message, confirms it is genuine, and answers the sender straight away, then handles the deliveries separately. That means a slow destination on your side never causes the original sender to time out and give up.

Cost and limits

What does it cost?

There is a free tier that needs no card, and a Starter plan at nine dollars a month.

PlanMessages / month per inboxInboxesDestinations per inboxHistory kept
Free1,000133 days
Starter50,00051030 days

What counts as one message?

One arrival counts as one, no matter how many destinations it then goes to. If a sale is copied to three places, that is one message and not three. Retries do not count again either.

What happens if I go over the limit?

New messages are turned away until the month resets, and the dashboard tells you clearly that you have hit the cap rather than leaving you to work it out. Nothing already stored is deleted.

What happens to my data if I stop using FanHook?

Deleting an inbox removes everything tied to it: the stored messages, the delivery history, the destinations and the held items. It happens immediately and it cannot be undone, so export anything you want to keep first.