How to Set Up Stripe Webhooks

· 7 min read

Stripe webhooks notify your application when events happen in your Stripe account — successful payments, failed charges, subscription changes, disputes, and more. Here is how to set them up from scratch.

Step 1: Get a Webhook Endpoint

You need a publicly reachable URL that Stripe can send events to. For development and monitoring, the fastest approach is dread:

# Install dread
curl -sSL dread.sh/install | sh

# Create a channel
dread init

# You will get a URL like:
# https://dread.sh/wh/ch_abc123?source=stripe

For production, your webhook endpoint is a route in your application:

// Node.js / Express
app.post('/webhooks/stripe', express.raw({type: 'application/json'}), (req, res) => {
  const sig = req.headers['stripe-signature'];
  const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

  let event;
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    return res.status(400).send('Webhook signature verification failed');
  }

  switch (event.type) {
    case 'payment_intent.succeeded':
      console.log('Payment succeeded:', event.data.object.id);
      break;
    case 'payment_intent.payment_failed':
      console.log('Payment failed:', event.data.object.id);
      break;
    case 'customer.subscription.deleted':
      console.log('Subscription cancelled:', event.data.object.id);
      break;
  }

  res.status(200).json({received: true});
});

Step 2: Configure in the Stripe Dashboard

  1. Go to Developers → Webhooks in your Stripe Dashboard
  2. Click Add endpoint
  3. Paste your webhook URL
  4. Select the events you want to receive (start with the essentials):

Essential Events to Monitor

  • payment_intent.succeeded — a payment was completed
  • payment_intent.payment_failed — a payment attempt failed
  • charge.refunded — a refund was issued
  • customer.subscription.created — new subscription started
  • customer.subscription.updated — subscription plan changed
  • customer.subscription.deleted — subscription cancelled
  • invoice.payment_failed — recurring payment failed
  • charge.dispute.created — a chargeback was filed

Step 3: Verify Webhook Signatures

Always verify the Stripe-Signature header to confirm the event came from Stripe. This prevents attackers from sending fake events to your endpoint.

# Python
import stripe

@app.route('/webhooks/stripe', methods=['POST'])
def stripe_webhook():
    payload = request.data
    sig = request.headers.get('Stripe-Signature')

    try:
        event = stripe.Webhook.construct_event(
            payload, sig, endpoint_secret
        )
    except stripe.error.SignatureVerificationError:
        return 'Invalid signature', 400

    # Process the event
    handle_event(event)
    return '', 200

Never skip signature verification, even in development. It is the only way to confirm that Stripe — not an attacker — sent the event.

Step 4: Handle Retries and Idempotency

Stripe retries failed webhook deliveries for up to 3 days with exponential backoff. Your handler must be idempotent — processing the same event twice should not cause problems.

// Store processed event IDs to prevent duplicates
const processedEvents = new Set();

function handleEvent(event) {
  if (processedEvents.has(event.id)) {
    return; // Already processed
  }
  processedEvents.add(event.id);
  // ... process the event
}

In production, use a database instead of an in-memory set.

Step 5: Monitor in Real Time

Once your webhooks are configured, use dread to monitor them live. You will get desktop notifications for every event and can inspect payloads in the terminal UI:

# Watch for events with desktop notifications
dread watch

# Or open the full terminal UI
dread

# Forward to Slack for team visibility
dread config --slack https://hooks.slack.com/services/xxx

Common Stripe Webhook Errors

  • 400 — Signature verification failed: Check that you are using the correct webhook signing secret (not your API key)
  • Timeout: Stripe expects a response within 20 seconds. Move heavy processing to a background job
  • Duplicate events: Stripe may send the same event more than once. Always check idempotency

Monitor Stripe webhooks from your terminal

Get real-time payment notifications with one command.

curl -sSL dread.sh/install | sh