Using Webhooks

    Use webhooks to be notified about events that happen in a Stripe account.

    Stripe can send webhook events that notify your application any time an event happens on your account. This is especially useful for events—like disputed charges and many recurring billing events—that are not triggered by a direct API request. This mechanism is also useful for services that are not directly responsible for making an API request, but still need to know the response from that request.

    You can register webhook URLs that we will notify any time an event happens in your account. When the event occurs—a successful charge is made on a customer’s subscription, a transfer is paid, your account is updated, etc.—Stripe creates an Event object.

    This Event object contains all the relevant information about what just happened, including the type of event and the data associated with that event. Stripe then sends the Event object, via an HTTP POST request, to any endpoint URLs that you have defined in your account’s Webhooks settings. You can have Stripe send a single event to many webhook endpoints.

    When to use webhooks

    Webhooks are necessary only for behind-the-scenes transactions. Most Stripe requests (e.g., creating charges or refunds) generate results that are reported synchronously to your code. These don’t require webhooks for verification.

    If you use Stripe only to accept card payments, webhooks are optional. However, most payment methods using Sources require using webhooks, so that your integration can be notified about asynchronous changes to the status of Source and Charge objects.

    You might also use webhooks as the basis to:

    • Update a customer's membership record in your database when a subscription payment succeeds
    • Email a customer when a subscription payment fails
    • Examine the Dashboard if you see that a dispute was filed
    • Make adjustments to an invoice when it's created (but before it's been paid)
    • Log an accounting entry when a transfer is paid

    Configuring your Webhooks settings

    Webhooks are configured in the Dashboard's Webhooks settings section. Click Add endpoint to reveal a form where you can add a new URL for receiving webhooks.

    Stripe supports two webhook types: Account and Connect. You'll likely want to create an Account webhook, unless you've created a Connect application.

    You can enter any URL as the destination for events. However, this should be a dedicated page on your server that is set up to receive webhook notifications. You can choose to be notified of all event types, or only specific ones.

    Your current mode—whether you're using test or live API keys—determines whether test events or live events are sent to your configured URL. If you want to send both live and test events to the same URL, you need to create two separate settings. You can add as many URLs as you like, and Stripe supports basic access authentication.

    Receiving a webhook notification

    Creating a webhook endpoint on your server is no different from creating any page on your website. With PHP, you might create a new .php file on your server; with a framework like Sinatra, you would add a new route with the desired URL.

    Webhook data is sent as JSON in the POST request body. The full event details are included and can be used directly, after parsing the JSON into an Event object.

    require 'json'
    
    # Using Sinatra
    post '/my/webhook/url' do
        # Retrieve the request's body and parse it as JSON:
        event_json = JSON.parse(request.body.read)
    
        # Do something with event_json
    
        status 200
    end
    
    import json
    from django.http import HttpResponse
    
    # Using Django
    def my_webhook_view(request):
      # Retrieve the request's body and parse it as JSON:
      event_json = json.loads(request.body)
    
      # Do something with event_json
    
      return HttpResponse(status=200)
    
    // Retrieve the request's body and parse it as JSON:
    $input = @file_get_contents('php://input');
    $event_json = json_decode($input);
    
    // Do something with $event_json
    
    http_response_code(200); // PHP 5.4 or greater
    
    // Using the Spark framework (http://sparkjava.com)
    public Object handle(Request request, Response response) {
      // Retrieve the request's body and parse it as JSON:
      Event eventJson = APIResource.GSON.fromJson(request.body(), Event.class);
    
      // Do something with eventJson
    
      response.status(200);
      return "";
    }
    
    // This example uses Express to receive webhooks
    const app = require('express')();
    
    // Retrieve the raw body as a buffer and match all content types:
    app.use(require('body-parser').raw({type: '*/*'}));
    
    app.post('/my/webhook/url', function(request, response) {
      // Retrieve the request's body and parse it as JSON:
      const event_json = JSON.parse(request.body);
    
      // Do something with event_json
    
      response.send(200);
    });
    
    http.HandleFunc("/webhook", func(w http.ResponseWriter, req *http.Request) {
        body, err := ioutil.ReadAll(req.Body)
        if err != nil {
            fmt.Sprintf(os.Stderr, "Error reading request body: %v\n", err)
            w.WriteHeader(http.StatusServiceUnavailable)
            return
        }
    
        var event stripe.Event
        err = json.Unmarshal(body, &event)
        if err != nil {
            fmt.Sprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err)
            w.WriteHeader(http.StatusBadRequest)
            return
        }
    
        // do something with event.Data
    
        w.WriteHeader(http.StatusOK)
    })
    
    using System;
    using System.IO;
    using Microsoft.AspNetCore.Mvc;
    using Stripe;
    
    namespace workspace.Controllers {
        [Route("api/[controller]")]
        public class StripeWebHook : Controller {
            [HttpPost]
            public void Index() {
                var json = new StreamReader(HttpContext.Request.Body).ReadToEnd();
                var stripeEvent = StripeEventUtility.ParseEvent(json);
    
                // Do something with stripeEvent
            }
        }
    }
    

    Receiving webhooks with a CSRF-protected server

    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 webhooks. 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`
        end
    end
    
    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`.
    @require_POST
    @csrf_exempt
    def webhook(request):
      # Process webhook data in `request.body`
    

    Receiving webhooks 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.

    Responding to a webhook

    To acknowledge receipt of a webhook, your endpoint should return a 2xx HTTP status code. All response codes outside this range, including 3xx codes, will indicate to Stripe that you did not receive the webhook. This does mean that a URL redirection or a "Not Modified" response will be treated as a failure. Stripe will ignore any other information returned in the request headers or request body.

    In live mode, we will attempt to deliver your webhooks for up to three days with an exponential back off. In test mode, we retry 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.

    When viewing a specific event information through the Dashboard, you can check how many times we've attempted to send an event to an endpoint by clicking on that endpoint URL in the Webhook details section. This will show you the latest response we received from your endpoint, along with a list of all attempted webhooks and the respective HTTP status codes we received.

    Best practices

    Before going live, test that your webhook is working properly. You can do so by sending dummy test events from the Webhooks settings pane. To do this, first enable your Dashboard's Viewing test data option, then add the endpoint to which test events should be sent. Next, click that endpoint's row to view details, and finally, click the send a test webhook button. Understand that because these are dummy, test events, they will not map to real customers, invoices, charges, or other objects in your account.

    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. For that reason, you might want to have your webhook endpoint immediately acknowledge receipt by returning a 2xx HTTP status code, and then perform the rest of its duties.

    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. Additionally, we recommend verifying webhook signatures to confirm that received events are being sent from Stripe.

    Webhooks and 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 API version in use or the most current API version. The Event sent to the webhook URL is structured for the endpoint's specified version. You can use endpoint versioning to test the latest API version before upgrading to it.

    More information

    Questions?

    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.

    Was this page helpful? Yes No

    Send

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