# Process incoming webhooks with event notification handlers

> Event notification handlers are available in [public preview](https://docs.stripe.com/sdks/versioning.md#public-preview-release-channel).
> 
> This feature doesn’t support event destinations that use [EventBridge](https://docs.stripe.com/event-destinations/eventbridge.md) in public preview.

In each of our SDKs, we’ve created a specialized class that encapsulates the mechanics of parsing and validating a Stripe webhook. Event notification handlers take care of validating, parsing, and routing incoming webhooks to your business logic.

To use this feature, you must write a function for each event type you want to handle. After you register these functions on the handler, Stripe will call them when you receive the corresponding event notification.

## Before you begin

You must use one of the following SDK versions (or higher) to use event notification handlers.

| Language | Public Preview | Private Preview |
| --- | --- | --- |
| Python | `v14.2.0b1` | `v15.2.0a3` |
| Ruby | `v18.2.0-beta.1` | `v19.2.0-alpha.3` |
| PHP | `v19.2.0-beta.1` | `v20.2.0-alpha.4` |
| Go | `v84.2.0-beta.1` | `v85.2.0-alpha.3` |
| Node | `v20.2.0-beta.1` | `v22.2.0-alpha.4` |
| .NET | `v50.2.0-beta.1` | `v51.2.0-alpha.3` |
| Java | `v31.2.0-beta.1` | `v32.2.0-alpha.3` |

## Write a fallback callback

Write a function that runs whenever a dedicated callback hasn’t been registered for a specific event type. It will receive the `EventNotification`, plus a `StripeClient` and additional information about the event.

This function might log the fact that you received an unexpected event or throw an error to alert you to the unexpected state. You can also add business logic in this function if you’re handling events that your SDK doesn’t have types for.

#### Java

```java
public class EventNotificationWebhookEndpoint {
  private static void fallbackCallback(
      EventNotification notif, StripeClient client, UnhandledNotificationDetails details) {
    System.out.println("Received unhandled event notification type: " + notif.getType());
  }
}
```

As part of your migration, consider moving all of your [webhook endpoint](https://docs.stripe.com/webhooks.md) code into this function. Then, you can migrate individual event types to their own functions.

## Initialize your handler

In your [webhook endpoint](https://docs.stripe.com/webhooks.md), initialize an `EventNotificationHandler`, passing it your fallback callback. There’s a convenience method on `StripeClient` to simplify this step.

#### Java

```java
public class EventNotificationWebhookEndpoint {
  private static final StripeClient client = new StripeClient(API_KEY);
  private static final StripeEventNotificationHandler handler =
      client.notificationHandler(WEBHOOK_SECRET, EventNotificationWebhookEndpoint::fallbackCallback);
}
```

## Write & register a callback

Next, write a function responsible for handling a specific event type. It uses the [event types](https://docs.stripe.com/webhooks.md?snapshot-or-thin=thin) released with the `Clover` API version in September 2025.

Your callback will receive the event notification cast to the correct class. You’ll also get a `StripeClient`, bound to the [context](https://docs.stripe.com/context.md) of the notification, which makes it easy to make additional API calls without juggling account ids.

#### Java

```java
public class EventNotificationWebhookEndpoint {
  public static void main(String[] args) throws IOException {
    handler.onV1BillingMeterErrorReportTriggered(
        EventNotificationWebhookHandler::handleMeterErrors);
  }

  private static void handleMeterErrors(
      V1BillingMeterErrorReportTriggeredEventNotification notif, StripeClient client) {
    Meter meter = notif.fetchRelatedObject();
    System.out.println("Handling meter error for meter: " + meter.getDisplayName());
  }
}
```

You can register zero or more callbacks. If you don’t register any, all events will be routed to your fallback callback.

## Process events

Send incoming `POST` bodies into the handler. This replaces most of the original code in your webhook endpoint.

#### Java

```java
public class EventNotificationWebhookEndpoint {
  public static void main(String[] args) throws IOException {
    // ...

    HttpServer server = HttpServer.create(new InetSocketAddress(4242), 0);
    server.createContext("/webhook", new WebhookHandler());
    server.setExecutor(null);
    server.start();
  }

  static class WebhookHandler implements HttpHandler {
    @Override
    public void handle(HttpExchange exchange) throws IOException {
      if ("POST".equals(exchange.getRequestMethod())) {
        InputStream requestBody = exchange.getRequestBody();
        String webhookBody = new String(requestBody.readAllBytes(), StandardCharsets.UTF_8);
        String sigHeader = exchange.getRequestHeaders().getFirst("Stripe-Signature");

        try {
          handler.handle(webhookBody, sigHeader);

          exchange.sendResponseHeaders(200, -1);
        } catch (StripeException e) {
          exchange.sendResponseHeaders(400, -1);
        }
      } else {
        exchange.sendResponseHeaders(405, -1);
      }
      exchange.close();
    }
  }
}
```

## See also

- [Using thin events](https://docs.stripe.com/event-destinations.md#thin-events)
- [Receive Stripe events in your webhook endpoint](https://docs.stripe.com/webhooks.md)
