Author picture Gareth Wilson

Guide to Stripe Webhooks: Features and Best Practices

Published


Stripe webhooks are HTTP callbacks that deliver real-time notifications about events occurring in your Stripe account. When a customer completes a payment, disputes a charge, updates their subscription, or when a recurring billing cycle completes, Stripe sends an HTTP POST request to your configured endpoint with detailed event data as a JSON payload.

This guide covers Stripe webhook features, configuration options, security considerations, common limitations, and best practices for building reliable integrations.

How Stripe webhooks work

Webhooks provide an event-driven architecture for your Stripe integration. Instead of continuously polling the Stripe API to check for changes, webhooks push notifications directly to your server the moment events occur. This approach is more efficient, reduces API calls, and ensures your application responds to critical events in near real-time.

When you register a webhook endpoint with Stripe, they send HTTP POST requests containing an Event object whenever relevant activity happens in your account. These events range from successful payments and failed charges to subscription updates and dispute notifications.

Stripe webhook features

FeatureDetails
Webhook configurationStripe Dashboard or API
Verification methodHMAC signature in Stripe-Signature header
TimeoutA few seconds (exact value not publicly documented)
Retry logicExponential backoff up to 3 days (live mode)
Auto-disableAfter 3 days of continuous failures
Manual retryAvailable via Dashboard for recent events
Endpoint limit16 webhook endpoints per account
DestinationsHTTP endpoints, Amazon EventBridge

Comprehensive event types

Stripe provides an extensive catalog of webhook event types covering virtually every aspect of their platform.

Payment and charge events:

  • charge.captured — Previously uncaptured charge is captured
  • charge.failed — Failed charge attempt occurs
  • charge.pending — Pending charge is created
  • charge.refund.updated — Refund is updated on selected payment methods
  • charge.dispute.created — Customer disputes a charge with their bank
  • charge.dispute.funds_reinstated — Funds reinstated after dispute closure
  • charge.dispute.funds_withdrawn — Funds removed due to a dispute

Invoice events:

  • invoice.created — New invoice is created
  • invoice.finalized — Draft invoice becomes open
  • invoice.paid — Invoice payment succeeds or marked paid out-of-band
  • invoice.payment_failed — Automatic payment collection fails
  • invoice.payment_action_required — Payment requires further user action
  • invoice.upcoming — Notification before scheduled invoice creation
  • invoice.voided — Invoice is voided
  • invoice.overdue — Invoice becomes overdue (timing configurable via Automations)

Subscription events:

  • customer.subscription.created — New subscription is created
  • customer.subscription.updated — Subscription changes (plan switch, status change)
  • customer.subscription.deleted — Subscription is canceled
  • customer.subscription.paused — Subscription enters paused status
  • customer.subscription.resumed — Paused subscription resumes
  • customer.subscription.trial_will_end — Three days before trial ends
  • customer.subscription.pending_update_applied — Pending update is applied
  • customer.subscription.pending_update_expired — Pending update expires

You can also subscribe to all events using ['*'] when creating an endpoint, though this is generally not recommended for production systems.

Flexible endpoint configuration

Stripe allows you to register up to 16 webhook endpoints per account. You have flexibility in how you structure your webhook infrastructure:

  • Create one endpoint to handle multiple event types
  • Set up individual endpoints for specific events
  • Use separate endpoints for test mode and live mode
  • Configure different API versions per endpoint

When creating an endpoint in the Dashboard, you can choose between your account's current API version or the latest API version for the event payload format.

Multiple destination types

Beyond traditional webhook endpoints, Stripe supports alternative event destinations including Amazon EventBridge. This allows you to route events directly to AWS services without managing your own HTTP endpoint infrastructure.

Retry logic with exponential backoff

When webhook delivery fails, Stripe automatically retries using an exponential backoff schedule:

Live mode:

  • Immediate retry after initial failure
  • 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours
  • Then every 12 hours
  • Continues for up to 3 days total

Test/sandbox mode:

  • Three retry attempts over a few hours

After 3 days of continuous failures in live mode, Stripe disables your endpoint and sends you a notification.

Development and testing tools

