openwebhook.coOpen inspector →
developer guide

What Is a Webhook? A Practical Guide for Developers

Understand how webhooks work, how they differ from polling, and how to receive and verify webhook requests with practical HTTP examples.

A webhook is an HTTP request sent by one application when an event occurs. Instead of repeatedly asking an API whether something changed, your application exposes an endpoint and waits for the provider to call it.

If a payment succeeds, a repository receives a push, or a form is submitted, the provider serializes information about that event and sends it to the URL you configured.

The basic webhook flow

A typical webhook integration has four steps:

  1. Your application gives the provider an HTTPS endpoint.
  2. An event occurs inside the provider's system.
  3. The provider sends an HTTP request—usually a POST containing JSON.
  4. Your endpoint validates the request, returns a successful status, and processes the event.

Here is a small webhook request:

POST /webhooks/payments HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-Webhook-Signature: t=1788793200,v1=...

{
  "id": "evt_123",
  "type": "payment.succeeded",
  "data": {
    "amount": 4900,
    "currency": "usd"
  }
}

The endpoint should normally acknowledge the request quickly. Expensive work—sending email, updating analytics, or generating documents—belongs in a background job. Providers often treat a slow response as a failure and retry it.

Webhooks versus polling

Polling asks an API for updates on a schedule:

setInterval(async () => {
  const latestEvents = await fetch("/api/events?since=...");
  await processEvents(await latestEvents.json());
}, 30_000);

This is simple, but most requests return no changes. It also introduces a delay equal to the polling interval.

Webhooks reverse the direction. The provider sends data as soon as it is available. This reduces unnecessary API traffic and makes event-driven workflows feel immediate. Polling can still be useful as a reconciliation mechanism in case a webhook is permanently missed.

What belongs in a webhook handler

A production handler should perform a small, predictable sequence:

export async function webhookHandler(request, response) {
  const rawBody = await readRawBody(request);
  verifySignature(rawBody, request.headers["x-webhook-signature"]);

  const event = JSON.parse(rawBody);
  await eventQueue.add(event);

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

Important details:

  • Verify authenticity. Use the provider's signature scheme and the raw request body. Do not trust a request only because it reached a secret-looking URL.
  • Return quickly. Queue work and acknowledge the request before the provider's timeout.
  • Make processing idempotent. Store the provider's event ID so a retry does not charge a customer or send an email twice.
  • Expect retries and disorder. Events may arrive more than once or in a different order.
  • Log safely. Payloads can contain personal data, tokens, or customer information.

How to inspect an unknown webhook

During development, your local endpoint may not be public or you may not yet know the provider's exact payload. A temporary webhook inspector gives you a public URL and displays the incoming method, headers, query parameters, and body.

With OpenWebhook, open the free webhook inspector, copy the generated URL, and paste it into the provider's webhook settings. No signup or installation is required.

You can also send a request yourself:

curl -X POST \
  'https://openwebhook.co/YOUR_UUID/example' \
  -H 'content-type: application/json' \
  -H 'x-example-event: account.updated' \
  --data '{"id":"acct_123","active":true}'

Keep the inspector tab open while testing. OpenWebhook relays requests only to connected browsers and stores the visible history locally in that browser.

A webhook is a delivery mechanism, not a queue

Receiving a 200 response proves that the destination accepted the HTTP request. It does not guarantee that every downstream action completed. Reliable systems separate delivery from processing by placing accepted events on a durable queue and tracking failures independently.

That distinction helps when debugging: first confirm the provider sent the expected HTTP request, then confirm your application verified, queued, and processed the event.