Generate a Java POJO from a Webhook Payload

Convert a generic webhook envelope (id, type, created, data) into a Java POJO. Learn how to model the polymorphic data field for different event types.

Real-world

Detailed Explanation

Webhook Payloads in Java

Most webhook providers (Stripe, GitHub, Slack, Twilio) wrap event data in an envelope: an event id, a timestamp, an event type, and a data field whose shape depends on the type. The pattern is consistent enough that one generated POJO can serve as a starting point.

Generic Webhook Envelope

{
  "id": "evt_1NXyz9abcdef",
  "type": "invoice.paid",
  "created": 1700000000,
  "api_version": "2024-04-10",
  "livemode": true,
  "data": {
    "object": {
      "id": "in_1NXabc",
      "amount_paid": 2500,
      "currency": "usd",
      "customer": "cus_NffrFeUfNV2Hib"
    }
  }
}

Generated POJOs

package com.example.webhooks;

import com.fasterxml.jackson.annotation.JsonProperty;

public class WebhookEvent {
    private String id;
    private String type;
    private Long created;

    @JsonProperty("api_version")
    private String apiVersion;

    private Boolean livemode;
    private Data data;
    // accessors
}

public class Data {
    private Object object; // shape depends on event type
    // accessors
}

Handling the Polymorphic data.object

The object field's shape changes with each event type. Three approaches:

1. Map<String, Object> (simplest)

private Map<String, Object> object;

Fast to set up; loses type safety.

2. JsonNode (Jackson tree model)

private JsonNode object;

Lets you query the tree with object.get("amount_paid").asInt() without committing to a fixed type.

3. Polymorphic deserialization (most rigorous)

Deserialize the envelope first, dispatch on type, then deserialize object into the correct concrete class:

WebhookEvent event = mapper.readValue(payload, WebhookEvent.class);
switch (event.getType()) {
    case "invoice.paid":
        Invoice invoice = mapper.convertValue(event.getData().getObject(), Invoice.class);
        // process invoice
        break;
    case "customer.created":
        Customer customer = mapper.convertValue(event.getData().getObject(), Customer.class);
        // process customer
        break;
}

See the Polymorphic Types example for the @JsonTypeInfo approach.

Verifying Webhook Signatures

Always verify the webhook signature before deserializing:

String signature = request.getHeader("Stripe-Signature");
String payload = request.getBody();

if (!verifySignature(payload, signature, webhookSecret)) {
    throw new SecurityException("Invalid webhook signature");
}

WebhookEvent event = mapper.readValue(payload, WebhookEvent.class);

Use the provider's official SDK helper for signature verification — never implement HMAC comparison from scratch.

Idempotency

Webhook providers retry failed deliveries, so the same event id may arrive multiple times. Persist the event id and skip duplicates:

if (eventLog.exists(event.getId())) return; // already processed
processEvent(event);
eventLog.record(event.getId());

Use Case

Payment, communication, CI/CD, and CRM webhooks all need a typed envelope class plus per-event-type processing logic. Generating the envelope POJO is the first step toward a robust webhook ingestion service.

Try It — JSON to Java

Open full tool