Friday, April 24, 2026

JSON payloads explained (and why webhooks use them)

Hooksbase
Fundamentals
Anatomy of a JSON webhook payload with envelope and data fields highlighted

A payload is the body of an HTTP request — the data the sender wants the receiver to act on. In webhooks, the payload is almost always JSON: a structured, machine-readable representation of the event.

This post explains what a payload is, why JSON beat the alternatives, what a typical webhook payload looks like, and what production systems do with payloads beyond just parsing them.

What is a payload?

In HTTP terms, a request has three parts:

  1. The method and URLPOST /webhooks/stripe
  2. The headersContent-Type, Authorization, signature headers
  3. The body (the payload) — the actual data

The payload is the body. For webhooks, that body almost always carries:

  • The event type (payment_intent.succeeded)
  • A unique event ID (evt_1NxV5y...)
  • A timestamp (so you can detect replay attacks)
  • The data describing what happened (the customer, the amount, the affected object)

Why JSON?

Webhooks could carry XML, protobuf, MessagePack, form-encoded data, or anything else. They mostly use JSON because:

  • It's human-readable. You can curl a webhook URL and read the result.
  • It's universally supported. Every language has a JSON parser in its standard library.
  • It's self-describing. Field names are part of the payload, so you can read it without a schema.
  • It's flexible enough to evolve. Adding a new field rarely breaks old consumers.

The trade-off is verbosity (JSON is bigger than binary formats) and weak typing (you have to validate). At webhook scale (events per second per tenant, not millions per second), the trade-off is a clear win.

What does a webhook payload look like?

A typical Stripe payment_intent.succeeded payload:

{
  "id": "evt_1NxV5y2eZvKYlo2C8wT9pQ4Z",
  "type": "payment_intent.succeeded",
  "created": 1714000000,
  "data": {
    "object": {
      "id": "pi_3NxV5y2eZvKYlo2C0pZ4uX2A",
      "amount": 2999,
      "currency": "usd",
      "customer": "cus_O8N3K5p4lJ2mNw",
      "status": "succeeded"
    }
  }
}

Most providers follow a similar structure: a top-level id, type, created, and a nested data (or object) with the actual event details.

A GitHub pull_request payload is bigger but follows the same pattern: a pull_request action, the PR object, the repository it lives in, the user who triggered it.

Common payload patterns

Across providers, you'll see four recurring patterns:

Event envelope. Many providers wrap events in a consistent envelope (id, type, created, data), though the exact shape is provider-specific.

Versioned schema. The provider sends a schema or api_version field so consumers know what shape to expect.

Sparse vs full. Some providers send the full state of the affected object on every event ("here's the full subscription"); others send only the changed fields ("here's what changed about the subscription"). Sparse payloads need an API lookup to act on; full payloads are bigger but self-sufficient.

References vs embedded objects. A payment event might embed the customer object inline, or just reference it by ID. The trade-off is payload size vs lookup latency.

What production systems do with payloads

Beyond parsing, production webhook systems often need to:

  • Transform the payload into the shape the agent or downstream service expects (rename fields, flatten nested objects, drop sensitive data).
  • Persist the original payload so you can replay it later — even after you change the transformation.
  • Filter based on payload contents (only deliver subscription.created events for tier=enterprise customers, for example).
  • Verify signatures on the raw bytes — if you parse before verifying, an attacker who can inject malformed JSON can bypass your check.

Hooksbase handles these relay-side concerns: Starter+ payload transforms via JSONPath or Handlebars, persisted dispatch snapshots so retained replays remain correct under config change, programmable routing rules over payload contents, and raw-byte signature verification for supported provider packs after Hooksbase ingest auth.

Where to go next

Keep reading