All posts
webhooksapi

Webhook Handling: A Practical Guide for Full-Stack Developers

A practical guide to handling webhooks correctly — signature verification, idempotency, retries, and the patterns every integration needs.

SR

Suhail Roushan

August 6, 2026

·
5 min read
·
0 views

Nearly every payment, email, and SaaS integration eventually comes down to the same underlying problem: reliably handling an HTTP POST from someone else's server that might arrive late, out of order, or more than once — and most webhook bugs come from not designing for exactly that.

A webhook is a server-to-server callback: an external service sends an HTTP request to your endpoint when an event happens (a payment succeeds, a form is submitted, a build completes) instead of you polling for changes. The pattern is simple in concept, but correct implementation requires signature verification, idempotency handling, and tolerance for retries and out-of-order delivery.

Why Webhook Handling Matters (and When to Skip It)

Polling an API repeatedly to check for changes is wasteful and introduces latency between an event happening and your system reacting to it. Webhooks push events to you as they happen, which is both more efficient and more responsive — but that responsiveness comes with the responsibility of handling an endpoint that receives unauthenticated-by-default HTTP requests from the internet correctly.

Skip building custom webhook handling if a workflow automation tool (like a no-code integration platform) already covers the specific event-to-action mapping you need without custom logic — not every webhook needs a hand-rolled endpoint.

Getting Started with Webhook Handling

A baseline webhook handler with signature verification and idempotency:

import crypto from "crypto";

app.post("/webhooks/provider", express.raw({ type: "application/json" }), async (req, res) => {
  const signature = req.headers["x-provider-signature"] as string;
  const expected = crypto
    .createHmac("sha256", process.env.WEBHOOK_SECRET!)
    .update(req.body)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(400).send("Invalid signature");
  }

  const event = JSON.parse(req.body);

  const alreadyProcessed = await db.processedEvents.exists(event.id);
  if (alreadyProcessed) return res.status(200).json({ received: true });

  await db.processedEvents.create({ id: event.id });
  await handleEvent(event);

  res.status(200).json({ received: true });
});

Core Webhook Handling Concepts Every Developer Should Know

Signature verification is the difference between a webhook endpoint and an open door. Without verifying the sender-provided signature against a shared secret, anyone who discovers your webhook URL can send fake events — this is the single most important thing to get right in any webhook handler.

Idempotency is required because most providers guarantee at-least-once delivery, not exactly-once. A webhook can be delivered more than once for the same event (retries after a timeout, network issues on the provider's end) — your handler needs to detect and skip already-processed events rather than assuming single delivery.

Respond quickly and process asynchronously for anything non-trivial. Most providers expect a response within a few seconds and will retry if they don't get one — if your event handling involves slow work (calling other APIs, heavy processing), acknowledge receipt immediately and process the event in a background job rather than blocking the response.

app.post("/webhooks/provider", async (req, res) => {
  res.status(200).json({ received: true }); // ack immediately
  await queue.enqueue("process-webhook-event", event); // process async
});

Use the raw request body for signature verification, not the parsed body. Signature schemes typically hash the exact raw bytes received — if middleware has already parsed the body to JSON before verification runs, the signature check will fail or, worse, be silently bypassed if implemented incorrectly.

Common Webhook Handling Mistakes and How to Fix Them

Mistake 1: no signature verification at all, trusting any request that hits the endpoint. This is directly exploitable. Fix: always verify signatures using the provider's documented scheme before processing any event data.

Mistake 2: not handling duplicate deliveries, causing double-processing (double-charging, duplicate emails, duplicate database writes). Fix: track processed event IDs and skip duplicates, treating your handler as idempotent by design.

Mistake 3: slow synchronous processing causing timeouts and unnecessary retries. If your handler takes too long, the provider may retry, compounding load and potentially causing more duplicate processing. Fix: acknowledge receipt fast and do heavy processing asynchronously.

When Should You Use Webhooks Instead of Polling?

Use webhooks whenever a provider offers them and near-real-time reaction to events matters — they're strictly more efficient than polling for anything beyond very infrequent checks. Use polling only when webhooks aren't available, when your infrastructure can't reliably expose a public endpoint (some internal/restricted network setups), or when the event frequency is low enough that polling overhead is genuinely negligible.

Webhook Handling in Production

Log every received webhook event (even ones that fail verification) to make debugging delivery issues possible after the fact — a webhook that silently fails for one specific customer is much harder to diagnose without a record of what was actually received. Also monitor your webhook endpoint's error rate and response time as you would any other production endpoint, since a slow or failing webhook handler affects the provider's view of your integration's reliability too.

Before considering any webhook integration production-ready, confirm signature verification, idempotency handling, and fast acknowledgment are all in place — those three things are what separate a correct webhook handler from a fragile one.

Related posts

Written by Suhail Roushan — Full-stack developer. More posts on AI, Next.js, and building products at suhailroushan.com/blog.

Get in touch