Stripe Smart Retries Not Working: How to Fix It (And Prevent It Forever)

Smart Retries are probably working. they're just not enough. Stripe's ML-based retries recover 5-10% more than fixed schedules, but they still only recover 15-25% of total failed payments. The rest require customer action (updating their card). If you're expecting Smart Retries alone to solve involuntary churn, you need to add dunning emails and card expiry alerts on top. See our SaveMRR vs Stripe Smart Retries comparison for the full picture.

Why Smart Retries Seem Broken

Stripe Smart Retries use machine learning to pick the optimal time to retry a failed charge. considering day of week, time of day, and historical success patterns across Stripe's network. They're enabled by default for new Stripe accounts. But they have hard limits that founders misunderstand.

Smart Retries only help with soft declines. temporary issues like insufficient_funds or processing_error where retrying at a different time might succeed. For hard declines (expired_card, stolen_card, card_not_supported), no amount of retry timing optimization will recover the charge. The card is permanently rejected.

Here are the 4 most common reasons Smart Retries appear to not be working:

1. You're looking at hard declines. Check the decline_code on your failed invoices. If most failures are expired_card or generic_decline with a hard-fail flag, Smart Retries can't help. You need the customer to update their payment method.

2. Smart Retries aren't actually enabled. Go to Dashboard > Settings > Subscriptions and emails > Manage failed payments. Confirm "Use Smart Retries" is toggled on. If you're using a fixed schedule, Stripe retries at your configured intervals instead of using ML.

3. Your subscription status after all retries is set to "Cancel subscription." If Stripe cancels the subscription after exhausting retries, the customer loses access and you lose the recurring relationship. Switching to "Mark as unpaid" or "Leave as past due" gives you more time to recover.

4. You have no customer communication layer. Smart Retries silently retry the charge; the customer has no idea it's happening. Without dunning emails prompting them to update their card, you're relying entirely on Stripe's retries to get lucky. That's a 15-25% recovery ceiling.

Check if Smart Retries are recovering anything
// List recent invoices that were recovered by retry
const invoices = await stripe.invoices.list({
  status: 'paid',
  limit: 100,
});

// Filter for invoices that had a previous failed attempt
const recoveredByRetry = invoices.data.filter(inv =>
  inv.attempt_count > 1 && inv.status === 'paid'
);

console.log(`Recovered by retry: ${recoveredByRetry.length}/${invoices.data.length}`);
console.log(`Recovery rate: ${(recoveredByRetry.length / invoices.data.length * 100).toFixed(1)}%`);

Quick Fix (Manual)

1

Go to Dashboard > Settings > Subscriptions and emails > Manage failed payments. Confirm "Use Smart Retries" is toggled on (not a fixed schedule).

2

Change "If all retries fail" from "Cancel subscription" to "Mark subscription as unpaid"; this keeps the billing relationship alive and gives you more time to recover.

3

Review your failed invoices (Billing > Invoices > Past due). Check the decline_code on each one to understand whether you're dealing with soft or hard declines.

4

For hard-declined invoices (expired_card, card_not_supported): manually send the customer a billing portal link to update their card.

5

For invoices stuck as "Past due" beyond 21 days: the retry window has expired. You need to either manually retry the charge or reach out to the customer directly.

Audit your failed invoices by decline type
const invoices = await stripe.invoices.list({
  status: 'open',
  limit: 100,
});

const declineCounts = {};
for (const inv of invoices.data) {
  const code = inv.last_payment_error?.decline_code || 'unknown';
  declineCounts[code] = (declineCounts[code] || 0) + 1;
}

console.log('Decline breakdown:');
Object.entries(declineCounts)
  .sort((a, b) => b[1] - a[1])
  .forEach(([code, count]) => {
    console.log(`  ${code}: ${count}`);
  });

// If most are "expired_card" or "card_not_supported",
// Smart Retries can't help. You need dunning emails.

Warning: If you switch from "Cancel subscription" to "Mark as unpaid" or "Leave as past due," make sure your app checks the subscription.status field to gate access. Otherwise, customers with failed payments will continue using your product indefinitely without paying.

Permanent Fix (Automated)

