What Is a Webhook? A Simple Explanation for Beginners
After our tweet about building a webhook delivery service with Grok Build 0.1 went viral, a lot of people asked the same question: what is a webhook?
We wrote this article to answer this question in the simplest way possible.
What Is a Webhook?
A webhook is a way for one app to tell another app that something happened.
That’s it.
A webhook is basically an automatic message sent from one system to another when a specific event happens.
For example:
Someone pays you on Stripe → Stripe sends your app a webhook.
Someone submits a form → the form tool sends your server a webhook.
Someone pushes code to GitHub → GitHub sends another service a webhook.
A webhook is the internet version of:
“Hey, this thing just happened. Do whatever you need with that information.”
A simple way to understand webhooks
Let’s say you have an online store.
When someone buys something, a few things may need to happen:
You need to mark the order as paid.
You need to send a confirmation email.
You need to notify your team in Slack.
You need to update your CRM.
One way to do this is to keep asking Stripe:
“Did someone pay yet?”
“What about now?”
“Now?”
“Now?”
That’s called polling.
It works, but it’s inefficient. Your app keeps checking again and again, even when nothing has happened.
A webhook flips this around.
Instead of your app constantly asking Stripe if something happened, Stripe tells your app when something happens.
So the flow becomes:
Customer pays.
Stripe detects the payment.
Stripe sends a request to your webhook URL.
Your app receives it.
Your app does whatever it needs to do.
A Webhook is just a simple HTTP request
Technically, a webhook is usually just an HTTP request.
If you understand basic APIs, you can think of it like this:
With a normal API, your app calls another app.
With a webhook, another app calls your app.
That’s the main difference.
A normal API request goes like this: Your app → Stripe
Webhook: Stripe → Your app
So when people say “Stripe sent us a webhook,” they usually mean:
Stripe made an HTTP request to one of our backend URLs with some JSON data.
That data Stripe sends to your app might look something like this (this is in JSON format):
{
“type”: “payment_succeeded”,
“data”: {
“customer_id”: “cus_123”,
“amount”: 5000,
“currency”: “usd”
}
}
Your job is to receive that data, check what type of event it is, and run the right logic.
What does a webhook URL look like?
A webhook URL is just a URL that’s expecting data in a specific format.
Something like:
https://yourapp.com/webhooks/stripe
or:
https://yourapp.com/api/webhooks/github
You give this URL to the external service.
For example, in Stripe, you go to the dashboard and say:
“When a payment succeeds, send the event to this URL.”
Then Stripe will call that URL whenever that event happens.
Your code needs to be ready to accept the event.
For example, in pseudo-code:
app.post(”/webhooks/stripe”, async (req, res) => {
const event = req.body;
if (event.type === “payment_succeeded”) {
// mark order as paid
// send confirmation email
// update database
}
res.status(200).send(”ok”);
});
That’s the basic idea.
Webhook vs API: what’s the difference?
This is where people often get confused.
An API is something you call when you want data or want to perform an action.
A webhook is something that calls you when an event happens.
Example with an API:
“Hey Stripe, give me the latest payment status.”
Example with a webhook:
“Hey app, Stripe here. This payment just succeeded.”
APIs are usually request-driven by you.
Webhooks are event-driven by someone else.
Both use HTTP. Both can send any type of data (usually it’s JSON). Both are part of how systems talk to each other.
The difference is who starts the conversation.
Real examples of webhooks
Webhooks show up everywhere.
Stripe uses webhooks to tell your app about payments, failed payments, subscriptions, refunds, disputes, and invoices.
GitHub uses webhooks to tell other systems when someone pushes code, opens a pull request, creates an issue, or merges something.
Slack can receive webhooks so another app can post messages into a channel.
Shopify uses webhooks to notify apps when orders are created, products are updated, or customers are changed.
Calendly can send a webhook when someone books a meeting.
So when you connect tools together and something “automatically happens,” there’s a good chance a webhook is involved.
Why webhooks are useful
The main benefit is that webhooks make systems react in real time.
Without webhooks, your app has to keep checking for updates.
With webhooks, your app can wait until something actually happens.
That means:
Less unnecessary work.
Faster updates.
Cleaner integrations.
For example, if someone pays for your product, you probably don’t want to wait 10 minutes until your app checks Stripe again. You want to act on that immediately.
That’s exactly the kind of thing webhooks are good for.
But webhooks can fail
This is the part beginners sometimes miss.
A webhook is just a request over the internet.
And requests can fail.
Your server may be down.
The request may time out.
Your code may throw an error.
The external service may retry the webhook later.
That’s why most serious webhook providers, like payment platforms, have retry logic. If your app doesn’t return a successful response, they’ll try again.
But this also means your webhook handler needs to be careful.
For example, imagine Stripe sends you a payment_succeeded webhook.
Your app processes it, gives the user access, and then crashes before sending a proper 200 OK response.
Stripe may think the webhook failed and send it again.
If your code isn’t careful, you might process the same payment twice.
That’s why webhook handling should usually be idempotent.
This simply means:
If the same webhook arrives twice, your app should not do the same dangerous thing twice.
So instead of blindly saying: Give users access to my product
you check: Did we already process this payment?
If yes, ignore it, if no, process it.
You should verify webhooks
Another important thing: don’t blindly trust webhook requests.
Your webhook URL is public. In theory, someone else could try to send fake requests to it.
So most serious services include a signature.
That signature lets you verify that the webhook really came from them.
The flow usually looks like this:
External service sends webhook data.
It includes a signature in the request headers.
Your app uses a secret key to verify the signature.
If the signature is valid, you process the webhook.
If not, you reject it.
This matters a lot for payments.
You don’t want someone sending your app fake JSON like:
{
“type”: “payment_succeeded”,
“amount”: 999999
}
and your app just believing it.
So yes, webhooks are simple.
But in production, you need to treat them seriously.
A simple mental model
Think of webhooks like push notifications for servers.
A mobile push notification says:
“You got a new message.”
A webhook says:
“A payment succeeded.”
“A pull request was opened.”
“A user signed up.”
“An order was created.”
It’s the same basic concept.
Something happens somewhere else, and your system gets notified.
When should you use a webhook?
Use a webhook when your app needs to react to events from another system.
For example:
A user paid.
A subscription was cancelled.
A build finished.
A form was submitted.
A file was uploaded.
A meeting was booked.
An issue was created.
A message was received.
Basically, any time you find yourself thinking:
“I need to know when something changes in that other app.”
That’s probably webhook territory.
Hope you found this article to be useful!

