What is a webhook payload?
A webhook payload is the data a sender puts in the body of a webhook request. When an event happens, the sender delivers an HTTP POST to your webhook endpoint, and the payload is the JSON (occasionally XML or form-encoded) body of that request describing what occurred. Your endpoint reads the payload to decide what to do.
What a webhook payload contains
A payload has two parts that matter: the body and the headers. The body carries the event data. The headers carry metadata the receiver needs before it trusts or processes that data, most importantly the signature and timestamp used to verify the request.
Most senders wrap the event data in a small envelope: a type field that says what happened, an identifier, a timestamp, and a nested object with the details. Here is a representative payload for a paid invoice:
{
"type": "invoice.paid",
"id": "evt_29w3b8c7d1",
"timestamp": "2026-07-22T14:03:11Z",
"data": {
"invoice_id": "in_1M2n3o",
"customer_id": "cus_9Xy",
"amount_paid": 4900,
"currency": "usd",
"status": "paid"
}
}
The type field is what your endpoint switches on to route the event. The id uniquely identifies this delivery and is what you store to deduplicate retries. Amounts are typically sent in the smallest currency unit (here, 4900 means $49.00), a convention worth checking in each provider's docs rather than assuming.
Payload headers
The headers travel alongside the body and tell your endpoint how to handle it. Providers following the Standard Webhooks specification send three:
| Header | Purpose |
|---|---|
webhook-id | Unique ID for the message, used to detect and drop duplicate deliveries. |
webhook-timestamp | When the message was sent, checked against a tolerance window to block replay attacks. |
webhook-signature | The signature over the timestamp and body, proving the payload is authentic and unmodified. |
Older providers use their own header names for the same ideas, such as GitHub's X-Hub-Signature-256 or Stripe's Stripe-Signature, but the roles are identical.
How to read a webhook payload safely
The order of operations matters. Verify the webhook signature against the raw request body first, before you parse it. Parsing to JSON and then re-serializing changes whitespace and key order, which produces a different byte sequence and breaks signature verification. So capture the raw bytes, verify, and only then deserialize.
Two more habits save you from common bugs. Treat the payload schema as append-only: providers add fields over time, so read the fields you need and ignore the rest rather than validating against an exact shape. And rely on the payload's own timestamp rather than arrival order, because retries and parallel delivery mean events can arrive out of order.
Thin payloads vs. full payloads
Senders take one of two approaches to how much they put in the body. A full payload includes the complete event data, so the receiver can act immediately without a follow-up request. A thin payload includes only an identifier and event type, and the receiver calls back to the sender's API to fetch the current state.
Thin payloads trade a round trip for two benefits: they keep sensitive data out of the delivered body, and they always reflect the latest state even if the webhook was delayed or delivered out of order. Full payloads are faster to process and don't depend on the sender's API being reachable. Many providers offer both and let you choose per event type.
Common webhook payload problems
Most payload bugs come from three habits. Parsing before verifying, which both wastes work on forged requests and, as above, can break verification if you verify against re-serialized JSON. Assuming the schema is frozen, which breaks the day a provider adds a field or nests an object differently. And letting payloads grow unbounded: large bodies slow down processing and push you toward webhook timeouts, which is why thin payloads exist. Acknowledge fast, then do the heavy work asynchronously.
For production-ready examples of parsing and verifying payloads, see our documentation on receiving webhooks.
Frequently asked questions
What format is a webhook payload usually in?
JSON is by far the most common format, because it is lightweight and every language can parse it. Some older or enterprise systems send XML or form-encoded bodies, but a new integration should expect JSON unless the provider documents otherwise.
Where is the webhook signature: in the payload or the headers?
In the headers, not the body. The signature is computed over the body (and usually a timestamp) and sent in a header such as webhook-signature, Stripe-Signature, or X-Hub-Signature-256. Your endpoint reads the header and recomputes the signature over the raw body to verify it.
Should I verify the payload before or after parsing it?
Before. Verify the signature against the raw request body first, then parse. If you parse to JSON and re-serialize before verifying, whitespace and key-order differences produce different bytes and verification fails, even for a legitimate request.
What is the difference between a thin and a full webhook payload?
A full payload contains the complete event data so you can act on it immediately. A thin payload contains only an ID and event type, and you call the sender back to fetch current state. Thin payloads keep sensitive data out of the body and always reflect the latest state; full payloads avoid the extra round trip.