Card Expiry Reminders for Stripe SaaS
Send card expiry reminders by checking each customer's card.exp_month and card.exp_year via the Stripe API, then emailing at 30, 14, and 3 days before expiration. Expired cards cause 25-30% of all failed payments. Pre-dunning reminders prevent 55-65% of those failures from ever happening.
The best way to handle a failed payment is to prevent it from happening. Expired credit cards are the #1 cause of failed subscription payments, responsible for 25-30% of all involuntary churn. The card expiry date is predictable. it's right there in the Stripe API. Yet most SaaS companies wait until the payment fails, then scramble to recover it with dunning emails. Pre-dunning card expiry reminders flip this approach: you contact the customer before the failure, while their subscription is still active and there's no urgency or friction. SaaS companies using pre-dunning report a 30-40% reduction in failed payments from expired cards. See the failed payment recovery benchmarks for supporting data. Use our dunning ROI calculator to estimate the impact, and combine pre-dunning with a full dunning setup in Stripe.
Why pre-dunning matters
Dunning (recovering after a failure) has a recovery ceiling. Even with a perfect multi-email sequence, Smart Retries, and in-app banners, you'll recover about 55-65% of failed payments. That means 35-45% are permanently lost. Pre-dunning targets the prevention side: if the customer updates their card before it expires, the payment never fails in the first place. There's nothing to recover.
The conversion rate on pre-dunning emails is also higher than dunning emails. A customer who gets a friendly "Hey, your card expires next month. want to update it?" email converts at 25-35%. A customer who gets a "Your payment failed" email converts at 15-25%. Pre-dunning has better optics (you look proactive instead of chasing payment), less urgency stress, and the customer's card is still technically valid when they click through to update.
Stripe's automatic card updater
Before building pre-dunning, know that Stripe already handles some expired cards automatically. Stripe participates in Visa Account Updater (VAU) and Mastercard Automatic Billing Updater (ABU). These programs let card networks push updated card details to merchants when a bank issues a replacement card.
How it works: when a customer's card expires and the bank issues a replacement (same account, new number/expiry), the card network notifies Stripe. Stripe automatically updates the stored PaymentMethod with the new details. The next charge goes through on the new card without the customer doing anything. This is enabled by default on most Stripe accounts.
Limitations: the card updater doesn't work for all card types, all banks, or all countries. It covers roughly 60-70% of US and EU cards. It also only works when the bank issues a direct replacement. If the customer closes the account or switches banks entirely, the updater has nothing to push. That leaves a significant gap that pre-dunning emails fill.
Method 1: Stripe API polling
The most straightforward approach is to periodically query the Stripe API for customers whose payment method is about to expire. Here's the implementation:
Step 1: List expiring payment methods
// Run this daily via cron job
const now = new Date();
const thirtyDaysFromNow = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);
const targetMonth = thirtyDaysFromNow.getMonth() + 1;
const targetYear = thirtyDaysFromNow.getFullYear();
// Get all active subscriptions
const subscriptions = await stripe.subscriptions.list({
status: 'active',
limit: 100,
expand: ['data.default_payment_method'],
});
// Filter for expiring cards
const expiring = subscriptions.data.filter(sub => {
const pm = sub.default_payment_method;
if (!pm || pm.type !== 'card') return false;
return pm.card.exp_month === targetMonth
&& pm.card.exp_year === targetYear;
});
// Send reminder emails to expiring customers
for (const sub of expiring) {
await sendCardExpiryReminder(sub.customer, sub.default_payment_method);
}Step 2: Build the reminder sequence
Send three emails at escalating intervals:
- 30 days before expiry: "Heads up: your card ending in {last4} expires next month. Update it now to avoid any interruption."
- 14 days before expiry: "Reminder: your card expires in 2 weeks. Update it in 30 seconds so your {product} access isn't interrupted."
- 3 days before expiry: "Your card expires in 3 days. This is the last reminder before your next payment attempt."
Include a direct link to update the card. Use Stripe's Billing Portal for the update page:
const session = await stripe.billingPortal.sessions.create({
customer: customerId,
return_url: 'https://yourapp.com/settings',
flow_data: {
type: 'payment_method_update',
},
});
// session.url = card update link for the emailStep 3: Track and deduplicate
Store which reminders you've sent in your database to avoid sending duplicates. Track open rates and card update conversions. When a customer updates their card, stop the reminder sequence (listen for payment_method.attached or customer.updated events).
Build time: 8-15 hours for the cron job, email templates, deduplication logic, and tracking. You'll also need to handle pagination for the Stripe API if you have more than 100 active subscriptions.
Method 2: Stripe webhook listener
Instead of polling the API daily, you can listen for the customer.source.expiring webhook event. Stripe fires this event approximately 30 days before a card's expiration date.
// Webhook handler for card expiry
app.post('/webhooks/stripe', async (req, res) => {
const event = stripe.webhooks.constructEvent(
req.body, sig, endpointSecret
);
if (event.type === 'customer.source.expiring') {
const source = event.data.object;
const customerId = source.customer;
// Start your 3-email pre-dunning sequence
await startCardExpirySequence(customerId, {
last4: source.last4,
expMonth: source.exp_month,
expYear: source.exp_year,
});
}
res.json({ received: true });
});Important note: the customer.source.expiring event fires for legacy Source objects. If your customers are using PaymentMethods (the newer API), you may not receive this event. In that case, use the API polling method (Method 1) or check the payment_method.automatically_updated event to identify cards that were updated and therefore don't need reminders.
Advantage over polling: no cron job needed. Stripe tells you when a card is expiring, and you react. Less infrastructure, fewer moving parts.
Disadvantage: you only get a ~30-day heads up, and it doesn't work for all payment method types. The polling method gives you more control over timing.
Method 3: Tool-assisted pre-dunning
SaveMRR's Pre-Dunning Alerts engine handles card expiry monitoring automatically. It connects to your Stripe account, continuously monitors all active payment methods for upcoming expiry dates, and sends a branded 3-email reminder sequence at 30, 14, and 3 days before expiry. It also tracks which customers updated their card and stops the sequence automatically.
The setup is a Stripe API key paste. No cron jobs, no webhook endpoints, no email templates, no deduplication logic. SaveMRR handles the full lifecycle and reports the results in your dashboard: how many cards were expiring, how many customers updated, and how many failures were prevented.
Pre-dunning email best practices
- Keep subject lines simple. "Your card ending in 4242 expires next month" outperforms clever marketing copy
- Include the card's last 4 digits. helps the customer identify which card you're talking about
- One CTA: update card. don't distract with feature announcements or promotions. One button, one action.
- Make the update link prominent. large button, above the fold, in a contrasting color
- Use transactional tone. pre-dunning emails should read like account notifications, not marketing emails. This also improves deliverability.
- Don't send if card was already updated. check for
payment_method.automatically_updatedevents before sending. If Stripe's card updater already refreshed the card, the reminder is unnecessary. - Mention what's at stake. "Your {product} access won't be interrupted if you update before {date}" is more effective than generic "please update your card"
Expected impact
Based on data from SaaS companies using pre-dunning:
- 30-40% reduction in failed payments caused by expired cards
- 25-35% click-through rate on pre-dunning emails (vs 15-25% on post-failure dunning emails)
- 8-12% improvement in overall payment success rate when combined with Smart Retries
For a SaaS at $20K MRR with 5% involuntary churn, reducing expired-card failures by 35% translates to roughly $350/month in prevented churn. That's $4,200/year from a feature that takes 8-15 hours to build or $19/month to buy.
Next step
Find out how many of your customers have cards expiring in the next 30 days. SaveMRR's free Revenue Scan connects to your Stripe account (read-only) and shows you exactly how many payment methods are expiring soon, your current failed payment volume, and the estimated impact of adding pre-dunning. It takes 2 minutes and helps you decide whether to build or buy a pre-dunning solution.
