Back to News & Insights
Web Development September 30, 2026 · 7 min read

What Is a Webhook? A Practical Guide (Plus a Real-World Bank Transfer Use Case)

Have you ever written something like this? js setInterval(async () => { const res = await...

What Is a Webhook? A Practical Guide (Plus a Real-World Bank Transfer Use Case)

Every 5 seconds you ask the server: "Anything new?" And 99% of the time the answer is "Nope." It wastes requests, burns resources, and is still slow.

What a webhook is and how it works Webhooks vs. polling Building a webhook endpoint in Node.js (with signature verification) Best practices for reliable, secure webhooks A very practical use case: getting near-instant bank balance notifications with PionPay What is a webhook?

A webhook is an HTTP callback. When an event happens in system A, system A proactively sends an HTTP request (usually a POST) to a URL you provided, carrying data about that event.

In short: instead of you asking over and over, the other system tells you when something happens.

Real-life analogy: You order something online. Polling = walking to the front door every 10 minutes to check if the courier has arrived. Webhook = leaving your phone number so the courier calls you when they're outside.

Webhooks are often called a "reverse API" or "push API", because the direction is flipped: instead of the client calling the server, the server calls the client. How do webhooks work?

A webhook flow has four steps: Register: You give the provider a public URL (an endpoint) to receive data. Event happens: Someone pushes code, a payment succeeds, money lands in an account... Deliver: The provider sends a POST request with a payload (usually JSON) to your URL. Acknowledge: Your server returns a 2xx to confirm receipt. If it doesn't, most providers will retry later.

You use webhooks every day, maybe without noticing: GitHub fires webhooks to trigger CI/CD on pushes and pull requests. Stripe fires webhooks when a payment succeeds or is refunded. Slack / Discord let you post messages into channels via incoming webhooks. Bank notification services fire webhooks when an account balance changes — more on this at the end. Polling vs. webhooks

Webhooks don't always fully replace polling. Many solid systems use both: webhooks for real-time events, plus a periodic reconciliation job (polling) to catch anything that slipped through. Building a webhook endpoint in Node.js

Let's build a webhook receiver with Express. It will do the three things every production endpoint needs: Verify the signature (make sure the request really came from the provider) Be idempotent (never process the same event twice) Respond fast, and do the heavy lifting later

Note: Header names, signing algorithms and payload shapes vary by provider. The code below uses the most common pattern: HMAC-SHA256 with an X-Signature header. For a real integration, follow your provider's docs.

Why express.raw() instead of express.json()? The HMAC signature is computed over the exact raw bytes of the body. If you let Express parse the JSON and then JSON.stringify it back, key order or whitespace may change and the signature will never match. This is one of the most common bugs in first-time webhook integrations.

What you should see: First request → OK, and the server logs Received event order.paid: { orderId: 'DH123', amount: 150000 } Send the exact same request again → Already processed (idempotency works) Change X-Signature to anything else → 401 Invalid signature Exposing localhost for real providers

Providers can't reach your localhost. During development you can use: ngrok: ngrok http 3000 gives you a public HTTPS URL that tunnels to your machine. Cloudflare Tunnel (cloudflared): similar, and free. webhook.site: just to inspect what a provider sends, before writing any code. Webhook best practices

Receiving a request is easy. Making it reliable in production is where it gets interesting.

Security Always use HTTPS for your webhook URL. Always verify signatures (HMAC, tokens, or whatever your provider supports). Your webhook URL is public — anyone can send fake requests to it. Compare signatures in constant time (crypto.timingSafeEqual), not with ===. If the provider publishes its IP ranges, consider IP allowlisting as an extra layer. For anything involving money: don't blindly trust the payload. Cross-check the amount and order ID against your own data before updating status. Reliability Return 2xx as fast as possible. Most providers time out after a few seconds. Push heavy work (calling other APIs, sending emails, multi-table writes) onto a queue (BullMQ, RabbitMQ, SQS...). Design for idempotency. Webhooks are usually delivered at least once — the same event can arrive 2–3 times. Store the event ID (or transaction ID) in your DB with a UNIQUE constraint. Don't rely on ordering. A later event may arrive first. Run a periodic reconciliation job to catch missed events (server downtime, bad deploys...).

Observability Log raw payloads (with sensitive data masked) for debugging. Alert when the webhook failure rate spikes. Real-world use case: bank balance notifications with PionPay

Now for the use case where webhooks really shine, especially in Vietnam: automatic bank transfer confirmation.

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation