Skip to main content Skip to main content

What Is a Webhook? A Plain-English Explanation

· 5 min read

A webhook is an HTTP POST request that an external service sends to your server when a specific event occurs. You do not ask for it. The service pushes it to you automatically, the moment the event happens.

That single distinction, push versus pull, is the core idea. With a regular API call, your code asks a remote server for data and waits for the response. With a webhook, the remote server calls your code when it has something to tell you. The direction of the request is reversed.

How webhooks work

The mechanics are the same as any HTTP request, just initiated by the other side. Here is the sequence:

  1. An event occurs on the external service: a payment clears, a user pushes code, an order ships.
  2. The service packages the event details into a JSON body and prepares an HTTP POST request.
  3. The service sends that request to a URL you registered with it in advance. That URL is your webhook endpoint.
  4. Your server receives the request, processes the data, and returns a 200 OK response to confirm delivery.

The 200 OK response is important. If your server does not respond quickly with a success status, the external service assumes delivery failed and will retry. Respond first, do the heavy processing after.

Webhook delivery cycle Four-step animated sequence diagram showing: Event Occurs, HTTP POST, Your Endpoint, 200 OK — each box appearing in turn with connecting arrows. Event Occurs Payment cleared, code pushed, order shipped HTTP POST JSON payload to your registered URL Your Endpoint Receives and validates the request 200 OK Confirm delivery to stop retries
The webhook delivery cycle: four steps from event to confirmation.

A real example: Stripe payment succeeded

Walk through what happens when a customer completes a Stripe checkout:

  1. Your application calls the Stripe API to create a checkout session and redirects the customer to Stripe's hosted payment page.
  2. The customer enters their card details and clicks Pay. Stripe processes the charge.
  3. Stripe's system fires a checkout.session.completed event and sends an HTTP POST request to the webhook URL you registered in your Stripe dashboard, for example https://yourapp.com/webhooks/stripe/.
  4. Your server receives the request, verifies the signature, reads the payload, and marks the order as paid in your database.
  5. Your server returns 200 OK. Stripe logs the delivery as successful.

Without that webhook, the only alternative is polling: asking Stripe every few seconds "did this customer pay yet?" That burns API rate limits and introduces latency. The webhook delivers the confirmation the instant it is ready.

What's inside a webhook payload

A webhook request is a standard HTTP POST with two parts: headers and a body.

The headers carry metadata. Most services include a signature header so you can verify the request is genuine. Stripe uses Stripe-Signature. GitHub uses X-Hub-Signature-256. There is usually a content-type header as well, almost always application/json.

The body is a JSON object describing what happened. Here is a simplified Stripe checkout.session.completed payload:

{
  "id": "evt_3OqK2LJZ2eZvKYlo1mHkVz5A",
  "type": "checkout.session.completed",
  "created": 1740835200,
  "data": {
    "object": {
      "id": "cs_test_a1b2c3d4e5f6",
      "object": "checkout.session",
      "amount_total": 4900,
      "currency": "usd",
      "customer_email": "[email protected]",
      "payment_status": "paid",
      "metadata": {
        "order_id": "8821"
      }
    }
  }
}

The type field tells you which event occurred. The data.object block contains the resource that changed. Your handler reads those fields and decides what to do: update the database, send a confirmation email, provision access.

Webhooks vs polling

Before webhooks became standard practice, the only way to know if something changed on a remote system was to keep asking. Poll the API every 30 seconds. Most of those requests return the same data you already have. You burn rate limits, add latency between the event and your response, and write code to diff results on every request.

Webhooks replace all of that with a single inbound request. The external service does the work of detecting the event and notifying you. You only handle traffic when something actually happened.

Polling Webhook
Who initiates Your application The external service
Latency Up to your poll interval Near-instant
Wasted requests Many (most return no change) None
Rate limit risk High if polling frequently None from your side
Complexity Simpler to write, wasteful to run Requires a public endpoint

The one genuine downside to webhooks: your server needs a public URL. During local development, localhost is not reachable from the internet, so you need a tunneling tool or a dedicated testing setup. Polling has no such requirement. For production systems, that tradeoff is almost always worth it.

Common webhook use cases

Webhooks show up everywhere real-time event notification matters:

  • Payments. Stripe, Braintree, and PayPal send webhooks for successful charges, failed payments, refunds, disputes, and subscription changes. Fulfillment and access provisioning happen here, not in the checkout flow.
  • CI/CD pipelines. GitHub, GitLab, and Bitbucket fire push and pull_request events that trigger build servers and deployment pipelines. Every modern CI system is built on top of these webhooks.
  • E-commerce order management. Shopify sends webhooks for new orders, fulfillments, cancellations, and inventory updates. Third-party logistics systems and inventory tools stay in sync this way.
  • CRM and support sync. HubSpot and other CRMs send webhooks when contacts are created or deals change stage. Your internal systems stay current without periodic data exports.
  • Communication events. Twilio and SendGrid send webhooks for incoming messages, delivery receipts, opens, and bounces. Your application reacts to those signals without polling messaging APIs.
  • Alerting and incident management. PagerDuty and Datadog send webhooks when incidents are triggered, acknowledged, or resolved, feeding data into your internal dashboards or on-call workflows.

Testing and debugging webhooks

Debugging a webhook integration is harder than debugging a regular API call. With an API, you control the request: you can rerun it in Postman, inspect the response, and iterate. With a webhook, the remote service controls the request. You are on the receiving end of data you did not ask for, at a time you did not choose.

The first challenge is local development. Services like Stripe cannot POST to localhost:8000. You need a tunneling tool or a way to capture and replay requests. The second challenge is visibility: if you did not log the raw payload, you often cannot reconstruct exactly what was sent when something went wrong.

Payloader is built specifically for this problem. You point a webhook at a Payloader endpoint and every inbound request is captured, timestamped, and shown as a human-readable summary. Instead of scanning through raw JSON to find the relevant field, you see exactly what the event was and what data it carried. You can also replay captured requests to your local machine once your handler is ready, which removes most of the friction from the development loop.

For a broader look at how to approach webhook testing, see our guide on how to test webhooks.

A few things to keep in mind

Three properties matter for every production webhook handler:

  • Verify signatures. Your endpoint is a public URL. Anyone can POST to it. Check the HMAC signature on every request before trusting the contents. Most services document their signing scheme clearly.
  • Respond fast. Return 200 OK immediately, before you do any real processing. If your handler times out, the service marks the delivery as failed and retries. Queue heavy work for the background.
  • Make handlers idempotent. Most services retry failed deliveries. The same event may arrive more than once. Processing a duplicate invoice.paid event should not charge the customer twice. Use the event ID to deduplicate.

Once those three things are in place, webhooks are one of the most efficient patterns for connecting services. The external system does the work of detecting and delivering events. Your code just needs to be ready to receive them.