DunningKit

← All articles · 5 min read · 2026-05

Failed-payment recovery rates by Stripe decline code

A reference table for sizing what's actually recoverable in your failed-payment data. Recovery rates are aggregate estimates; absolute numbers vary 20–30% based on customer geography, card-issuer mix, and plan tier.

The decline codes that matter

Stripe returns hundreds of distinct decline codes. The ten below cover ~90% of the volume on a typical B2B SaaS Stripe account. The others are either rare or genuinely unrecoverable (e.g. fraudulent when the issuer flags real fraud).

Code Frequency Retry rate + Customer flow Best action
insufficient_funds ~25–35% ~70% ~75% Retry at 24h + 72h. Email if still failing.
expired_card ~20–30% ~5% ~85% Card-update email immediately. Don't waste retries.
generic_decline ~10–15% ~25% ~40% Retry once at 24h. Then email customer to contact issuer.
do_not_honor ~8–12% ~30% ~50% Issuer-side block. Retry at 24h + 7d. Customer needs to call issuer.
card_velocity_exceeded ~3–6% ~10% ~55% Issuer rate-limit. Wait 24h, retry once.
incorrect_cvc ~2–4% ~5% ~70% Prompt customer to re-enter CVC. Don't retry without input.
fraudulent ~2–4% ~5% ~30% Manual review. Don't auto-retry — increases fraud signal at issuer.
processing_error ~1–3% ~80% ~85% Stripe-side transient. Retry at 1h.
card_not_supported ~1–2% ~10% ~60% Customer needs different card type. Email with PayPal/ACH alternative.
currency_not_supported <1% ~5% ~40% Customer's card can't pay your currency. Offer regional payment method.

Why "retry rate alone" lies

The "Retry rate" column shows what percentage of failures recover with retries only — no customer email, no card-update flow. For codes like expired_card, that's ~5% (a card occasionally un-expires when the customer's issuer renewed it without them noticing). The "+ Customer flow" column shows the recovery rate when you ALSO email the customer with a card-update link.

The gap between "Retry rate" and "+ Customer flow" is the dunning opportunity. For a Stripe account with $20k/month of failed charges:

This is why dunning tools that include customer-facing flows charge $99–$299/month — the math works at almost any scale above $10k/month of failed charges.

Retry timing — the underdiscussed lever

Beyond "retry yes/no," the timing of the retry matters more than most teams realise. Three rules of thumb that show up consistently in the recovery data:

  1. Don't retry within 1 hour. Same-card-same-amount-same-hour retries trigger issuer rate-limits (card_velocity_exceeded) which actively hurt your recovery on subsequent attempts.
  2. The 24-hour window is the sweet spot for the first retry. Issuers typically clear soft-block flags overnight. Retry at +24h on most codes.
  3. Don't retry past 7 days for the same card. If it hasn't recovered by then, the card is dead — you're better off in a customer-facing flow.

How to use this on your own data

Stripe Dashboard → Payments → filter by Status: Failed → Export to CSV. Run the DunningKit CLI on the export:

$ pip install dunningkit
$ dunningkit analyze stripe-failed-charges.csv

Period: 2026-04-01 to 2026-04-30
Failed charges: 142 totalling $14,820

Decline-code breakdown:
  insufficient_funds        38 charges  $3,940   ~$2,955 recoverable
  expired_card              31 charges  $3,210   ~$2,729 recoverable (with card-update flow)
  ...

Total recoverable potential: ~$8,400 (57%)

The free CLI returns the breakdown above for any CSV. If the recoverable number looks meaningful, the rest of the toolkit (retry sequences, customer flows, SMS escalation) is on the way.

Notify me when v0.1 ships: