How a dunning email gets sent
The retry does the recovering; the email does the asking. This post is about the message that goes out at each attempt — branded, in your voice, gentle on the first nudge and clear about the consequence by the last — with a one-tap link to update the card, sent through SES, and with bounces and complaints fed back so the system stops emailing a dead address.
Key takeaways
- A branded dunning email goes out at every retry attempt, from your verified SES sender — not a generic decline notice.
- One Bedrock Haiku 4.5 call writes the copy in your voice, escalating from a gentle nudge to a clear consequence.
- Every email carries a one-tap update-card link — a signed, single-use URL into the processor’s hosted card page.
- Bounces and complaints are fed back through an SES configuration set, so the system stops emailing a dead address.
- The email only ever asks and informs; clicking nothing in it charges anything — the retry does the charging.
The retry charges; the email asks
It’s worth being precise about the division of labour. The retry (Part 3) is what actually recovers the money — it asks the processor to charge the saved card again. The email does something different and just as important: it tells the customer what’s happening and gives them a way to help. Most failed renewals recover not because the third automatic retry finally cleared, but because somewhere between retries the customer read a polite email and updated their card. The email is the human-facing half of the recovery.
That’s why a generic, processor-default decline email is a wasted opportunity. It looks like spam, often lands in promotions, and gives no sense that a real business is behind it. The dunning email here goes out from your verified domain, with your logo, in your voice — so the customer recognises it as you and acts on it.
Copy that escalates gently
The four emails are not the same message sent four times. They escalate — carefully. The first is reassuring: cards fail all the time, no drama, here’s the link. The middle two are a touch more direct. The last one is clear about the consequence without being a threat: if this final attempt doesn’t go through, access will pause, and here’s exactly how to avoid that. Getting that tone right four times, in your brand voice, is the one place a model earns its place in this system.
dunr-mailer reads a short HTML template and a tone brief from the dunr-assets S3 bucket, then makes a single Bedrock Claude Haiku 4.5 call — a few hundred tokens — passing the attempt number, the plan name, the amount, and the retry date, and asking for subject and body copy at the right escalation level. The model writes the words; it does not decide anything. The amount, the dates, the link, and whether to send at all are all fixed by the deterministic code around it. If the Bedrock call fails or times out, the mailer falls back to a plain templated version with the same facts — the customer always gets the email, even if it’s the unstyled-copy version.
The one-tap update-card link
The single most useful thing in the email is the button that lets the customer fix their card in two taps. The system never handles card details itself — that would drag it into PCI scope for no reason. Instead, the link is a signed, single-use URL that lands the customer on the processor’s own hosted card-update page (a billing portal session or a setup link). dunr-mailer builds it with an HMAC over the customer id, the subscription id, and an expiry, keyed from a secret in Secrets Manager, so a forwarded or leaked link can’t be reused after it’s spent or expired. The customer taps it, updates their card on the processor’s secure page, and the processor fires a payment_method.updated webhook — which (Part 2) tells the system to retry now rather than waiting for the next scheduled slot.
Not emailing a dead address forever
Sending mail responsibly means listening to what comes back. The SES configuration set dunr-mail routes bounce and complaint notifications to an SNS topic, and a small handler writes the affected address to a suppression list. If a customer’s email hard-bounces (the mailbox doesn’t exist) or they mark a dunning email as spam, the system stops sending to that address — the retries still run silently, but no more email goes to a place that doesn’t want it. This protects your sender reputation, which is what keeps the next customer’s dunning email out of their spam folder. It’s also why the system is outbound-only on SES: there’s no inbound mail to receive, unlike some other systems in this series.
Design rules that shaped the email
- It looks like you. Verified domain, your logo, your voice — never a generic decline notice.
- It escalates gently. Reassuring first, clear about the consequence last; the model writes the words, not the facts.
- The model only writes copy. Amounts, dates, the link, and whether to send are all deterministic.
- No cards, ever. The update-card link is a signed, single-use URL to the processor’s own hosted page.
- Listen to bounces. A configuration set feeds a suppression list, so the system never mails a dead address.
- Always falls back. If Bedrock is unavailable, a plain templated email with the same facts still goes out.
Next: the one thing the customer actually feels — how access pauses after the final failed retry, and how it’s restored the instant a payment succeeds or a card is updated.
All posts