A customer's card declined on renewal, and your dashboard now shows an unpaid invoice for someone who never decided to leave. Open your Stripe Dashboard today, go to Billing, then Revenue recovery, and check whether retries and failed-payment emails are switched on. If either is off, you are losing renewals that nobody chose to cancel.
Dunning is the automated process that recovers a subscription payment after a card is declined: it retries the charge on a schedule and sends the customer emails asking them to fix their payment method. This guide covers the Stripe settings that matter, what to do on top of them, and where your own product needs to step in. It sits inside the SaaS billing and payments series, so if the subscription engine itself is not live yet, start with Stripe subscription billing for SaaS, and read subscription lifecycle states for what past_due and unpaid do to access. I checked every Stripe behaviour below on 30 September 2026.
What is involuntary churn and how does dunning fix it?
Involuntary churn is a subscription lost because a payment failed, not because the customer chose to leave, and dunning fixes it by retrying the charge and prompting the customer to update their card.
The split matters because the two problems need different tools. Voluntary churn, where a customer cancels, calls for product value and a cancellation flow with real alternatives. Involuntary churn calls for retries, emails and a fast way to replace a card. Do not send recovery emails to someone who already cancelled, and tag the failure reason in your billing events so the sequence fires only for payment failures.
The scale is worth a look. Stripe's Billing page states that it recovered $8.2B in failed payments in 2025 and that businesses using Stripe recover 55% of failed payments on average. Those are Stripe's own marketing figures, so treat them as the vendor's claim about its own platform rather than a benchmark for your business. The useful number is yours: Stripe's recovery analytics show your first-attempt failure rate and recovery rate, with a breakdown of what recovered the money (retries, emails, or other methods). Read that page before changing anything so you have a baseline.
How should you configure Stripe failed payment retries?
Turn on Smart Retries and keep the recommended default unless you have data against it: Stripe's documented default is 8 tries within 2 weeks.
| Option | What you set | Notes |
|---|---|---|
| Smart Retries | Number of tries within 1 week, 2 weeks, 3 weeks, 1 month or 2 months | Stripe recommends it; default is 8 tries within 2 weeks |
| Custom schedule | Up to three retries, each a set number of days after the previous | Use only if you have a reason to override timing |
| Automations | Different policies per customer segment | No code, configured in the Dashboard |
Stripe's retry documentation says Smart Retries uses signals such as how many devices have presented a payment method recently and the best time to pay, and gives one example: debit card payments in some countries can be slightly more successful at 12:01 AM local time. That is a reason to let Stripe choose timing instead of hand-coding a fixed "retry every 6 hours" loop, because a fixed loop cannot use those signals.
Which failures will Stripe not retry?
Stripe will not retry when no payment method is available, when the issuer returns a hard decline code, for India-issued cards, or when a Connect account is disconnected. The hard decline codes listed include incorrect_number, lost_card, pickup_card, stolen_card, revocation_of_authorization, authentication_required and transaction_not_allowed. For these, scheduled retries continue but the payment only runs once the customer supplies a new payment method. This is the moment where your email and in-app prompt do the work, because no retry can help.
How do you drive your own emails from webhooks?
Listen for invoice.payment_failed. Stripe documents that the attempt_count on that event tells you how many attempts have been made, and that the invoice's next_payment_attempt field shows when the next one is scheduled (if you use Stripe automations, that field moves to invoice.updated events instead). Those two fields are enough to pick which email to send: attempt one gets the gentle notice, a later attempt with a date on it gets the warning. Verify webhook signatures before acting on any event, as Stripe's subscription guide also states, because this handler can change a customer's access.
What happens after the last retry?
You choose the outcome in your failed-payment settings. The subscription can move to canceled, move to unpaid, or stay past_due. According to Stripe's subscription overview, an unpaid subscription keeps generating invoices without attempting payment, and Stripe says to revoke access when it reaches that status. Pick the outcome deliberately and make your access rules match it, otherwise a customer can keep using the product after the retries have given up.
What makes a failed payment email work?
A recovery email works when it names the problem, gives one button to a page where the card can be replaced, and stops before it feels like nagging.
Stripe can send these for you. Its customer email settings include an option to send an email after each failed card payment, plus expiring-card notices sent one month before a card on file expires, renewal reminders, and trial-ending reminders seven days before a trial ends. Each of these can link to a Stripe-hosted page where the customer updates their payment method, or to your own subscription management page. The hosted link stops working if the subscription status becomes cancelled, incomplete_expired or unpaid, so if your final-notice email goes out after that point, the link it carries may be dead.
What I recommend for a sequence, based on the mechanics above rather than a measured result:
- First email, sent on the first failure: say the payment failed, name the card by its last four digits, and link to the update page.
- Second email, partway through the retry window: say access is at risk and give the date.
- Final email, before the last retry: state the cancellation or suspension date.
- In the app, show a banner that stays visible but does not block work until the account is actually restricted.
Keep it to three or four messages. I do not have measured data on the ideal count; watch your own recovery rate by method in Stripe's analytics, which separates retries from emails, and adjust.
Test the sequence before customers see it. In a Stripe sandbox, emails are not sent automatically; Stripe's documentation says to use addresses that belong to your verified email domain or an active team member to receive failed payment, renewal, trial-ending and expiring-card notifications there. You can also review sent emails for the last 60 days in the Customers page email logs.
How do you stop payments failing before dunning starts?
Reduce failures upstream with automatic card updates, expiry reminders and plan design, since a payment that never fails needs no recovery.
Stripe's card documentation explains that it works with card networks to update saved card details automatically when a customer receives a replacement card. It says this is widely supported in the United States, international support varies by country, and you cannot tell in advance which cards support it. So do not count on it alone; pair it with the expiring-card email above. You can also listen for the payment_method.automatically_updated event to keep your own records in step.
Annual billing is another lever. A customer billed once a year has one renewal per year that can fail instead of twelve. That is arithmetic, not a measured recovery result, and it has trade-offs for cash flow and churn visibility that I cover in building a B2B SaaS pricing page that converts.
What does dunning look like in a real multi-tenant product?
The Callidus case study documents the shape of it. Callidus is a React, Firebase and Stripe Connect SaaS for UK aesthetic clinics. It handles twenty Stripe webhook events, runs a four-stage trial lifecycle (a 3-day reminder, a 1-day reminder, expiry with a 3-day grace window, then suspension), and has a second scheduled function that escalates tenants with failed payments. During a grace period, pages stay viewable while mutations are blocked on the server, at both the route layer and in the Firestore rules. The point for dunning: your billing state has to reach the layer that enforces access, which I describe for Firebase in Firestore rules for a multi-tenant clinic SaaS.
BookBed, my Flutter, Firebase and Stripe booking product, has subscription infrastructure wired and tested at EUR 9 per month for up to twenty units, but it is not yet activated for paid traffic. That means I have no recovery statistics from it, and I am not going to invent any. If you want the webhook-to-database pattern for Supabase, Stripe and Supabase subscription pattern walks through it.
Open your Stripe revenue recovery overview now and write down two numbers: your first-attempt failure rate and your recovery rate. Which of the two is worse, and does that point you at retries, at emails, or at stopping failures earlier?