Stripe CLI: Test your webhook endpoint handler locally using the Stripe CLI. This tool forwards webhook events to your local development server, eliminating the need to deploy your code to a public server during development.

Built-in fixtures: The CLI includes a trigger command that runs API calls against Stripe to generate specific events. This provides a streamlined approach to testing without manually creating test data.

Dashboard event viewer: View all delivery attempts in the Stripe Dashboard (Developers → Webhooks → Events) and manually retry specific events when needed.

Security considerations

Verifying webhook signatures

Every Stripe webhook includes a Stripe-Signature header containing an HMAC signature. This cryptographic signature allows you to verify that requests genuinely originated from Stripe and haven't been tampered with during transmission. Stripe provides official SDKs in multiple languages that handle signature verification for you.

// Node.js example using Stripe SDK
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

const sig = request.headers['stripe-signature'];
let event;

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

Important: Stripe requires the raw request body for signature verification. Many web frameworks automatically parse JSON bodies, which can break verification. Ensure you capture the raw body before any parsing occurs.

Store webhook secrets securely

Never hardcode webhook signing secrets in source code. Use environment variables and keep secrets out of version control. Use the webhook signing secret from the specific endpoint in the Stripe Dashboard, not your API key.

Stripe webhook limitations and pain points

Understanding Stripe webhook limitations helps you design more robust integrations. This section covers the most significant pain points developers encounter, along with workarounds and how dedicated webhook infrastructure can help.

Duplicate event delivery

The Problem: Webhook endpoints occasionally receive the same event more than once, leading to duplicate processing of payments, double-sending of emails, or corrupted data.

Why It Happens: Network timeouts, retries triggered by slow responses, or infrastructure issues can cause Stripe to resend events. Your endpoint might have processed the event but returned a non-2xx status code due to unrelated errors.

Workarounds:

  • Implement idempotency by tracking processed event IDs in your database
  • Use database transactions with unique constraints on event IDs
  • Design all handlers to be idempotent (same result regardless of how many times executed)

How Hookdeck Can Help: Hookdeck automatically deduplicates events before they reach your endpoint. By filtering duplicate deliveries at the infrastructure level, you reduce the burden on your application logic and eliminate the risk of missed edge cases in your deduplication code.

Out-of-order event delivery

The Problem: Events don't arrive in chronological order, causing race conditions. For example, a customer.subscription.deleted event might arrive before customer.subscription.created, breaking your application state.

Why It Happens: Stripe sends multiple events simultaneously, and network conditions can cause later events to arrive before earlier ones. This is by design—Stripe explicitly does not guarantee delivery order.

Workarounds:

  • Use timestamp fields within event data to determine actual chronology
  • Implement optimistic locking patterns in your database
  • Fetch current state from Stripe API when processing ambiguous events
  • Design state machines that can handle events in any order

How Hookdeck Can Help: Hookdeck provides event ordering capabilities that can ensure events are delivered to your endpoint in the correct sequence. You can configure ordering rules based on event properties to guarantee chronological processing.

Timeout and response time requirements

The Problem: Complex webhook handlers timeout before completing, causing failed deliveries and retry storms. Stripe requires quick 2xx responses, but real-world processing often involves database operations, external API calls, and business logic.

Why It Happens: Stripe sets timeout limits on webhook responses. If your endpoint doesn't respond quickly enough (typically within a few seconds), the delivery is marked as failed and queued for retry.

Workarounds:

  • Return 200 immediately, then process asynchronously
  • Use message queues (SQS, RabbitMQ, Redis) to decouple receipt from processing
  • Implement background job systems (Sidekiq, Celery, Bull)

How Hookdeck Can Help: Hookdeck responds to Stripe under 200ms, capturing the event immediately. It then queues events and delivers them to your destination at a rate your system can handle, with built-in retry logic. This eliminates timeout concerns entirely, so your handlers can take as long as needed without affecting webhook delivery success.

Downtime recovery and missed events

The Problem: When your webhook endpoint is down, events accumulate at Stripe. After 3 days of failures, your endpoint is disabled.

Why It Happens: Stripe retries failed webhooks for up to 3 days, but if your infrastructure is down for longer, events stop being sent. Even after recovery, there's no easy way to request all missed events.

