What Stripe decline codes are
When a card payment fails, Stripe returns two values that explain why: outcome.reason (Stripe's normalized classification) and decline_code (the raw issuer reason, when provided). A successful charge has neither.
These codes are not error messages for end users — they're machine-readable instructions for your billing system. Treating them as such is the single biggest lever in failed payment recovery.
Why decline codes matter for revenue recovery
A naive system retries every failed charge the same way: four attempts, fixed intervals, identical email. That approach loses money in three places:
- Retrying hard declines (lost or stolen card) burns goodwill and triggers issuer blocks. Don't retry — email.
- Retrying expired cards at all is wasted compute. The card will fail until the customer updates it.
- Retrying insufficient_funds on the wrong day(T+1, T+3, T+7) misses payday windows. Recovery rates double when retries land on the 1st or 15th.
Routing each decline code to its matching play is the highest-ROI change a billing team can make. The rest of this page is that routing table.
Decline code reference table
The codes you'll see most often on a Stripe-billed SaaS book of business, what they mean, and the right next action.
| Meaning | Recoverable? | Recommended action | Related playbook | |
|---|---|---|---|---|
| expired_card | Card past its printed expiry date. | Yes | Don't retry. Send update-card flow. | expired-card-failed, expiry-preemptive |
| insufficient_funds | Card valid, account didn't have the balance. | Yes | Retry on payday (1st or 15th) before retrying weekly. | insufficient-funds |
| authentication_required | 3DS / SCA challenge required by the issuer. | Yes | Email explainer, then retry only when customer confirms. | authentication-required |
| do_not_honor | Generic issuer refusal with no detail. | Yes — partially | Wait 24h, retry once, then contact the customer. | bank-declined-generic |
| generic_decline | Issuer declined, no useful reason. | Yes — partially | Offer an alternative payment method via email. | bank-declined-generic |
| fraudulent | Issuer suspects the charge is fraud. | Sometimes | Send merchant-descriptor whitelist guide. Do NOT retry. | fraud-block |
| lost_card / stolen_card | Card reported lost or stolen. | No | Do not retry. Email customer for a new card. | expired-card-failed (update flow) |
| pickup_card | Issuer wants the card confiscated. | No | Hard decline. Contact the customer for a new method. | expired-card-failed (update flow) |
| card_replaced (network update) | Network updater has a newer card on file. | Yes | Retry silently. Most succeed without customer contact. | card-replaced-stale |
| processing_error | Transient issuer or network glitch. | Yes | Retry after 60s. Usually resolves. | bank-declined-generic |
| high-value account, no auto-dunning | Any decline on a VIP invoice held back from automation. | Yes | Personal outreach from a named human — never a template. | untouched-vip |
| rep commitment outstanding | Customer promised to pay; deadline has passed. | Yes | Follow up referencing the prior commitment by date. | commitment-outstanding |
expired_card
The single biggest bucket of recoverable failures — roughly 30% of involuntary churn. The customer's card has hit its printed expiry date. Retrying the same card will never succeed; you need a new card on file.
The right play
- Prevent before failure. 7–14 days before expiry, email the customer with a one-tap update link. The card-expiring playbook recovers 60–80% of expiries this way.
- After failure. The expired-card playbook leads with the update link, acknowledges the failed charge once, and doesn't repeat the bad news in every follow-up.
- Enable Stripe's Card Account Updater. Free. Silently rotates cards from major issuers and catches ~30% of expiries with zero customer touch.
insufficient_funds
The card is valid; the account didn't have the balance on the day you charged. Often timing, not intent — corporate cards and consumer cards both routinely refuse on the 28th and approve on the 1st.
The right play
- Retry near payday. Schedule the retry for the next 1st or 15th, not blindly on T+3.
- Ask, don't assume. The insufficient-funds playbook offers the customer a retry date instead of demanding a new card.
- Tone matters. "Want us to try again on the 1st?" beats "Your payment failed" — same outcome, dramatically better recovery rates.
authentication_required
The issuer wants the customer to approve the charge in their banking app (3DS / SCA). This is normal in Europe and increasingly common in the US. Stripe Smart Retries won't solve it — the challenge needs a human.
The right play
- Explain 3DS in plain language. Most customers don't know what a 3DS push notification is until you tell them.
- Retry only when ready. The 3DS playbook waits for the customer to confirm they're ready, then re-fires the charge so the push lands while they have the app open.
do_not_honor
The most frustrating code in Stripe. The issuer refused but didn't say why. It's their default catch-all and covers everything from velocity limits to soft fraud flags to plain old "the customer's bank is being weird".
The right play
- Retry once after 24 hours. A meaningful share resolve themselves.
- Then contact the customer. The bank-declined playbook is honest about the unknown reason and offers an alternative method.
- Don't burn retries. Four attempts on a do_not_honor often escalates to a hard block from the issuer.
generic_decline
Stripe's catch-all when the issuer didn't return anything more specific. Treat it the same as do_not_honor: one retry, then outreach. The bank-declined playbook handles both codes with the same flow.
Fraud-related declines
The codes fraudulent, card_declined with a fraud risk score, and certain do_not_honor responses all indicate the issuer's fraud system flagged the charge. The customer's card is fine — your merchant descriptor looks suspicious to their bank.
The right play
- Never retry blindly. Retries on a fraud flag push the issuer toward a permanent block.
- Send the whitelist guide. The issuer-fraud-block playbook leads with the exact descriptor the customer will see on their statement (e.g. "CHASER*ACME 4.99") and one-page instructions per major issuer.
- Snooze 5 days. Bank whitelisting isn't instant.
card_replaced (network update)
The card network has a newer card on file for the same customer — usually because the issuer reissued the card and pushed an update through Visa Account Updater or Mastercard ABU. You don't need to email anyone; you need to retry.
The right play
- Retry silently first. The card-replaced playbook fires a retry with no customer touch. Most succeed.
- Only email if retry fails. Then fall back to the standard update-card flow.
Recovery strategy by category
Group the codes into four behavioural buckets and the strategy becomes obvious:
| Examples | Strategy | |
|---|---|---|
| Needs a new card | expired_card, lost_card, stolen_card, pickup_card | Don't retry. Send the update-card flow. |
| Timing problem | insufficient_funds, processing_error | Retry on payday or after a short delay. Customer doesn't need to act. |
| Customer-action required | authentication_required, fraudulent (whitelist) | Email explainer first. Retry only after the customer acts. |
| Issuer black box | do_not_honor, generic_decline | One retry, then offer an alternative method. |
For a top-down view of how these decisions plug into a recovery system, see the failed-payment-recovery guide. For the underlying retention math — involuntary churn: definition, formula, and how to cut it. For buyer-side comparisons of tooling that automates this, the dunning management software buyer's guide.
- Map every code your system sees to one of the four buckets above.
- Set retry policy per bucket, not per code.
- For the recoverable buckets, run a 3–4 step recovery sequence — single-email systems convert about a third as well.
Frequently asked questions
›What are Stripe decline codes?
Stripe decline codes are short machine-readable strings (e.g. expired_card, insufficient_funds, authentication_required) returned on a charge or PaymentIntent when the card issuer refuses the payment. They live on the `outcome.reason` and `decline_code` fields of the Charge object and tell you why the bank said no — so you can pick the right recovery action instead of blindly retrying.
›Which Stripe decline codes are recoverable?
Soft declines — insufficient_funds, authentication_required, do_not_honor, generic_decline, processing_error, and most expired_card cases — are recoverable with the right retry timing and customer outreach. Hard declines — lost_card, stolen_card, pickup_card, fraudulent — should never be retried and require contacting the customer for a new payment method.
›Does Stripe Smart Retries handle every decline code?
No. Smart Retries is built for transient soft declines like insufficient_funds and processing_error. It does not solve expired cards (needs a new card), authentication_required (needs the customer in their banking app), or fraud blocks (needs the customer to whitelist the merchant). Those require a recovery sequence — see the failed payment recovery guide.
›How do I see the decline code in Stripe?
On the Charge object, check `outcome.reason` for Stripe's classification and `decline_code` for the issuer's raw reason. In the Stripe dashboard, open any failed payment and the decline reason appears in the timeline alongside the issuer's response.
›What's the difference between decline_code and outcome.reason?
`outcome.reason` is Stripe's normalized classification (e.g. `generic_decline`, `card_declined`) that you should branch your logic on. `decline_code` is the more specific issuer reason (e.g. `insufficient_funds`, `do_not_honor`) when the issuer provides one. Always read both — the specific code drives the recovery playbook.
- Failed Payment Recovery: The Complete Guide for SaaSThe end-to-end system that turns these decline codes into recovered revenue.
- Involuntary Churn: Definition, Formula, and How to Cut ItWhy decline-code routing is the highest-ROI retention lever for SaaS.
- Dunning Management Software: 2026 Buyer's GuideEvaluation criteria for tools that automate decline-aware retries and recovery.
- Open playbook libraryEvery recoverable decline code mapped to a named play — signal, email, snooze.
