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 --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…
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.
$ 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.
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.
$ 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.