---
ref: workflow:failed-payment-recovery
title: Recover failed payments before the subscription cancels
author: thedogwiththedataonit
tools: [tool:stripe/list-subscriptions, tool:stripe/list-invoices, tool:resend/send-email, tool:slack/post-message]
tags: [capability:route-alerts, capability:send-email, capability:track-revenue, channel:chat, channel:email, has:api, has:cli, has:mcp, motion:retention]
updated: 2026-09-27
---

# Recover failed payments before the subscription cancels

Set up the tools below, then run the steps in order for the user, carrying each step's results into the next.

## Inputs

Ask the user for these before you start.

- `sender`: the address to send from, on a domain verified in Resend, e.g. billing@acme.example
- `stripe_emails_on`: whether Stripe already emails customers about failed payments, e.g. no; if yes, this workflow only escalates
- `escalate_amount`: the amount due that deserves a personal call, with its currency, e.g. 1,000 USD (Stripe's 100000 in cents)
- `cs_channel`: the Slack channel for escalations, e.g. #customer-success

## Set up

### Stripe (tool:stripe/list-subscriptions, tool:stripe/list-invoices)

Use the first option your agent supports.

#### MCP (official, remote)

Add this server to your agent's MCP settings, then sign in when asked.

```json
{ "mcpServers": { "stripe": { "url": "https://mcp.stripe.com" } } }
```

- List subscriptions: call the MCP tool `stripe_api_read` with `GET /v1/subscriptions`
- List invoices: call the MCP tool `stripe_api_read` with `GET /v1/invoices`

#### CLI (official)

Install the command and sign in with it, then confirm it runs.

```sh
npm install -g @stripe/cli
stripe --version
```

- List subscriptions: run `stripe subscriptions list`
- List invoices: run `stripe invoices list`

### Send an email (Resend, tool:resend/send-email)

Use the first option your agent supports.

Note: Pass an `Idempotency-Key` header (`--idempotency-key` on the CLI) so a retry never sends it twice; keys expire after 24 hours.

#### MCP (official, remote)

Add this server to your agent's MCP settings, then sign in when asked.

```json
{ "mcpServers": { "resend": { "url": "https://mcp.resend.com/mcp" } } }
```

Call the MCP tool `send-email`.

#### CLI (official)

Install the command, then confirm it runs.

```sh
npm install -g resend-cli
resend --version
```

Run `resend emails send`.

Set `$RESEND_API_KEY` in your environment first (get a key: https://resend.com/api-keys).

### Post a message (Slack, tool:slack/post-message)

Use the first option your agent supports.

Note: Needs the `chat:write` scope. Over MCP it posts as the signed-in user; the API and CLI post as the app's bot.

#### MCP (official, remote)

Add this server to your agent's MCP settings, then sign in when asked.

```json
{ "mcpServers": { "slack": { "url": "https://mcp.slack.com/mcp" } } }
```

Call the MCP tool `slack_send_message`.

#### CLI (official)

Install the command, then confirm it runs.

```sh
curl -fsSL https://downloads.slack-edge.com/slack-cli/install.sh | bash
slack --version
```

Run `slack api chat.postMessage`.

Set `$SLACK_BOT_TOKEN` in your environment first (get a key: https://api.slack.com/apps).

Before step 1, confirm access to each service with its cheapest read-only call, like a list or a search. Never send or change anything to test access.

## Steps

1. **Find past-due subscriptions** with List subscriptions (Stripe). List subscriptions with status `past_due`. Keep each subscription ID and customer ID.
2. **Get the unpaid invoices** with List invoices (Stripe). For each subscription, list its `open` invoices. Keep, per customer, `customer_name`, `customer_email`, and each invoice's `amount_due` and `currency` (in the currency's smallest unit), `next_payment_attempt` and `hosted_invoice_url`.
3. **Write the nudges** yourself. Skip this step and the next when `stripe_emails_on` is yes, so no one gets two emails. Otherwise draft one short, friendly email per customer, with a subject and a plain-text body: the payment did not go through, each `hosted_invoice_url` to pay or update the card, and the next retry date, or that no retries are left when `next_payment_attempt` is empty. Show the drafts to the user.
4. **Send** with Send an email (Resend). After the user approves, send each email from `sender` to its `customer_email`, one recipient per email. Keep each email's ID.
5. **Escalate the big ones** with Post a message (Slack). Post one message per customer who owes `escalate_amount` or more to `cs_channel`, with `customer_name`, the amount and the next retry date.

## Done when

- Every customer with a past-due subscription got an email, or has a note saying why not.
- `cs_channel` has one post per large account, and the user has the total amount due by currency.

## Rules

- Use only the services set up above. The read-only calls they need, like listing ids or polling for results, are fine.
- Ask the user before anything that sends messages, costs money, or changes data, and say how many records it touches. One approval covers a batch the user has seen.
- Never print API keys.