Workarounds:

  • Implement high-availability infrastructure for webhook endpoints
  • Use periodic reconciliation jobs to fetch events from Stripe's Events API
  • Monitor webhook health actively and respond to issues quickly
  • Consider multi-region failover for critical endpoints

How Hookdeck Can Help: Hookdeck reliably captures all incoming webhooks even when your services are down. Events are stored and can be easily replayed when your systems recover. Failed events can be retried automatically, manually, or in bulk from the dashboard or API.

Scaling under high volume

The Problem: Traffic spikes from high-volume events (like a flash sale or end-of-month billing run) overwhelm your webhook processing infrastructure, causing cascading failures.

Why It Happens: Webhooks arrive unpredictably and can spike dramatically. If Stripe is processing thousands of subscription renewals simultaneously, your endpoint receives thousands of webhook requests at once.

Workarounds:

  • Auto-scale webhook processing infrastructure
  • Implement rate limiting and backpressure mechanisms
  • Use queues to buffer events during spikes
  • Design handlers to be stateless and horizontally scalable

How Hookdeck Can Help: Hookdeck acts as a buffer between Stripe and your application, automatically absorbing traffic spikes. The queue serves webhooks to your consumers at a rate they can handle, preventing overload and crashes while ensuring no events are lost.

API rate limits during processing

The Problem: Webhook handlers often need to call Stripe's API for additional data, but high webhook volumes combined with API calls can exceed Stripe's rate limits (100 requests/second in live mode, 25 in test mode).

Why It Happens: The "fetch-before-process" pattern, where you retrieve the latest object state from Stripe before processing, multiplies API calls. Each webhook triggers one or more API requests, and under high volume, you quickly hit rate limits.

Workarounds:

  • Cache Stripe object data locally and update from webhooks
  • Use the data provided in the webhook payload when possible instead of fetching
  • Implement request queuing with rate limiting
  • Batch API calls where possible

How Hookdeck Can Help: Hookdeck's queueing and rate limiting features let you control the pace of webhook delivery, preventing your handlers from overwhelming Stripe's API. You can configure delivery rates that stay comfortably below rate limits.

Limited endpoint count

The Problem: Stripe allows only 16 webhook endpoints per account, which can be restrictive for complex architectures with multiple services, environments, or microservices that each need their own webhook handling.

Why It Happens: This is a deliberate Stripe platform limitation, likely for performance and abuse prevention.

Workarounds:

  • Consolidate endpoints and route events to different services internally
  • Build a webhook routing layer that fans out events
  • Use different Stripe accounts for truly separate concerns

How Hookdeck Can Help: By pointing a single Stripe webhook endpoint to Hookdeck, you can fan out events to unlimited destinations. Hookdeck's multi-destination support lets you simultaneously send the same event to multiple services, microservices, analytics systems, and backup storage.

Testing and local development challenges

The Problem: Testing webhooks locally is awkward. You need to expose your local environment to the internet, and triggering specific event sequences for edge case testing is difficult.

Why It Happens: Webhooks require a publicly accessible HTTPS endpoint, but development happens on local machines behind NAT/firewalls. Stripe's test mode helps, but generating specific event sequences still requires creating test data.

Workarounds:

  • Use Stripe CLI to forward webhooks to localhost
  • Use ngrok or similar tunneling services
  • Create test fixtures and mock webhook payloads
  • Maintain comprehensive test data generation scripts

How Hookdeck Can Help: The Hookdeck CLI lets you receive webhooks on your local machine without deploying to a public server. You can replay past events with one click, making it easy to test specific scenarios. The dashboard provides full visibility into event history for debugging.

Delayed invoice webhook delivery

The Problem: Invoice webhooks don't always send immediately. Stripe may delay them by up to an hour, causing confusion during development and testing.

Why It Happens: When webhooks are configured, Stripe waits until one hour after the last webhook is successfully sent before sending invoice events. This batching behavior optimizes delivery but creates unexpected delays.

Workarounds:

  • Account for delays in your testing procedures
  • Don't rely on immediate webhook delivery for time-sensitive invoice processing
  • Use API polling as a supplement for critical invoice operations

