Having installed the current versions of the Stripe CLI and dj-stripe, running manage.py stripe_listen to receive webhooks locally fails. You’ll have to create the webhook yourself and hand its URL to stripe listen.

stripe_listen failsconsole
$ stripe --version
stripe version 1.52.0
$ poetry show dj-stripe
 name         : dj-stripe
 version      : 2.11.0
 description  : Django + Stripe made easy

$ poetry run python manage.py stripe_listen
Forwarding Stripe webhooks to http://localhost:8000/stripe/webhook/a5f9c668-49cd-47c8-8add-ed0329be2d5d/
must specify events to forward using --events, --all-snapshot, or --all-thin

Why does it fail?

The Stripe CLI requires you to specify which events you want to receive. The management command isn’t including them in the call, and you aren’t able to specify them yourself.

Normally, we would create the endpoint through the Django Admin, which also creates the endpoint in Stripe. That’ll fail though as the webhook URL is local. We are pointed to use manage.py stripe_listen, which is our starting point.

Great. We need a manual setup then…

Webhook creation failure
Creating webhooks with local URLs is prevented in the Django Admin.

How to create it manually?

If you are on an older dj-stripe version you still might have the non-UUID webhook endpoint. If that’s available to you, use it. If not, continue like I did by getting your Stripe webhook secret by running stripe listen --print-secret.

get the Stripe webhook secretconsole
$ stripe listen --print-secret  # not my actual secret...
whsec_be47787554bebcdb17a7246b18a8bf28f9bc312d9ae1b4343881caa9dc5d6958

Secret in hand, you can create a webhook endpoint through the Django Shell manually. dj-stripe uses UUIDs to prevent webhook endpoint guessing and to select the webhook signing secret used to verify event signatures. This creation succeeds because we don’t try to register that endpoint with Stripe.

manual_webhook.pypython
import uuid
from djstripe.models import WebhookEndpoint

endpoint = WebhookEndpoint.objects.create(
    id="we_local_dev",
    livemode=False,
    url="http://localhost:8000/stripe/webhook/",
    secret="whsec_be47787554bebcdb17a7246b18a8bf28f9bc312d9ae1b4343881caa9dc5d6958",
    status="enabled",
    enabled_events=["*"],
    api_version="",
    djstripe_uuid=uuid.uuid4(),
)
print(f"http://localhost:8000/stripe/webhook/{endpoint.djstripe_uuid}/")
# prints: http://localhost:8000/stripe/webhook/1eb5c9d0-f449-47e5-ad2c-80d5fa6f6dd7/

How to listen locally?

Finally, you are able to use the Stripe CLI to name your local endpoint and specify the missing --all-snapshot argument.

an operational forward 🎉console
$ stripe listen --forward-to http://localhost:8000/stripe/webhook/1eb5c9d0-f449-47e5-ad2c-80d5fa6f6dd7/ --all-snapshot
> Ready! You are using Stripe API Version [2025-05-28.basil]. Your webhook signing secret is whsec_be47787554bebcdb17a7246b18a8bf28f9bc312d9ae1b4343881caa9dc5d6958 (^C to quit)
2026-09-25 10:19:29   --> charge.succeeded [evt_3UJULqDEhzP3e7dk1bzFQpmv]
2026-09-25 10:19:29   --> checkout.session.completed [evt_1UJULtDEhzP3e7dk4bM5qhRi]
2026-09-25 10:19:29   --> invoice.finalized [evt_1UJULtDEhzP3e7dkbN17z8Ul]
2026-09-25 10:19:29   --> payment_method.attached [evt_1UJULtDEhzP3e7dkEUHdMFMa]
2026-09-25 10:19:29   --> invoice.paid [evt_1UJULtDEhzP3e7dk1LsRUN40]
2026-09-25 10:19:29   --> customer.subscription.created [evt_1UJULtDEhzP3e7dkN9DJ2iEI]
2026-09-25 10:19:29   --> payment_intent.succeeded [evt_3UJULqDEhzP3e7dk1dj5MkiI]

I really appreciate that Stripe provides a mechanism where we don’t have to fiddle with solutions for tunnelling publicly routable traffic to our local machines.