Account deleted
Confirmation that it is gone, what is left, and for how long.
Account · 7 placeholders
The job
This is the last email the account will get, and it does two jobs at once. It confirms to the person who asked that the deletion happened, and it warns the person who did not ask that somebody deleted their account.
How this one works
The retention window is the content. A deletion email that says "your data has been removed" without a date leaves the reader unable to tell whether an export is still possible, which is the single question they will write back with.
What you need
A key, a domain, and the two or three things only your app knows. Nothing here takes longer than the email did to read.
When it sends
Once, when the deletion has actually run rather than when it was requested. Send it to the address on the account even though that address no longer signs in anywhere.
A sending domain
Verified with three DNS records, or send from onboarding@emails.sh until yours is ready.
A retention window you honour
The real number of days before the data is unrecoverable, from your own deletion job.
A recovery path while it lasts
One address a person can write to inside the window, staffed by somebody who can undo it.
The email
Table layout, inline styles, no stylesheet, no web font, no JavaScript. It renders the same in Outlook, Gmail, and Apple Mail, and the plain-text version goes with it every time.
Your {{PRODUCT_NAME}} account has been deleted<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<!-- Stops iOS Mail resizing the text on its own. -->
<meta name="x-apple-disable-message-reformatting">
<title>Account deleted</title>
</head>
<body style="margin:0;padding:0;width:100%;background-color:#f4f4f5;">
<div style="display:none;max-height:0;max-width:0;opacity:0;overflow:hidden;mso-hide:all;font-size:1px;line-height:1px;color:#f4f4f5;">Recoverable until {{PURGE_DATE}}.            </div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="width:100%;background-color:#f4f4f5;">
<tr><td align="center" style="padding:24px 12px;">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:100%;background-color:#ffffff;border:1px solid #e4e4e7;">
<tr><td style="padding:32px;">
<h1 style="margin:0 0 16px;font-family:Helvetica,Arial,sans-serif;font-size:21px;line-height:28px;font-weight:600;color:#18181b;">Your account has been deleted</h1>
<p style="margin:0 0 16px;font-family:Helvetica,Arial,sans-serif;font-size:15px;line-height:24px;mso-line-height-rule:exactly;color:#3f3f46;">The {{PRODUCT_NAME}} account for {{ACCOUNT_EMAIL}} was deleted on {{DELETED_DATE}}. Signing in is no longer possible and the data is out of the product.</p>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:0 0 20px;border-top:1px solid #e4e4e7;"><tr><td style="padding:10px 0;border-bottom:1px solid #e4e4e7;font-family:Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#71717a;" width="45%">Account</td><td align="right" style="padding:10px 0;border-bottom:1px solid #e4e4e7;font-family:Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#18181b;font-weight:600;">{{ACCOUNT_EMAIL}}</td></tr><tr><td style="padding:10px 0;border-bottom:1px solid #e4e4e7;font-family:Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#71717a;" width="45%">Deleted</td><td align="right" style="padding:10px 0;border-bottom:1px solid #e4e4e7;font-family:Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#18181b;font-weight:600;">{{DELETED_DATE}}</td></tr><tr><td style="padding:10px 0;border-bottom:1px solid #e4e4e7;font-family:Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#71717a;" width="45%">Recoverable until</td><td align="right" style="padding:10px 0;border-bottom:1px solid #e4e4e7;font-family:Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#18181b;font-weight:600;">{{PURGE_DATE}}</td></tr><tr><td style="padding:10px 0;border-bottom:1px solid #e4e4e7;font-family:Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#71717a;" width="45%">Billing</td><td align="right" style="padding:10px 0;border-bottom:1px solid #e4e4e7;font-family:Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#18181b;font-weight:600;">{{BILLING_NOTE}}</td></tr></table>
<p style="margin:0 0 16px;font-family:Helvetica,Arial,sans-serif;font-size:15px;line-height:24px;mso-line-height-rule:exactly;color:#3f3f46;">Backups are kept for {{RETENTION_DAYS}} days. Until {{PURGE_DATE}} we can still restore the account if this was a mistake. After that date nothing can be recovered by anyone.</p>
<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:0 0 20px;"><tr><td bgcolor="#18181b" style="background-color:#18181b;"><a href="mailto:{{SUPPORT_EMAIL}}" style="display:inline-block;padding:13px 24px;font-family:Helvetica,Arial,sans-serif;font-size:15px;line-height:20px;font-weight:600;color:#ffffff;text-decoration:none;">Write to support</a></td></tr></table>
<p style="margin:0 0 12px;font-family:Helvetica,Arial,sans-serif;font-size:13px;line-height:20px;mso-line-height-rule:exactly;color:#71717a;">If you did not ask for this, write to <a href="mailto:{{SUPPORT_EMAIL}}" style="color:#18181b;text-decoration:underline;">{{SUPPORT_EMAIL}}</a> today. Somebody with access to the account requested the deletion, and the window to undo it is short.</p>
</td></tr>
</table>
<table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:100%;">
<tr><td style="padding:20px 32px 0;">
<p style="margin:0 0 8px;font-family:Helvetica,Arial,sans-serif;font-size:12px;line-height:18px;color:#71717a;">{{PRODUCT_NAME}} sent this to the address on the deleted account.</p>
</td></tr>
</table>
</td></tr>
</table>
</body>
</html>How it works
One send, start to finish. Every message is in your logs with its delivery events.
Somebody asks for deletion
You confirm it in the product, then queue the job.
The job runs
Live data goes first. Backups age out on their own schedule.
You send this
After the deletion, with the purge date the backup schedule actually produces.
The window closes
On the purge date the account is unrecoverable, exactly as this email said.
Code
The same send three ways. Each one fills the placeholders, reads the two bodies, and posts them to POST /v1/emails.
import { readFileSync } from 'node:fs';
import { Emailssh } from '@emails.sh/sdk';
const mail = new Emailssh({ apiKey: process.env.EMAILSSH_API_KEY });
// What changes per send. In your app these come from the row you just
// wrote, not from literals.
const vars: Record<string, string> = {
PRODUCT_NAME: 'Acme',
ACCOUNT_EMAIL: 'ada@example.com',
DELETED_DATE: '31 July 2026',
RETENTION_DAYS: '30',
PURGE_DATE: '30 August 2026',
BILLING_NOTE: 'Your Team subscription was cancelled and will not renew',
SUPPORT_EMAIL: 'support@acme.com',
};
const fill = (body: string) =>
Object.entries(vars).reduce(
(out, [name, value]) => out.replaceAll('{{' + name + '}}', value),
body
);
const { id } = await mail.send({
from: 'Acme <onboarding@emails.sh>',
to: ['ada@example.com'],
subject: fill('Your {{PRODUCT_NAME}} account has been deleted'),
html: fill(readFileSync('email.html', 'utf8')),
text: fill(readFileSync('email.txt', 'utf8')),
replyTo: vars.SUPPORT_EMAIL,
tags: { type: 'account-deleted' }
});
// Queued, not delivered. Ask for the id later, or take the webhook.
console.log(id);Questions
Why send anything to a deleted account?
Because deletion is the most damaging action an attacker can take with a stolen session, and this email is the only signal the real owner gets. It is also the record that proves you acted on a request.
Is it safe to keep the address after deletion?
Keep the minimum needed to send this and to honour a suppression list, and say so in your privacy notice. Deleting the address entirely means you can no longer prove the deletion happened or stop mailing them again.
What if the deletion is immediate and total?
Then say that, and drop the recovery paragraph. What matters is that the email describes the retention your systems actually have, not a comfortable number.
Send this one in the next five minutes.
Before your domain is verified you can send from onboarding@emails.sh, so the first send needs a key and nothing else. Verification is three DNS records and one screen.
Every message has delivery events you can read: accepted, delivered, bounced, complained. No support ticket to find out whether it arrived.