Smart Retries are step 1, not the whole solution. Think of them as the silent background process that handles the easy recoveries. To capture the other 75-85% of failures, you need two additional systems:

Dunning emails; a 5-7 email sequence sent to the customer over 21 days with escalating urgency and one-click card update links. This is the single highest-impact recovery mechanism: customers can't fix what they don't know about, and Stripe's Smart Retries are silent. Dunning emails turn a 15-25% recovery rate into 40-55%.

Pre-dunning card expiry alerts. emails sent 30, 14, and 7 days before a card expires. This prevents the failure from ever happening. Stripe fires the customer.source.expiring webhook ~30 days before expiry, but doesn't send any customer communication by default. You need to build that yourself. or let SaveMRR handle it.

SaveMRR layers all three systems. Smart Retries (Stripe's) + dunning sequences (7 automated emails) + card expiry alerts. into a single $19/mo tool. Paste your Stripe key and the entire recovery pipeline activates in minutes. No webhook code, no email templates, no cron jobs.

What a complete recovery pipeline looks like (what SaveMRR automates)
// The 3-layer recovery stack:
//
// Layer 1: Smart Retries (Stripe handles this)
//   - ML-optimized retry timing for soft declines
//   - Recovers ~15-25% of failures automatically
//
// Layer 2: Dunning emails (SaveMRR handles this)
//   - Email 1: Immediate notification + card update link
//   - Email 2: Day 3 follow-up with urgency
//   - Email 3: Day 7 mid-cycle reminder
//   - Email 4: Day 10 consequence warning
//   - Email 5: Day 14 final notice
//   - Email 6: Day 18 last-chance offer
//   - Email 7: Day 21 grace period end
//   - Recovers additional 25-30% on top of retries
//
// Layer 3: Pre-dunning (SaveMRR handles this)
//   - 30-day card expiry alert
//   - 14-day follow-up
//   - 7-day urgent reminder
//   - Day-of last chance
//   - Prevents 15-20% of failures before they happen
//
// Combined: 40-55% total recovery rate vs 15-25% with
// Smart Retries alone. That's 2-3x more revenue recovered.

Tip: Run a free Revenue Scan to see your actual recovery rate right now. Most SaaS founders using only Smart Retries are shocked to learn that 75%+ of their failed payments are going unrecovered. The Scan shows you the exact dollar amount you're losing monthly.

Related Stripe Billing Issues

Frequently Asked Questions

Are Stripe Smart Retries enabled by default?

Yes, for Stripe accounts created after 2020. Smart Retries are the default retry method. Older accounts may still use fixed retry schedules. Check Dashboard > Settings > Subscriptions and emails > Manage failed payments to confirm. If you see "Use Smart Retries" toggled on, they're active.

What's the actual recovery rate of Smart Retries?

Stripe's own data suggests Smart Retries recover 5-10% more failed payments than fixed retry schedules. In absolute terms, Smart Retries alone recover approximately 15-25% of all failed subscription payments. The remaining 75-85% require customer action, which is where dunning emails become critical.

Can I use Smart Retries and dunning emails together?

Yes, and you should. Smart Retries handle the payment retry timing (when to re-attempt the charge on the card). Dunning emails handle customer communication (prompting them to update their card). They operate on different layers and don't conflict. Using both together recovers 40-55% vs. 15-25% with retries alone.

Should I switch from Smart Retries to a fixed schedule?

Almost never. Stripe's ML model has data from millions of transactions to optimize retry timing. You can't beat that with a manually configured schedule. The only exception is if you have very specific business logic (e.g., you know your customers get paid on the 1st and 15th of the month and want to retry on those dates specifically).

Why did Smart Retries recover a payment weeks later?

Smart Retries can spread retries over up to 3 weeks, targeting moments when the customer's bank is most likely to approve the charge (e.g., after payday, on weekdays during business hours). A payment that failed on March 1st might be successfully recovered on March 18th. This is by design; the ML model sacrifices speed for higher overall recovery rates.

SaveMRR catches these automatically

Stop firefighting Stripe billing issues manually. Paste your API key, get a free Revenue Scan in 60 seconds, and let SaveMRR handle recovery automatically.

Run my free scan