Best Practices for Using Webhooks

    Learn about best practices when working with webhooks.

    Webhooks provide a powerful method to track the state of transactions and actions for your integeration. Review these best practices to ensure your webhooks remain secure and function with your integration as seamlessly as possible:

    API versions

    The structure of an Event object sent in a webhook is dictated by the API version in your account settings at the time of the event’s occurrence. For example, if your account is set to an older API version, such as 2015-02-16, and you change the API version for a specific request via versioning, the Event object generated and sent to your endpoint is still based upon the 2015-02-16 API version.

    Event objects can never be changed once created. For example, if a charge is updated, the original charge event remains unchanged. This means that subsequent updates to your account’s API version do not retroactively alter existing Event objects. Fetching older events by calling /v1/events using a newer API version also has no impact on the structure of the received events.

    You can set test webhook endpoints to either your default API version or the latest API version. The Event sent to the webhook URL is structured for the endpoint’s specified version. You can also programmatically create endpoints with a specific api_version.

    Delivery attempts and retries

    Understand how to view delivery attempts, event logs, and the retry logic when webhook events aren’t acknowledged.

    View events

    When viewing information about a specific event through the Dashboard, you can check how many times Stripe attempted to send an event to an endpoint by clicking on that endpoint URL in the Webhooks section. This shows the latest response from your endpoint, a list of all attempted webhooks, and the respective HTTP status codes Stripe received.

    Retry logic

    In live mode, Stripe attempts to deliver your webhooks for up to three days with an exponential back off. In the Events section of the Dashboard, you can view when the next retry will occur.

    In test mode, Stripe retries three times over a few hours. Webhooks cannot be manually retried after this time, though you can query for the event to reconcile your data with any missed events.

    Event handling

    Handling webhook events correctly is crucial to making sure your integration’s business logic works as expected.

    Acknowledge events immediately

    If your webhook script performs complex logic, or makes network calls, it’s possible that the script would time out before Stripe sees its complete execution. Ideally, your webhook handler code (acknowledging receipt of an event by returning a 2xx status code) is separate of any other logic you do for that event.

    Handle duplicate events

    Webhook endpoints might occasionally receive the same event more than once. We advise you to guard against duplicated event receipts by making your event processing idempotent. One way of doing this is logging the events you’ve processed, and then not processing already-logged events.

    Order of events

    Stripe does not guarantee delivery of events in the order in which they are generated. For example, creating a subscription might generate the following events:

    • customer.subscription.created
    • invoice.created
    • invoice.payment_succeeded
    • charge.created (if there’s a charge)

    Your endpoint should not expect delivery of these events in this order and should handle this accordingly. You can also use the API to fetch any missing objects (e.g., you can fetch the invoice, charge, and subscription objects using the information from invoice.payment_succeeded if you happen to receive this event first).


    Keeping your endpoints secure is critical to protecting your customers’ information. Stripe provides several ways for you to verify events are coming from Stripe in a secure manner.

    CSRF protection

    If you’re using Rails, Django, or another web framework, your site might automatically check that every POST request contains a CSRF token. This is an important security feature that helps protect you and your users from cross-site request forgery attempts. However, this security measure might also prevent your site from processing legitimate events. If so, you might need to exempt the webhooks route from CSRF protection.

    class StripeController < ApplicationController
      # If your controller accepts requests other than Stripe webhooks,
      # you'll probably want to use `protect_from_forgery` to add CSRF
      # protection for your application. But don't forget to exempt
      # your webhook route!
      protect_from_forgery :except => :webhook
      def webhook
        # Process webhook data in `params`
    import json
    # Webhooks are always sent as HTTP POST requests, so ensure
    # that only POST requests reach your webhook view by
    # decorating `webhook()` with `require_POST`.
    # To ensure that the webhook view can receive webhooks,
    # also decorate `webhook()` with `csrf_exempt`.
    def webhook(request):
      # Process webhook data in `request.body`

    Receive events with an HTTPS server

    If you use an HTTPS URL for your webhook endpoint, Stripe will validate that the connection to your server is secure before sending your webhook data. For this to work, your server must be correctly configured to support HTTPS with a valid server certificate.

    Roll endpoint secrets

    If you need to change the secret used for verifying events come from Stripe, you can do so in the Webhooks section in the Dashboard. For each endpoint, click Roll secret and you can choose to immediately expire the current secret, or delay for up to 24 hours to provide time to update the verification code on your server. During this time, multiple secrets are active for the endpoint and Stripe generates one signature for each secret until expiration.

    Verify events are sent from Stripe

    Verify webhook signatures to confirm that received events are sent from Stripe. Additionally, Stripe sends webhook events from a set list of IP addresses. Only trust events coming from these IP addresses.

    More information

    Was this page helpful?

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.


    We're always happy to help with code or other questions you might have. Search our documentation, contact support, or connect with our sales team. You can also chat live with other developers in #stripe on freenode.

    On this page