# How we win back subscription payments that fail

Every hour, an agent walks each failed Stripe invoice up a four-step ladder of retry and reminders, keeps the revenue team posted in Slack, and goes quiet the second the customer pays.

Canonical page: https://themachine.myapps.ai/use-cases/payment-recovery

Plenty of customers who "churn" never meant to go anywhere. Their card hit its
expiry date. Their bank got nervous about a charge. The payment bounced for a
reason unrelated to how much they like your product. Nobody notices, and a few
weeks later the subscription is simply gone. That's involuntary churn, and you
can win most of it back if you spot the failure early and follow up the same
way every time.

So we put an agent on Machine in charge of it. It keeps an eye on each Stripe
invoice that fails and moves it up a set ladder: smart retry, then a reminder,
then a "please update your card" note, then a final notice. When the customer
pays, it stops right there. Cancelling, crediting and refunding are never its
call. A person on the team makes those.

- **Team:** Machine
- **Runs on:** A cron every hour, the same session each time
- **Connected systems:** Stripe · Slack · Email
- **Mode:** Retries and reminder emails only · it escalates, it never cancels

## The problem

Think of a declined charge as a clock starting, not a single moment. Stripe
does try again on its own for a while. But its schedule is one size for
everyone. It has no idea a customer's bank just put a fraud hold on their
card, or that someone else stopped reading email a week ago. Send everyone the
same flat nudge and you lose people who would have paid with a better-timed
ask. Send nothing and the plan expires, and support only hears about it when
an angry customer writes in asking where their access went.

The usual fixes forget things. Stripe's own retries help, yet they never pull
in a person, never get firmer as days go by, and never warn revenue which
accounts are close to dropping off. A spreadsheet of who got what email is out
of date by Friday. And an automatic "cancel after so many failures" rule gives
you a tidy list by throwing away customers you could have kept.

## What we built

Once an hour, a cron on Machine wakes up a single **persistent session**. That
session holds a ledger with one line per subscription, showing which rung each
failed invoice has reached: smart retry, reminder, update-your-card, or final
notice. On each pass the agent pulls failed invoices and subscription status
from Stripe. If a subscription has waited long enough, it moves up a rung and
the customer gets that rung's email. Then the agent drops a rundown of
everything still open into the revenue team's Slack channel. The minute a
subscription pays, its line on the ledger is closed and nobody emails that
customer again.

## How it works

### Wake the same session every hour

An hourly **cron trigger** starts the run. Most of our agents get a brand new
session each time, but this one goes back to the **same session** on every
pass. Its memory is where the ledger lives: each subscription's current rung
and the time it's due to move. That's how the agent knows exactly what has
already gone out, so it never sends a step twice or jumps one.

### Pull the failures from Stripe

Stripe is reached through a scoped **connector**. Machine holds the key on its
own servers, so the model never sees a raw key. From there the agent reads
failed invoices, subscription status and the state of each payment method.
That's all it needs to choose a next move, and the same read picks up brand
new failures alongside subscriptions already partway up the ladder.

### Hand it the ladder as a playbook

The steps and their timing are written down as a **skill**, the agent's
playbook. Rung one is a smart retry. Rung two is a friendly reminder about the
payment. Rung three is a firmer note asking the customer to update their card,
with a link to do it. Rung four is the final notice. Each rung has to wait a
minimum time after the last, so nobody gets two emails on the same day.

### Email the customer, then tell revenue

The agent sends whichever **email** matches the rung, straight to the
customer, with no approval step. A retry or a dunning email is expected and
easy to undo, so holding it for sign-off would only slow recovery. It also
posts to **Slack**: which accounts moved up today, which are now on final
notice, and which ones paid since last time.

### Stop at the top rung

The final notice is as far as the agent goes. Getting there does not cancel
anything, refund anything or credit anything. The account simply waits,
flagged in the Slack rundown, for someone on revenue to pick the next step.
The Stripe and email credentials are stored encrypted in the Secrets Manager,
added at run time, and only handed to the agents you grant them to.

### Close it out as soon as money lands

On every pass the agent checks whether anything on the ladder has paid in the
meantime. If it has, the ledger marks it recovered, the emails stop, and the
Slack rundown lists it as a win. No follow-up, no stray reminder a day later.

> **The pattern**
> One **reusable session**, woken every hour by a cron, keeps a ledger with a
> line per subscription. A Stripe **connector** that mostly reads tells it
> what changed. A **skill** holds the escalation ladder. It can only ever
> write **email** and **Slack**. At the final notice it stops, and
> cancellations, credits and refunds stay with a person every time.

## Guardrails

Retrying a charge and sending an email are things the agent may do alone.
Anything that moves money or ends a subscription is not:

- **Retries and reminders, nothing else.** It can try a failed charge again
  and send the four ladder emails. Cancelling, crediting and refunding always
  go to a person. There are no exceptions.
- **A rung at a time.** The playbook sets a minimum gap between rungs, so no
  customer hears from us twice in quick succession. Each subscription climbs
  on its own clock instead of the whole list moving together.
- **Paid means done.** When an invoice clears, its ledger line closes and the
  emails end.
- **Locked-down keys.** The Stripe credential is stored encrypted in the
  Secrets Manager, added to the sandbox at run time, and only handed to the
  agents you grant them to.
- **All of it lives in code.** The ladder, the waiting times and what the
  agent is allowed to touch sit as files in the repo. They're versioned, and
  you change them with a reviewed **change request**, not a toggle on a
  dashboard.

## The outcome

- **Hourly:** How often each failed invoice is checked against its rung
- **4 rungs:** Smart retry, reminder, update your card, final notice
- **0:** Subscriptions cancelled, credited or refunded by the agent

A failed payment used to sit untouched until the plan quietly ran out. Now it
starts climbing a steady, firmer-each-time ladder the hour it fails, and the
revenue team can see what's open and what's waiting on them. The agent does
the chasing. People still decide when it's time to let a customer go.
