Format a Webhook Payload

Webhooks are HTTP POST requests that third-party services send to your server when events occur — a payment succeeds, a repository receives a push, a form is submitted. Your server has milliseconds to receive, validate, parse, and acknowledge the request before the sender marks it as a timeout. Understanding the payload structure before writing handler code is critical to getting this right the first time. This example shows a Stripe-style payment webhook, which is one of the most common webhook integrations developers implement. The outer envelope contains event-level metadata: a unique event id, the event type (payment_intent.succeeded), a created Unix timestamp indicating when the event occurred, and a livemode flag differentiating production from test mode. The actual resource that triggered the event lives inside data.object — in this case a PaymentIntent with its id, amount in the smallest currency unit (cents), currency, status, customer ID, and order metadata. Key architectural detail: the data.object contains the state of the resource at the time of the event. This means your webhook handler should use data.object rather than making a separate API call to fetch the resource, since the separate call might return a newer state that reflects subsequent events. Real-world scenarios for this example: a payment webhook handler that extracts the order_id from metadata to update order status in your database; a fraud detection system that checks the customer ID against a blocklist before fulfilling the order; a receipt email trigger that fires after reading the amount and currency from the payload. Tips and pitfalls: always verify the webhook signature before processing the payload. Stripe, GitHub, and most webhook providers include a signature in a request header (Stripe-Signature, X-Hub-Signature-256) that you must validate against your webhook secret. Process webhook handlers idempotently — the same event may be delivered more than once, so check whether you've already processed an event id before acting on it. To customise: replace the payload values with your provider's actual webhook schema. Each provider documents their event types and payload shapes in their developer documentation.

Example
{
  "id": "evt_1OqXample",
  "type": "payment_intent.succeeded",
  "created": 1700000000,
  "livemode": false,
  "data": {
    "object": {
      "id": "pi_3OqXample",
      "amount": 4999,
      "currency": "usd",
      "status": "succeeded",
      "customer": "cus_Xample123",
      "metadata": { "order_id": "ord_789" }
    }
  }
}
[ open in Webhook Payload Formatter → ]

FAQ

How do I test webhooks locally?
Use a tool like ngrok or Stripe CLI to forward webhooks to localhost. The webhook payload formatter helps you understand the structure before writing handler code.
Should I verify webhook signatures?
Yes, always. Most webhook providers include a signature header (e.g., Stripe-Signature) that you should verify using the shared secret before trusting the payload.
What is the difference between an event type and an event object?
The type field identifies what happened (e.g., payment_intent.succeeded). The data.object contains the full resource that triggered the event.

Related Examples