Stripe Decline Code Reference
Every common Stripe decline code, categorized as soft (retryable) or hard (don't retry, ask customer to update card), with realistic recovery rates and the right retry strategy. Built from production data across multiple SaaS billing systems.
Free interactive tools — run your own numbers in 30 seconds before you dig into the tables:
Billing leaking revenue? Get the $19 Stripe Billing Audit →
Why this matters
Stripe's default retry logic treats all decline codes roughly the same. That's wrong — and it's why most founders' Smart Retries recover ~35-40% when better-tuned retries recover 50-70%. The trick is segmenting by code: retry soft declines aggressively, don't waste cycles on hard ones, and stop emailing customers whose cards are stolen.
Soft declines (retry these)
These usually clear on retry within hours or days — bank holds, insufficient funds, temporary network issues. Recovery rates here are 30–50% if you retry on the right schedule.
| Code | Type | What it means | Recovery rate | Best retry strategy |
|---|---|---|---|---|
insufficient_funds | SOFT | Card has insufficient balance. | ~40% | Retry day 3, day 7. Many customers fund the card after payday. |
generic_decline | SOFT | Bank declined without specifying — often fraud-prevention false positive. | ~25% | Retry once on day 3. If second decline, send "contact your bank" email. |
do_not_honor | SOFT | Bank-level decline without specifics. Bank is being cautious. | ~22% | Retry day 4, day 9. Recovery often happens when customer travels back to home region. |
processing_error | SOFT | Temporary issue at processor level. | ~50% | Retry within 24h — most clear quickly. |
try_again_later | SOFT | Network or temporary system issue. | ~55% | Retry within 1h then within 24h. |
call_issuer | ACTION | Bank wants to verify the transaction with the cardholder. | ~30% | Don't retry blindly. Email customer asking them to authorize. |
fraudulent | ACTION | Bank's fraud system flagged this charge. | ~20% | Email customer immediately. Don't retry — second decline strengthens the fraud flag. |
incorrect_cvc | SOFT | CVC code mismatch. | ~10% | Don't retry. Send "update card" link — customer has to enter CVC again anyway. |
card_velocity_exceeded | SOFT | Too many recent transactions on this card. | ~25% | Retry day 2 — velocity limits reset on rolling windows. |
Hard declines (don't retry — update the card)
These will not clear with retries. Continuing to retry only damages your sender reputation and wastes email credibility. Send the customer to the Stripe Customer Portal or a card-update flow.
| Code | Type | What it means | Recovery rate | Action |
|---|---|---|---|---|
expired_card | HARD | Card past expiration date. | ~3% via retry | Skip retries. Send card-update email. Enable Stripe Card Account Updater — Stripe will auto-update this for ~30% of cards via card networks. |
card_declined (generic hard) | HARD | Bank declined and won't say why; often closed account. | ~5% | One retry only at day 3. After that, card-update only. |
lost_card | HARD | Customer reported card lost. | 0% | Never retry. Send card-update email immediately. |
stolen_card | HARD | Customer reported card stolen. | 0% | Never retry. Suspend subscription, send card-update email. |
pickup_card | HARD | Bank's instruction to merchant to physically retain the card (rare in CNP). | 0% | Never retry. Card-update only. |
restricted_card | HARD | Card has restrictions (region, currency, merchant category). | ~2% | Card-update or contact-customer flow. |
invalid_cvc | HARD | CVC repeatedly wrong (different from incorrect_cvc). | ~3% | Card-update flow. |
invalid_account | HARD | Account number invalid or closed. | 0% | Never retry. |
Related
Webhooks 400ing on you? Stripe webhook signature verification failed — fix per framework.
What to do with this
1. Audit your current retry schedule
If your retry logic doesn't branch on decline code, you're treating expired_card the same as insufficient_funds — wasting ~80% of retry attempts on permanent failures. Open your billing code and check whether the dispatcher reads failure_code from the Charge or Invoice object before deciding to retry.
2. Enable Stripe Card Account Updater
It's free. It auto-updates expired or replaced cards via Visa Account Updater + Mastercard Automatic Billing Updater. Approximately 30% of expired_card declines disappear with this enabled before your customer ever sees a dunning email. Most teams don't have this on. Stripe docs.
3. Separate dunning emails by code
Soft decline emails should be short, factual, and have a "retry in 24h" tone. Hard decline emails should explicitly say "your card needs updating" and link directly to your Stripe Customer Portal. Sending the same generic "payment failed" email to both buckets cuts your open rate roughly in half.
Want this analysis on YOUR actual Stripe data?
I run a one-shot audit that pulls your decline-code distribution, maps it against your current retry logic, and writes a remediation plan. $19 for the diagnostic, $49 if you want the recovery email templates + 30-day re-audit included.
See the audit + free calculator → Buy $19 audit directFAQ
What about authentication_required?
That's a 3DS / SCA challenge, not a true decline — the customer needs to confirm with their bank. Best handled with off_session=true retries and an "authenticate this payment" email link. Different flow than soft/hard.
How do I read the decline_code from a webhook?
On invoice.payment_failed, look at invoice.last_payment_error.decline_code. On charge.failed, look at charge.failure_code. Both fields are strings matching the codes above.
What recovery rate should I expect total?
Industry benchmark for native Stripe (Smart Retries + dunning emails + Card Account Updater): 25-40% of all involuntary churn. Top quartile with decline-code-aware retries + segmented dunning emails: 50-70%. Anything below 25% means something is broken in your setup — usually Smart Retries not enabled on legacy plans.
Why doesn't Stripe just do this automatically?
Smart Retries does some of it. But Stripe's logic is the same across all merchants — they can't be aggressive because some merchants have low margins where aggressive retry would harm. Tuning to your business is where the additional recovery comes from.
More Stripe billing references
- Stripe failed payment recovery — the complete playbook
- Involuntary churn — what it is and how to cut it
- Stripe webhook signature verification failed — fix per framework
- network_decline_code list decoded (05, 51, 59, 65...)
- Smart Retries not working? The legacy-plan trap
- Subscription proration explained — change plans without losing data