Build a Stripe Dunning Sequence That Recovers Revenue, Not Just Reminders
Stripe retries the card. You still have to reach the customer. A 3-step email + Telegram sequence recovers more than a single generic invoice email.
1. Stripe retries the card. That is not dunning.
Smart Retries will hit the card again on a better day. They will not tell the customer their Visa expired. If the only email they get is Stripe's generic invoice, a lot of them ignore it — and you lose a month of MRR that was already earned.
A real dunning sequence is a conversation: instant notice, a second nudge when Stripe retries, a last chance before access drops. The trigger is already in your stack — invoice.payment_failed fires on every attempt. You just have to map attempt 1 / 2 / 3 to different copy and channels.
2. The 3-step sequence that actually converts
- Step 1 — instant. Email the customer with the hosted invoice link. Ping yourself on Telegram so you see the miss in real time.
- Step 2 — next Stripe retry (~24h). Email again, shorter and more direct. Same pay link. Do not invent a cron.
- Step 3 — last retry (~3 days). Final notice. If they pay,
invoice.payment_succeededmarks the row recovered and the sequence stops.
That mapping is one function. Stripe's attempt_count is the step index:
function pickStep(flow, attemptCount) {
const enabled = flow.steps.filter((s) => s.enabled);
const idx = Math.max(0, Math.min(attemptCount - 1, enabled.length - 1));
return enabled[idx];
}
// attempt 1 → instant email + Telegram
// attempt 2 → 24h email
// attempt 3 → final notice
3. What to send (and what not to)
Put the amount in the subject. Put a single button in the body. Send them to Stripe's hosted invoice — never collect a card on your own page. Founder alerts belong on Telegram, not in the same inbox as the customer.
Pause must actually pause. If a founder hits Pause on a row, the next Stripe retry for that invoice should skip. Otherwise the sequence is theater.