Step 1 — Resume the ledger
Read .kortix/memory/payment-recovery-ledger.md. For every subscription
already tracked, note its current rung, the timestamp of its last action, and
its next-eligible-escalation time. Anything not yet on the ledger is a
candidate for Step 2.
Step 2 — Pull failed invoices and subscription state from Stripe
Query Stripe for invoices in a failed/past-due state and their parent
subscriptions. For each:
- New failure (not on the ledger) → start at rung 1 (smart retry).
- Already on the ledger → check whether
now >= next-eligible time; if
not, leave it alone this run.
- No longer failing (invoice paid, subscription active/current) → go to
Step 6 (close out), regardless of rung.
Step 3 — The ladder
| Rung | Name | Action | Minimum wait before this rung |
|---|
| 1 | Smart retry | Retry the failed charge against the existing payment method via Stripe (off-session), no email sent yet | Immediately on first failure |
| 2 | Payment reminder | Friendly email: the charge didn't go through, here's the amount and a pay link | 24h after rung 1 if still failing |
| 3 | Update-your-card notice | Firmer email naming the likely cause (expired/declined card) with a direct link to update the payment method | 48h after rung 2 if still failing |
| 4 | Final notice | Clear, professional email stating this is the last automated reminder before the account needs manual attention | 72h after rung 3 if still failing |
A subscription advances at most one rung per run, and only once its
minimum wait has elapsed. Never send two rungs' worth of email in the same
run, and never re-send the same rung.
Step 4 — Send the rung's email
Send the exact email for the current rung via {{dunning_channel}}, addressed
to the subscription's billing contact. Keep the tone matched to the rung —
rung 2 is a nudge, rung 4 is unambiguous but still professional, never
threatening. Every send is logged to the ledger with the rung and timestamp.
Step 5 — Rung 4 is the ceiling
Once a subscription has received the final notice and the invoice is still
unpaid, do not create a rung 5. Mark it awaiting-human on the ledger and
surface it in the Slack summary every run until a person acts or it pays.
Never cancel the subscription, issue a credit, or process a refund — that
decision, and the action, belongs to a human.
Step 6 — Close out on payment
If a tracked subscription's invoice has been paid (checked fresh from Stripe
every run, not assumed), mark it recovered on the ledger with the rung it
was on when it cleared, stop sending it anything, and report it as a recovery
in the summary.
Step 7 — Post the summary
Post one message per run to {{alert_channel}}: subscriptions that advanced a
rung (with new rung), subscriptions newly at awaiting-human, and
subscriptions that recovered since the last run. Omit subscriptions with no
change.
Step 8 — Update the ledger
Write the full current state back to .kortix/memory/payment-recovery-ledger.md
(see <ledger-format>) before ending the turn.