How Hookdeck Can Help: While Hookdeck cannot change Stripe's sending behavior, it ensures that when webhooks do arrive, they're reliably captured, logged, and delivered. The detailed logging helps you understand actual delivery timing and diagnose apparent delays.

Signature verification complexity

The Problem: Implementing signature verification correctly across different frameworks and languages is error-prone. Issues with raw body access, character encoding, or clock skew can cause legitimate webhooks to fail verification.

Why It Happens: Stripe requires the raw, unmodified request body for signature verification. Many web frameworks automatically parse JSON bodies, which changes the byte-for-byte content and breaks verification. Clock skew between servers can also cause timestamp validation failures.

Workarounds:

  • Capture raw request body before any framework parsing
  • Use Stripe's official SDKs which handle verification correctly
  • Ensure server clocks are synchronized with NTP
  • Test verification thoroughly in all environments

How Hookdeck Can Help: Hookdeck handles Stripe signature verification at the edge, validating webhooks before they reach your infrastructure. Legitimate requests pass through; invalid requests are flagged and blocked. You can then implement a simpler, single Hookdeck signature verification on your end instead of dealing with each platform's unique implementation.

Connect webhook complexity

The Problem: Stripe Connect introduces additional webhook complexity. You need separate endpoints for account events vs. Connect events, and managing webhooks across many connected accounts is challenging.

Why It Happens: Connect platforms must handle both their own account events and events from connected accounts. While you only need one Connect endpoint for all connected accounts, the event routing and account identification adds complexity.

Workarounds:

  • Create separate routes for account webhooks and Connect webhooks
  • Use event metadata to route events to appropriate handlers
  • Implement robust account identification from event data

How Hookdeck Can Help: Hookdeck's filtering and routing capabilities let you direct Connect events to appropriate destinations based on event type, connected account ID, or any other event property. Transform and route events without complex application-level logic.

Best practices

Always verify webhook signatures

Signature verification is non-negotiable. Without it, malicious actors could send fake webhook events to your endpoint, potentially resulting in unauthorized access, fraudulent transactions, or data corruption.

Return 2xx status codes quickly

Your endpoint must return a successful status code (200-299) before performing any complex business logic. Stripe has timeout limits, and if your handler takes too long, the request will be marked as failed and retried.

Subscribe only to required events

Configure your webhook endpoints to receive only the event types your integration actually needs. Listening for extra events (or all events) puts unnecessary strain on your server and increases processing overhead.

Use separate endpoints for test and live modes

Use separate webhook URLs with different signing secrets for test mode and live mode. This prevents test events from affecting production data and allows you to safely test changes before deploying to production.

Process events asynchronously with queues

For any non-trivial processing, push webhook events to an asynchronous queue rather than processing them synchronously. This approach provides better scalability, prevents timeout issues, and makes it easier to handle retries and failures.

Implement reconciliation jobs

Webhooks can fail for various reasons: your server might be down, network issues might prevent delivery, or bugs might cause processing failures. Implement periodic reconciliation jobs that check Stripe's API to catch any events you may have missed.

Implement idempotent handlers

Design your webhook handlers to produce correct results regardless of how many times they receive the same event:

def process_payment_webhook(payload):
    event_id = payload['id']

    # Check if we've already processed this event
    if cache.get(f"stripe_event_{event_id}"):
        return  # Already processed

    # Process the event
    do_processing(payload)

    # Mark as processed with TTL
    cache.set(f"stripe_event_{event_id}", True, ex=86400)

Validate payloads

Don't assume webhook payloads are well-formed. Validate the structure before processing:

def process_charge_event(payload):
    # Validate required fields exist
    if payload.get('type') != 'charge.succeeded':
        return

    charge = payload.get('data', {}).get('object')
    if not charge:
        logger.warning(f"Malformed charge event: {payload}")
        return

    # Process with confidence
    amount = charge.get('amount')
    # ...

Conclusion

Stripe webhooks are essential for building real-time, event-driven payment integrations. While the basic concept is straightforward, production implementations can face challenges around reliability, scalability, and operational complexity.

For teams that want to focus on business logic rather than infrastructure, platforms like Hookdeck can handle the operational complexity of webhook delivery, retry logic, and scaling. With these practices in place, Stripe webhooks become a dependable foundation for your payment automation.


Author picture

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.