Build the mental model
Every API call you've made so far follows the same pattern: your app reaches out and asks a server for something, and the server replies. Your app is always the client, always in control of when the conversation starts. A webhook flips that relationship around.
- Payment processors (e.g. a charge completes)
- Chat platforms (e.g. a message is posted)
- CI/CD pipelines (e.g. a build finishes)
- Version control hosts (e.g. code is pushed to a repository)
With a webhook, one of these external services sends an HTTP POST request to a URL belonging to YOUR server the moment something happens on their end. You are no longer asking a question — you are receiving a request. Your server must listen, parse the body, and decide what to do, exactly the role a normal API server plays for you.
This is why webhooks feel unfamiliar at first: the prerequisite course assumed your code sends requests and reads responses. Now your code accepts requests and sends responses back to acknowledge receipt. To receive webhooks at all, your server needs a publicly reachable URL, registered with the external service ahead of time.
- Webhook
- An HTTP callback: a URL you register with an external service so that service can send your server an HTTP POST request the moment a specific event happens, instead of your server having to ask for updates.
NORMAL API CALL VS WEBHOOK
--------------------------
-----
Normal API (pull):
Your App --- asks for data ---> API Server
Your App <-------- response ---- API Server
Webhook (push):
External Service: event happens
External Service --- POST request ---> Your Server
Your Server --- 200 OK ack ---> External Service
Note: Your server now RECEIVES a request, it does not send one.Connect it to a real scenario
You won't write a full production webhook receiver in this lesson — that needs a real server framework — but you can practice the exact logic your server would run once a request arrives.
The code below models one incoming webhook request as a plain object: an HTTP method, a URL path, and a JSON string body. The function checks that the method is POST, parses the body, and reports what it found.
Run it and notice the output describes the request from your server's point of view: something was received, not sent. That reversal is the whole lesson.
Try the working example
function receiveWebhookRequest(request) {
if (request.method !== "POST") {
return { accepted: false, reason: `Expected POST, got ${request.method}` };
}
const event = JSON.parse(request.body);
return {
accepted: true,
route: request.url,
eventType: event.type,
summary: `Received '${event.type}' event pushed FROM the external service TO our server`
};
}
const incomingRequest = {
method: "POST",
url: "/webhooks/payments",
body: JSON.stringify({ type: "payment.completed", id: "evt_001" })
};
console.log(receiveWebhookRequest(incomingRequest));{
accepted: true,
route: '/webhooks/payments',
eventType: 'payment.completed',
summary: "Received 'payment.completed' event pushed FROM the external service TO our server"
}5-minute try-it
Modify receiveWebhookRequest so it also rejects requests with a missing or empty body, returning a clear reason string instead of throwing when JSON.parse fails.
One important caution
Forgetting to return a fast success response (like 200 OK) can make providers think delivery failed and retry the same event unnecessarily.
Assuming your webhook URL is private just because you never published it — it is still reachable by anyone who finds or guesses it.
Webhook — Wikipedia — API Integration & Webhooks