
Stripe is reliable. Your webhook endpoint is not always reliable. This is the gap that causes silent billing failures — subscriptions that should activate don't, cancellations that aren't processed, payments that succeed in Stripe but never trigger fulfillment in your application.
Monitoring your Stripe integration means monitoring both sides: Stripe's delivery of events and your endpoint's handling of them.
When Stripe delivers a webhook to your endpoint, it expects a 2xx response within 30 seconds. If your endpoint returns anything else — a 500, a timeout, a redirect — Stripe marks the delivery as failed and retries. After multiple failed attempts, the event is marked as undeliverable.
The problem: none of this generates a visible error in your application. Users don't see a failure message. You don't get an email. Your dashboard looks normal. Meanwhile, subscription activations, payment confirmations, and cancellation cleanups are silently not happening.
Step 1: Enable Stripe's webhook event log. In your Stripe dashboard, navigate to Developers → Webhooks → your endpoint. Stripe shows the delivery attempt history, response codes, and retry status for every event. Check this regularly after deployments, as a broken deployment is the most common cause of sudden webhook failures.
Step 2: Monitor your webhook endpoint externally. Your webhook endpoint URL should return a fast, correct response. Add it as an uptime monitor — not for HTTP GET monitoring (webhooks are POST), but to confirm the route exists and your application is serving it. A 405 Method Not Allowed response from a GET request to /webhook/stripe confirms the endpoint is live; a 404 means something has changed in your routing.
Step 3: Alert on Stripe webhook failures. Stripe doesn't natively send alerts when events fail. Use Stripe's API to query for failed events:
import stripe
# Check for events that failed delivery in the last hour
events = stripe.WebhookEndpoint.list()
for endpoint in events.data:
# Check endpoint delivery success rate
pass
# Or directly check events
failed = stripe.Event.list(
delivery_success=False,
created={'gte': int(time.time()) - 3600}
)
if failed.data:
# Alert — events failed delivery in the last hour
alert_team(f"{len(failed.data)} webhook events failed delivery")
Schedule this check to run every 15–30 minutes.
Webhook monitoring catches processing failures. Checkout monitoring catches the user-facing flow itself. These are different things — your webhooks can be working while your checkout page is broken.
Add a dedicated monitor for your checkout page URL. A checkout that returns a 500, redirects incorrectly, or fails to load Stripe.js is invisible to your webhook monitoring.
For the most critical path monitoring — actually testing that a checkout can be initiated — you can use Stripe's test mode to create synthetic transactions:
// A smoke test endpoint that confirms Stripe is configured correctly
app.get('/health/stripe', async (req, res) => {
try {
// Verify Stripe key works by listing one product
const products = await stripe.products.list({ limit: 1 });
res.json({ status: 'ok', stripe: 'reachable' });
} catch (err) {
res.status(503).json({ status: 'error', stripe: err.message });
}
});
Point a monitor at /health/stripe to confirm your Stripe integration is configured and reachable, even without processing a real transaction.
Stripe retries failed webhooks. Your endpoint must be idempotent — processing the same event twice must not cause double fulfillment, double provisioning, or duplicate charges.
Always check whether an event has already been processed before acting on it:
def handle_payment_succeeded(event):
payment_intent_id = event['data']['object']['id']
# Check if already processed
if Order.objects.filter(stripe_payment_intent=payment_intent_id).exists():
return # Already handled
# Process the payment
create_order(event['data']['object'])
This is especially important once you have monitoring and alerting that might trigger manual replays.
Domain Monitor monitors your application URLs every minute from multiple locations. Add monitors for:
Create a free account and set alerts to fire immediately if any of these return unexpected responses. See how to set up downtime alerts for alert configuration.
When your site goes down, your status page becomes the most important page you have. Here's why it matters, what happens when you don't have one, and what a good status page does during a real outage.
Read moreYour domain is resolving, but pointing to the wrong server — showing old content, a previous host's page, or someone else's site entirely. Here's what causes this and how to diagnose it.
Read moreUptime monitoring isn't foolproof. Single-location monitors, wrong health check endpoints, long check intervals, and false positives can all cause real downtime to go undetected. Here's what to watch out for.
Read moreLooking to monitor your website and domains? Join our platform and start today.