Stripe webhook event
invoice.payment_failed
What the Stripe invoice.payment_failed webhook means, when it fires, what it carries, and the recovery workflow it should trigger.
What this event means
Stripe attempted to charge the saved card against an invoice and the bank declined. The invoice is now in open status, the subscription is in past_due, and the customer doesn't know yet unless you tell them. This is your recovery window.
When it fires
- On the initial failed charge for a subscription invoice.
- On every subsequent failed retry, with
attempt_countincremented. - On manual charge attempts to an open invoice that also fail.
What data it contains
The invoice object is the payload. Key fields for recovery:
customer— the customer ID, for looking up email and account.subscription— the subscription ID, useful for tying the failure to the plan.attempt_count— which attempt this is. Use it to escalate copy.amount_dueandcurrency— what was owed.next_payment_attempt— when Stripe will retry next.payment_intent— fetch this for the decline code (last_payment_error.decline_code).
Example event payload
{
"id": "evt_1Q...",
"type": "invoice.payment_failed",
"data": {
"object": {
"id": "in_1Q...",
"customer": "cus_PaA...",
"subscription": "sub_PaA...",
"attempt_count": 1,
"amount_due": 9900,
"currency": "usd",
"next_payment_attempt": 1758500000,
"status": "open",
"payment_intent": "pi_3Q..."
}
}
}What action to trigger
- On
attempt_count == 1: send a polite, plain-text email with a hosted invoice link. - On
attempt_count == 2: send a short reminder, no new copy bloat. - On
attempt_count >= 3: send the final before-pause email and surface an in-app banner. - Fetch the linked payment intent to look up the decline code and tailor the message (expired card vs do-not-honor vs 3DS).
Sample recovery workflow
POST /webhooks/stripe ├─ verify signature ├─ if type == invoice.payment_failed: │ ├─ load customer + subscription │ ├─ fetch payment_intent.last_payment_error │ ├─ pick template by decline_code + attempt_count │ ├─ send via your transactional email provider │ └─ log event for recovery dashboard └─ return 200
When to use this page
Use this as the reference when wiring up your own dunning system or when debugging why a customer's subscription went past_due without anyone noticing. For other Stripe events, see the Stripe Webhook Event Decoder.
What to do next
Building this yourself is a couple of weeks of work plus ongoing maintenance. ChurnNote already listens for invoice.payment_failed, ties it to the customer, sends the right email per decline reason, and tracks recovered MRR.
Frequently Asked Questions
What does invoice.payment_failed mean?
It is the Stripe webhook event that fires whenever a payment attempt against an invoice fails. For subscriptions, that means a recurring charge did not succeed. The event carries the invoice, customer, subscription, the attempt count, and the decline reason on the underlying payment intent.
When does it fire?
On every failed attempt. If Stripe's smart retries try the charge four times, you will receive four invoice.payment_failed events for the same invoice, each with attempt_count incremented. Treat the first one as 'send the recovery email' and later ones as 'escalate'.
Does the customer get notified by Stripe?
Only if you have enabled Smart Retries email automation in the Stripe Billing settings. Many SaaS founders leave this off because the default copy is brand-less and impersonal. Either turn it on or send your own.
What's the relationship to customer.subscription.deleted?
If all retries fail and the configured grace period expires, Stripe fires customer.subscription.deleted to cancel the subscription. invoice.payment_failed is your recovery window before that happens.
How ChurnNote helps
ChurnNote listens for failed payment events and starts the recovery workflow for you.
ChurnNote connects to Stripe or Lemon Squeezy and automatically captures cancellation reasons, recovers failed payments, and queues win-back emails. So you stop losing revenue silently.
Start recovering churnNext step
Decode every Stripe churn event in one place
The Stripe Webhook Event Decoder covers cancellations, refunds, and trial-end events alongside payment failures.
Related tools