# Set up events

Receive, verify, and process Stripe events in your application.

This guide describes how to create an event handler that receives events, verifies each request, and processes events in your application.

[Code quickstart](https://docs.stripe.com/webhooks/quickstart.md)

If you’re not yet familiar with how Stripe events are structured or which format to use, read [How events work](https://docs.stripe.com/events/how-events-work.md) first.

## Choose your event format

Use thin events for new integrations. Thin events are unversioned, typed in the Stripe SDKs, and return the latest resource state when you fetch—so you don’t need to manage API version alignment on your event destination. Thin events are generated by API v1 and API v2 resources. See the [thin event catalog](https://docs.stripe.com/api/v2/core/events/event-types.md) for the full list of supported types.

Use snapshot events only if either of the following applies:

- A third-party tool or integration requires receiving the complete `Event` object payload.
- You need the previous values of changed attributes on a resource without making an additional API call.

Snapshot events send the complete `Event` object, including a point-in-time snapshot of the related resource. They include a `previous_attributes` property showing which fields changed. They’re versioned by API version, which requires you to keep your endpoint API version aligned with your SDK version.

For a detailed comparison, see [How events work](https://docs.stripe.com/events/how-events-work.md#choosing-event-format).

## Create an event handler

Add an endpoint to your server that accepts `POST` requests from Stripe. The primary example uses thin events, which we recommend for new integrations. The code differs depending on which event format you use.

#### Thin events

Use `parseEventNotification()` to validate the signature and return a typed event notification. Call `fetchRelatedObject()` to get the latest resource state, or `fetchEvent()` when you need additional event data like changed fields.

#### Node.js

```javascript

// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
// Find your keys at https://dashboard.stripe.com/apikeys.
const stripe = require('stripe')('<<YOUR_SECRET_KEY>>');

// This example uses Express to receive webhooks
const express = require('express');
const app = express();
const endpointSecret = 'whsec_...';

app.post('/webhook', express.raw({type: 'application/json'}), async (request, response) => {
  const sig = request.headers['stripe-signature'];
  let eventNotification;

  try {
    eventNotification = client.parseEventNotification(request.body, sig, endpointSecret);
  } catch (err) {
    response.status(400).send(`Webhook Error: ${err.message}`);
    return;
  }

  // Handle the event notification
  switch (eventNotification.type) {
    case 'v1.billing.meter.error_report_triggered':
      // fetchRelatedObject() retrieves the latest version of the related resource
      const meter = await eventNotification.fetchRelatedObject();
      console.log('Meter error for:', meter.display_name);
      break;
    default:
      console.log(`Unhandled event type: ${eventNotification.type}`);
  }

  response.json({received: true});
});

app.listen(4242, () => console.log('Running on port 4242'));
```

#### Snapshot events (exception cases)

Use `constructEvent()` to verify the webhook signature, then check the `type` field to determine which event occurred. Each event’s `data.object` contains a snapshot of the affected resource as it was when the event was generated. Because this snapshot can be stale, fetch the latest resource from the API before taking any critical action.

#### Node.js

```javascript

// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
// Find your keys at https://dashboard.stripe.com/apikeys.
const stripe = require('stripe')('<<YOUR_SECRET_KEY>>');

// This example uses Express to receive webhooks
const express = require('express');
const app = express();
const endpointSecret = 'whsec_...';

app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => {
  const sig = request.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
  } catch (err) {
    response.status(400).send(`Webhook Error: ${err.message}`);
    return;
  }

  // Handle the event
  switch (event.type) {
    case 'payment_intent.succeeded':
      const paymentIntent = event.data.object;
      console.log('PaymentIntent was successful!');
      break;
    case 'payment_method.attached':
      const paymentMethod = event.data.object;
      console.log('PaymentMethod was attached to a Customer!');
      break;
    // ... handle other event types
    default:
      console.log(`Unhandled event type ${event.type}`);
  }

  // Return a 200 response to acknowledge receipt of the event
  response.json({received: true});
});

app.listen(4242, () => console.log('Running on port 4242'));
```

Start your server after adding the new endpoint.

## Install and set up the Stripe CLI

Install the Stripe CLI with [npm](https://www.npmjs.com/):

```bash
npm install -g @stripe/cli@latest
```

After installation, log in to your Stripe account:

```bash
stripe login
```

After you install the CLI, you can also install [agent tooling](https://docs.stripe.com/cli/agent/setup), or set up [autocompletion](https://docs.stripe.com/cli/completion).

> For more installation options for Windows, macOS, Linux, and Docker, see the [Stripe CLI readme](https://github.com/stripe/stripe-cli#installation) on GitHub.

After you have the Stripe CLI installed, run `stripe login` in the command line to generate a pairing code and link to your Stripe account. Press **Enter** to launch your browser and log in.

```bash
stripe login
Your pairing code is: humour-nifty-finer-magic
Press Enter to open up the browser (^C to quit)
```

The generated API key is valid for 90 days. You can modify or delete it under [API Keys](https://dashboard.stripe.com/apikeys) in the Dashboard.

> You can create a project-specific configuration by including the [–project-name](https://docs.stripe.com/cli/login#login-project-name) flag when you log in and when you run commands for that project.

## Test your webhook locally

Use the CLI to forward events to your local webhook endpoint.

Assuming your application is running on port 4242:

```bash
stripe listen --forward-to http://localhost:4242/webhook
```

In a separate terminal tab, trigger a mock event:

```bash
stripe trigger v1.billing.meter.error_report_triggered
```

The following output appears in your `listen` tab:

```bash
[200 POST] OK v1.billing.meter.error_report_triggered
```

Your server logs `Meter error for:` followed by the meter’s display name in the terminal where it’s running.

If you’re following the snapshot events example instead, trigger `payment_intent.succeeded` and your server logs `PaymentIntent was successful!`.

## Optional: Check webhook signatures

Stripe includes a signature in each event’s `Stripe-Signature` header so you can verify the request came from Stripe. For thin events, `parseEventNotification()` handles signature verification automatically. For snapshot events, use `constructEvent()` with three parameters:

- `requestBody`: The raw request body string from Stripe.
- `signature`: The `Stripe-Signature` header value.
- `endpointSecret`: The secret associated with your endpoint.

First, find your webhook endpoint secret. If you’re testing locally with the Stripe CLI, retrieve it from the CLI output when you run `stripe listen`. If you’re using a Dashboard-managed endpoint, open the endpoint in [Workbench](https://dashboard.stripe.com/workbench/webhooks) and click **Reveal secret**. The secret starts with `whsec_`.

#### Ruby

```ruby

# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
# Find your keys at https://dashboard.stripe.com/apikeys.
client = Stripe::StripeClient.new('<<YOUR_SECRET_KEY>>')

require 'stripe'
require 'sinatra'

# If you are testing your webhook locally with the Stripe CLI you
# can find the endpoint's secret by running `stripe listen`
# Otherwise, find your endpoint's secret in your webhook settings in
# the Developer Dashboard
endpoint_secret = 'whsec_...'

# Using the Sinatra framework
set :port, 4242

post '/my/webhook/url' do
  payload = request.body.read
  sig_header = request.env['HTTP_STRIPE_SIGNATURE']
  event = nil

  begin
    event = Stripe::Webhook.construct_event(
      payload, sig_header, endpoint_secret
    )
  rescue JSON::ParserError => e
    # Invalid payload
    puts "Error parsing payload: #{e.message}"
    status 400
    return
  rescue Stripe::SignatureVerificationError => e
    # Invalid signature
    puts "Error verifying webhook signature: #{e.message}"
    status 400
    return
  end

  # Handle the event
  case event.type
  when 'payment_intent.succeeded'
    payment_intent = event.data.object # contains a Stripe::PaymentIntent
    puts 'PaymentIntent was successful!'
  when 'payment_method.attached'
    payment_method = event.data.object # contains a Stripe::PaymentMethod
    puts 'PaymentMethod was attached to a Customer!'
  # ... handle other event types
  else
    puts "Unhandled event type: #{event.type}"
  end

  status 200
end
```

In addition to signature verification, configure your server or firewall to only accept webhook requests from Stripe’s [IP addresses](https://docs.stripe.com/ips.md).

For help with signature errors, see [Manage webhook endpoints](https://docs.stripe.com/events/manage-webhook-endpoints.md#signature-errors).

## Deploy your event handler

When you’re ready to move to production:

1. Create a [live-mode](https://docs.stripe.com/keys.md#test-live-modes) [restricted API key](https://docs.stripe.com/keys.md#create-restricted-api-key) with only the permissions your handler needs, such as read access to the resources you retrieve with `fetchRelatedObject()` or `fetchEvent()`.
2. Open [Workbench](https://dashboard.stripe.com/workbench) and go to the **Webhooks** tab.
3. Click **Add destination**.
4. Enter the specific event types you want to receive. For snapshot destinations, also select the API version. Click **Continue**.
5. Select **Webhook endpoint** from the list of destination types. Click **Continue**.
6. Enter the URL of your endpoint along with an optional name and description. Click **Create destination**.
7. Copy the endpoint secret shown in the destination details view and use it in place of the `whsec_...` placeholder.

Your event handler runs on your server, so it requires a restricted or secret key. A publishable key can’t authorize the API calls that `fetchRelatedObject()` and `fetchEvent()` make.

Don’t embed the API key or the endpoint secret in source code or client-side applications. Instead, use your server platform’s secrets vault to provide them to your application. If your platform doesn’t offer a secrets vault, set them in environment variables. For more information, see [API key best practices](https://docs.stripe.com/keys-best-practices.md).

Your application is now ready to accept live events. For more information on configuring a webhook endpoint, see the [Webhook Endpoint API](https://docs.stripe.com/api/webhook_endpoints.md).
