Email templates
Launch-ready first drafts of every email an app sends itself, not only sign-up and password reset: the welcome, the invite, notifications, the digest, receipts and invoices, failed payments, trials, plan changes, data exports, account deletion and security notices. Each one already passes Flowproof’s checks for its kind.
Get them from your agent
Your agent asks Flowproof for the drafts with get_email_templates, fills in your product’s name, support address and button colour, and writes them into your app next to the code that sends them:
Use Flowproof's get_email_templates to write the emails this app sends that don't exist yet (welcome, invites, notifications, billing, account and security notices). Use our product name and support address, match how the app already sends email, and follow each template's send_when.It can ask for all of them or only some (kinds: ["invite", "receipt"]), as email HTML with a plain-text part or as React Email components. The copies on this page say [Your product] and [your support email] where yours go.
How the templates work
| Part | What it is |
|---|---|
| Placeholders | Mustache: {{inviter_name}} is a value, {{#line_items}}…{{/line_items}} repeats per item, {{#has_payment_method}} shows only when true. Handlebars, Postmark, SendGrid dynamic templates and Mailgun templates read them as they are; with Resend, nodemailer or SES, render them with a Mustache library or use the React Email version. |
| HTML | Email-client HTML: tables, inline styles, 520px wide, a button that still draws in Outlook, and the link written out under it. |
| Plain text | Send it with the HTML (multipart/alternative). An email with no text part is graded down, and some people read only that. |
| React Email | A .tsx component per email with typed props (the placeholders, in camelCase), a subject() helper and preview props. Install @react-email/components in your app. |
| When to send | Each template says when to send it and when not to. They matter as much as the words. |
What each one is built to pass
These are the checks a run grades each kind of email on, and each draft is written to meet them once its placeholders are filled in (Flowproof’s tests fill each one and run every check on it):
| What it has to have | |
|---|---|
| Invite | Who invited them and to what, in the subject and the first line; an accept link; when it expires. |
| Notifications | A link to the exact item, not the home page; a way to turn these emails off. |
| Digest | Something in it (never sent empty); a way to turn it off, and a one-click List-Unsubscribe header. |
| Receipt | The amount with its currency; a receipt the customer can download. |
| Invoice | The amount due with its currency, and when it is due. |
| Payment failed | The amount; a button straight to where the card is updated. |
| Trial ending | The date it ends, and what happens then. |
| Security notices | When it happened, with the timezone; what to do if it wasn’t them. |
The templates
Welcome
Graded aswelcome- Subject
- Welcome to [Your product]
- Files
- emails/welcome.html, emails/welcome.txt, emails/WelcomeEmail.tsx
When to send: Once, after the email address is confirmed (not at sign-up, or it races the verification email). Send it from an address that accepts replies, or set Reply-To to one: "just reply" is a promise.
Preview
Placeholders (2)
Welcome placeholders Placeholder What goes in it Example {{email}}The recipient's email address. ann@example.com {{get_started_url}}Where a new user should land first: the first step of onboarding, or the dashboard. https://app.example.com/start Plain-text part
emails/welcome.txtWelcome to [Your product] Your [Your product] account is ready. You signed up as {{email}}. The quickest way to get going is the first step below. It takes a couple of minutes. Get started: {{get_started_url}} Stuck, or not sure [Your product] is right for you? Just reply to this email or write to [your support email]. Didn't sign up? Tell us and we'll close the account. -- [Your product] Questions? Write to [your support email].Invite
Graded asinvite- Subject
- {{inviter_name}} invited you to {{workspace_name}} on [Your product]
- Files
- emails/invite.html, emails/invite.txt, emails/InviteEmail.tsx
When to send: When someone invites an address. Re-sending an invite should send this again with a fresh expiry, not a second, different email.
Preview
Placeholders (5)
Invite placeholders Placeholder What goes in it Example {{inviter_name}}The inviter's display name. Fall back to their email address, never to "Someone" or "A teammate". Alice Chen {{workspace_name}}The team, workspace, project or document the email is about. Acme Design {{invite_expires_at}}When the invite stops working. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). October 5, 2026 {{inviter_email}}The inviter's email address, so the recipient can check who it is. alice@example.com {{accept_url}}The accept link, carrying the invite token. It should land on sign-up (or sign-in) with the invite already attached. https://app.example.com/invite/7Hq2xLp Plain-text part
emails/invite.txt{{inviter_name}} invited you to {{workspace_name}} {{inviter_name}} ({{inviter_email}}) invited you to join {{workspace_name}} on [Your product]. Accept the invite to create your account, or sign in if you already have one. You will land straight in {{workspace_name}}. Accept invite: {{accept_url}} This invitation expires on {{invite_expires_at}}. After that, ask {{inviter_name}} for a new one. Don't know {{inviter_name}}? You can ignore this email: nobody gets access unless you accept. -- [Your product] Questions? Write to [your support email].Notification: new comment
Graded asnotification- Subject
- {{actor_name}} commented on {{item_title}}
- Files
- emails/notification_comment.html, emails/notification_comment.txt, emails/NotificationCommentEmail.tsx
- Headers
- List-Unsubscribe: <{{one_click_unsubscribe_url}}> List-Unsubscribe-Post: List-Unsubscribe=One-Click
When to send: For each new comment on something the recipient follows, but never for their own comments. Batch bursts (several comments within a few minutes become one email) and stop when they have opened the item since.
Preview
Placeholders (6)
Notification: new comment placeholders Placeholder What goes in it Example {{actor_name}}Who did the thing: a person's display name. Alice Chen {{item_title}}What it happened to: the task, document or request, by its title. Homepage redesign {{comment_excerpt}}The start of the comment as plain text, cut at about 200 characters. Can we try the darker header before Friday? {{item_url}}A deep link to the exact item (and comment), e.g. /projects/42/tasks/181#comment-9, never the home page. Send signed-out readers through sign-in and back to it. https://app.example.com/projects/42/tasks/181#comment-9 {{manage_notifications_url}}Your notification settings, ideally opened at the switch for this kind of email. https://app.example.com/settings/notifications {{one_click_unsubscribe_url}}An HTTPS endpoint that turns off this kind of email when it receives a POST (RFC 8058 one-click). Used in the List-Unsubscribe header, not in the body. https://app.example.com/api/unsubscribe/comments/u_81Kd2 Plain-text part
emails/notification_comment.txt{{actor_name}} commented on {{item_title}} > {{comment_excerpt}} Reply to the comment: {{item_url}} -- [Your product] Questions? Write to [your support email]. You're getting this because you follow {{item_title}} in [Your product]. Manage notification settings: {{manage_notifications_url}}Notification: approval request
Graded asnotification- Subject
- {{actor_name}} needs your approval: {{item_title}}
- Files
- emails/notification_approval.html, emails/notification_approval.txt, emails/NotificationApprovalEmail.tsx
- Headers
- List-Unsubscribe: <{{one_click_unsubscribe_url}}> List-Unsubscribe-Post: List-Unsubscribe=One-Click
When to send: When an approval is assigned to the recipient. Send the result (approved or rejected) to the requester with the status-change draft.
Preview
Placeholders (7)
Notification: approval request placeholders Placeholder What goes in it Example {{actor_name}}Who did the thing: a person's display name. Alice Chen {{item_title}}What it happened to: the task, document or request, by its title. Homepage redesign {{approval_summary}}One line on what needs approving. Invoice #1042 for $1,200.00 USD {{approval_due}}When the approval is needed by. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). October 2, 2026 {{item_url}}A deep link to the exact item (and comment), e.g. /projects/42/tasks/181#comment-9, never the home page. Send signed-out readers through sign-in and back to it. https://app.example.com/projects/42/tasks/181#comment-9 {{manage_notifications_url}}Your notification settings, ideally opened at the switch for this kind of email. https://app.example.com/settings/notifications {{one_click_unsubscribe_url}}An HTTPS endpoint that turns off this kind of email when it receives a POST (RFC 8058 one-click). Used in the List-Unsubscribe header, not in the body. https://app.example.com/api/unsubscribe/comments/u_81Kd2 Plain-text part
emails/notification_approval.txt{{actor_name}} needs your approval {{actor_name}} asked you to approve {{item_title}}. What: {{approval_summary}} Needed by: {{approval_due}} Review and approve: {{item_url}} -- [Your product] Questions? Write to [your support email]. You're getting this because you're an approver in [Your product]. Manage notification settings: {{manage_notifications_url}}Notification: status change
Graded asnotification- Subject
- {{item_title}} is now {{new_status}}
- Files
- emails/notification_status.html, emails/notification_status.txt, emails/NotificationStatusEmail.tsx
- Headers
- List-Unsubscribe: <{{one_click_unsubscribe_url}}> List-Unsubscribe-Post: List-Unsubscribe=One-Click
When to send: When the status of something the recipient follows changes, and the recipient did not change it themselves.
Preview
Placeholders (7)
Notification: status change placeholders Placeholder What goes in it Example {{item_title}}What it happened to: the task, document or request, by its title. Homepage redesign {{new_status}}The status after the change. Approved {{actor_name}}Who did the thing: a person's display name. Alice Chen {{old_status}}The status before the change. In review {{item_url}}A deep link to the exact item (and comment), e.g. /projects/42/tasks/181#comment-9, never the home page. Send signed-out readers through sign-in and back to it. https://app.example.com/projects/42/tasks/181#comment-9 {{manage_notifications_url}}Your notification settings, ideally opened at the switch for this kind of email. https://app.example.com/settings/notifications {{one_click_unsubscribe_url}}An HTTPS endpoint that turns off this kind of email when it receives a POST (RFC 8058 one-click). Used in the List-Unsubscribe header, not in the body. https://app.example.com/api/unsubscribe/comments/u_81Kd2 Plain-text part
emails/notification_status.txt{{item_title}} is now {{new_status}} {{actor_name}} moved {{item_title}} from {{old_status}} to {{new_status}}. View {{item_title}}: {{item_url}} -- [Your product] Questions? Write to [your support email]. You're getting this because you follow {{item_title}} in [Your product]. Manage notification settings: {{manage_notifications_url}}Digest
Graded asdigest- Subject
- Your {{period_label}} summary: {{item_count}} updates in {{workspace_name}}
- Files
- emails/digest.html, emails/digest.txt, emails/DigestEmail.tsx
- Headers
- List-Unsubscribe: <{{one_click_unsubscribe_url}}> List-Unsubscribe-Post: List-Unsubscribe=One-Click
When to send: On its schedule, ONLY when there is something in it: count the items first and skip the send when the count is 0. This template has no empty state on purpose; an email that says "nothing new" teaches people to ignore the digest.
Preview
Placeholders (8)
Digest placeholders Placeholder What goes in it Example {{period_label}}How often the digest goes out, as an adjective. weekly {{item_count}}How many items are in this digest. Never 0: skip the send instead. 3 {{workspace_name}}The team, workspace, project or document the email is about. Acme Design {{since_date}}The start of the period the digest covers. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). September 21, 2026 {{#digest_items}}…{{/digest_items}}The items, newest first. Cap it (10 is plenty) and link the rest. Each item has: actor (Who did it), action (What they did, as a verb phrase ("commented on")), title (What they did it to), url (A deep link to that item). 3 items {{workspace_url}}The workspace itself, for everything the digest did not list. https://app.example.com/projects/42 {{manage_notifications_url}}Your notification settings, ideally opened at the switch for this kind of email. https://app.example.com/settings/notifications {{one_click_unsubscribe_url}}An HTTPS endpoint that turns off this kind of email when it receives a POST (RFC 8058 one-click). Used in the List-Unsubscribe header, not in the body. https://app.example.com/api/unsubscribe/comments/u_81Kd2 Plain-text part
emails/digest.txtYour {{period_label}} summary for {{workspace_name}} Here is what happened in {{workspace_name}} since {{since_date}}: {{#digest_items}} - {{actor}} {{action}} {{title}} {{url}} {{/digest_items}} Open {{workspace_name}}: {{workspace_url}} -- [Your product] Questions? Write to [your support email]. You get this {{period_label}} summary because digests are on for {{workspace_name}} in [Your product]. Manage notification settings: {{manage_notifications_url}}Receipt
Graded asreceipt- Subject
- Your [Your product] receipt: {{amount}}
- Files
- emails/receipt.html, emails/receipt.txt, emails/ReceiptEmail.tsx
When to send: After each successful charge, once. If your payment provider already sends receipts, send this one or theirs, not both.
Preview
Placeholders (7)
Receipt placeholders Placeholder What goes in it Example {{amount}}The amount with its currency, formatted for people: "$29.00 USD", "€29.00". $29.00 USD {{paid_at}}When the payment went through. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). September 28, 2026 {{payment_method}}The card or account charged, never the full number. Visa ending 4242 {{receipt_number}}The receipt or invoice number your records use. 1042-2231 {{#line_items}}…{{/line_items}}What the charge was for, one row per item. Each item has: description (The item), amount (Its amount with currency). 2 items {{receipt_url}}A receipt the customer can download: your PDF, or the payment provider's hosted receipt (Stripe: the charge's receipt_url). https://app.example.com/billing/receipts/1042-2231.pdf {{company_details}}Your legal business name, address and tax ID, as your accountant needs them on a receipt. Acme Software Inc., 1 Main St, Springfield, US · Tax ID 12-3456789 Plain-text part
emails/receipt.txtThanks for your payment We received your payment of {{amount}} for [Your product]. Amount paid: {{amount}} Paid on: {{paid_at}} Payment method: {{payment_method}} Receipt number: {{receipt_number}} {{#line_items}} - {{description}}: {{amount}} {{/line_items}} Download receipt (PDF): {{receipt_url}} {{company_details}}. Don't recognize this charge? Write to [your support email] before you dispute it and we'll sort it out. -- [Your product] Questions? Write to [your support email].Invoice
Graded asinvoice- Subject
- Invoice {{invoice_number}} from [Your product]: {{amount_due}} due {{due_date}}
- Files
- emails/invoice.html, emails/invoice.txt, emails/InvoiceEmail.tsx
When to send: When an invoice is issued (send-invoice billing). Once it is paid, send the receipt draft.
Preview
Placeholders (8)
Invoice placeholders Placeholder What goes in it Example {{invoice_number}}The invoice number your records use. INV-1042 {{amount_due}}The amount due with its currency: "$29.00 USD". $29.00 USD {{due_date}}When payment is due. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). October 12, 2026 {{billing_period}}The period the bill covers. September 28 to October 27, 2026 {{#line_items}}…{{/line_items}}What the charge was for, one row per item. Each item has: description (The item), amount (Its amount with currency). 2 items {{invoice_url}}The hosted invoice page where it can be paid (Stripe: the invoice's hosted_invoice_url). https://app.example.com/billing/invoices/INV-1042 {{invoice_pdf_url}}The invoice as a PDF (Stripe: invoice_pdf). https://app.example.com/billing/invoices/INV-1042.pdf {{company_details}}Your legal business name, address and tax ID, as your accountant needs them on a receipt. Acme Software Inc., 1 Main St, Springfield, US · Tax ID 12-3456789 Plain-text part
emails/invoice.txtInvoice {{invoice_number}} Your [Your product] invoice for {{billing_period}} is ready. {{amount_due}} is due on {{due_date}}. Amount due: {{amount_due}} Due: {{due_date}} Invoice number: {{invoice_number}} Period: {{billing_period}} {{#line_items}} - {{description}}: {{amount}} {{/line_items}} View and pay invoice: {{invoice_url}} Download the invoice (PDF): {{invoice_pdf_url}} {{company_details}}. Questions about this invoice? Write to [your support email]. -- [Your product] Questions? Write to [your support email].Payment failed
Graded aspayment_failed- Subject
- Your [Your product] payment didn't go through
- Files
- emails/payment_failed.html, emails/payment_failed.txt, emails/PaymentFailedEmail.tsx
When to send: On each failed renewal attempt, at most once per attempt. Stop as soon as a payment succeeds.
Preview
Placeholders (6)
Payment failed placeholders Placeholder What goes in it Example {{plan_name}}The plan, as the pricing page names it. Pro {{amount}}The amount with its currency, formatted for people: "$29.00 USD", "€29.00". $29.00 USD {{payment_method}}The card or account charged, never the full number. Visa ending 4242 {{next_retry_date}}When you will try the card again. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). October 1, 2026 {{access_ends_date}}When access stops if nothing changes. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). October 7, 2026 {{update_payment_url}}Straight to where the card is fixed: your billing page, a Stripe customer-portal session or the hosted invoice page. Every extra step loses customers who meant to pay. https://app.example.com/settings/billing/payment-method Plain-text part
emails/payment_failed.txtYour payment didn't go through We tried to charge {{amount}} to your {{payment_method}} for your [Your product] {{plan_name}} plan, and it was declined. Nothing is lost yet. We will try again on {{next_retry_date}}. To keep your plan, update your payment method before {{access_ends_date}}. Update payment method: {{update_payment_url}} Cards usually fail because they expired or the bank flagged the charge; a new card or a quick call to your bank fixes it. Need help? Write to [your support email]. -- [Your product] Questions? Write to [your support email].Trial ending
Graded astrial_ending- Subject
- Your [Your product] trial ends on {{trial_end_date}}
- Files
- emails/trial_ending.html, emails/trial_ending.txt, emails/TrialEndingEmail.tsx
When to send: Once, 3 to 7 days before the trial ends (Stripe fires customer.subscription.trial_will_end 3 days before). Set has_payment_method from whether a card is on file.
Preview
Placeholders (7)
Trial ending placeholders Placeholder What goes in it Example {{trial_end_date}}The day the trial ends. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). October 3, 2026 {{days_left}}Whole days until the trial ends. 3 {{#has_payment_method}}True when a card is on file (the trial rolls into a paid plan); false when the account will be paused instead. true {{plan_name}}The plan, as the pricing page names it. Pro {{plan_price}}What they will pay, with currency and period. $29.00 USD a month {{payment_method}}The card or account charged, never the full number. Visa ending 4242 {{billing_url}}Your billing page (or a Stripe customer-portal session). https://app.example.com/settings/billing Plain-text part
emails/trial_ending.txtYour trial ends on {{trial_end_date}} Your free trial of [Your product] ends on {{trial_end_date}}, in {{days_left}} days. {{#has_payment_method}} On that day your {{plan_name}} plan starts and we will charge {{plan_price}} to your {{payment_method}}. You don't need to do anything to keep going. If you would rather not continue, cancel before {{trial_end_date}} and you won't be charged. {{/has_payment_method}} {{^has_payment_method}} To keep your work and carry on, choose a plan before {{trial_end_date}}. {{plan_name}} is {{plan_price}}. If you don't, your account will be paused, not deleted. {{/has_payment_method}} Manage your plan: {{billing_url}} Questions about plans or pricing? Write to [your support email]. -- [Your product] Questions? Write to [your support email].Subscription changed
Graded assubscription_changed- Subject
- Your [Your product] plan is now {{new_plan_name}}
- Files
- emails/subscription_changed.html, emails/subscription_changed.txt, emails/SubscriptionChangedEmail.tsx
When to send: After an upgrade, downgrade or billing-period change takes effect (or is scheduled, with the date it will).
Preview
Placeholders (6)
Subscription changed placeholders Placeholder What goes in it Example {{new_plan_name}}The plan after the change. Pro {{old_plan_name}}The plan before the change. Starter {{effective_date}}When the change took (or takes) effect. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). September 28, 2026 {{new_price}}The new price with currency and period. $49.00 USD a month {{next_billing_date}}The next charge. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). October 28, 2026 {{billing_url}}Your billing page (or a Stripe customer-portal session). https://app.example.com/settings/billing Plain-text part
emails/subscription_changed.txtYour plan is now {{new_plan_name}} Your [Your product] plan changed from {{old_plan_name}} to {{new_plan_name}} on {{effective_date}}. New plan: {{new_plan_name}} Price: {{new_price}} Next bill: {{next_billing_date}} View billing: {{billing_url}} Didn't make this change? Write to [your support email] right away. -- [Your product] Questions? Write to [your support email].Subscription cancelled
Graded assubscription_cancelled- Subject
- Your [Your product] subscription is cancelled
- Files
- emails/subscription_cancelled.html, emails/subscription_cancelled.txt, emails/SubscriptionCancelledEmail.tsx
When to send: When a cancellation is confirmed, whether the customer cancelled or the subscription ended after failed payments (then say so in the first line).
Preview
Placeholders (4)
Subscription cancelled placeholders Placeholder What goes in it Example {{access_ends_date}}When access stops if nothing changes. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). October 7, 2026 {{plan_name}}The plan, as the pricing page names it. Pro {{data_kept_until}}How long you keep their data after access ends, as a date. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). January 5, 2027 {{billing_url}}Your billing page (or a Stripe customer-portal session). https://app.example.com/settings/billing Plain-text part
emails/subscription_cancelled.txtYour subscription is cancelled Your [Your product] {{plan_name}} subscription is cancelled. You keep full access until {{access_ends_date}}, and you won't be charged again. After that, we keep your data until {{data_kept_until}} in case you come back. You can resubscribe any time before then and pick up where you left off. Resubscribe: {{billing_url}} Didn't cancel? Write to [your support email] right away. Cancelled because something wasn't working? We'd genuinely like to hear it. -- [Your product] Questions? Write to [your support email].Data export ready
Graded asdata_export_ready- Subject
- Your [Your product] data export is ready
- Files
- emails/data_export_ready.html, emails/data_export_ready.txt, emails/DataExportReadyEmail.tsx
When to send: When the export file is ready. Never attach the export: link it behind sign-in.
Preview
Placeholders (4)
Data export ready placeholders Placeholder What goes in it Example {{download_expires_at}}When the download link stops working. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). October 5, 2026 {{requested_at}}When the export was asked for. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). September 28, 2026 at 9:12 AM UTC {{file_size}}The size of the export, for people. 12 MB {{download_url}}The download link. Make it require sign-in (or be single-use and short-lived): it is the whole account. https://app.example.com/exports/ex_5Yc1 Plain-text part
emails/data_export_ready.txtYour data export is ready The export of your [Your product] data you asked for on {{requested_at}} is ready ({{file_size}}). Download your data: {{download_url}} The download link expires on {{download_expires_at}}, and you'll be asked to sign in. Didn't ask for an export? Someone may have access to your account: change your password and write to [your support email]. -- [Your product] Questions? Write to [your support email].Account deleted
Graded asaccount_deleted- Subject
- Your [Your product] account has been deleted
- Files
- emails/account_deleted.html, emails/account_deleted.txt, emails/AccountDeletedEmail.tsx
When to send: Once, after the deletion has actually happened (not when it is requested), to the address the account had.
Preview
Placeholders (3)
Account deleted placeholders Placeholder What goes in it Example {{deleted_at}}When the account was deleted. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). September 28, 2026 {{email}}The recipient's email address. ann@example.com {{kept_records}}Exactly what you still keep after deletion, and why. Say it only if it is true. invoices, which tax law requires us to keep for 7 years Plain-text part
emails/account_deleted.txtYour account has been deleted Your [Your product] account {{email}} and its data were deleted on {{deleted_at}}. The only things we keep are {{kept_records}}. Everything else is gone, and this is the last email you will get from us. This is a notice: there is nothing you need to do. Didn't delete your account? Write to [your support email] right away. -- [Your product] Questions? Write to [your support email].Email changed (to the old address)
Graded asemail_change_notice_old- Subject
- Your [Your product] email address was changed
- Files
- emails/email_changed_old.html, emails/email_changed_old.txt, emails/EmailChangedOldEmail.tsx
When to send: To the OLD address, immediately after the change. It is the one alert that reaches the real owner if an attacker changed the email, so it must never be skipped or batched.
Preview
Placeholders (4)
Email changed (to the old address) placeholders Placeholder What goes in it Example {{new_email}}The address after the change. ann@new-example.com {{old_email}}The address before the change. ann@example.com {{changed_at}}When the change happened. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). September 28, 2026 at 2:14 PM UTC {{secure_account_url}}A link that reverts the change or locks the account and starts recovery. It must work WITHOUT signing in: whoever made the change may have changed the password too. Single-use, long-lived (days, not minutes). https://app.example.com/security/review/sa_Q8n2 Plain-text part
emails/email_changed_old.txtYour email address was changed The email address for your [Your product] account was changed from {{old_email}} to {{new_email}} on {{changed_at}}. If that was you, you're all set. There's nothing else to do. Wasn't you? Someone else may have access to your account. Use the button to undo the change and lock the account. It works without signing in. This wasn't me: {{secure_account_url}} This is the only warning we can send you here, because future emails go to the new address. Questions? Write to [your support email]. -- [Your product] Questions? Write to [your support email].Email changed (to the new address)
Graded asemail_change_notice_new- Subject
- This is now your [Your product] sign-in email
- Files
- emails/email_changed_new.html, emails/email_changed_new.txt, emails/EmailChangedNewEmail.tsx
When to send: To the NEW address, after the change is confirmed. It deliberately does not show the old address: if an attacker added this address, it should not learn the owner's.
Preview
Placeholders (2)
Email changed (to the new address) placeholders Placeholder What goes in it Example {{new_email}}The address after the change. ann@new-example.com {{app_url}}Your app's sign-in page or dashboard. https://app.example.com Plain-text part
emails/email_changed_new.txtYour sign-in email is updated {{new_email}} is now the email address for your [Your product] account. Use it to sign in from now on. Open [Your product]: {{app_url}} This is a notice: there is nothing you need to do. Didn't expect this? Someone may have added your address by mistake: write to [your support email]. -- [Your product] Questions? Write to [your support email].New sign-in alert
Graded asnew_login_alert- Subject
- New sign-in to your [Your product] account
- Files
- emails/new_login_alert.html, emails/new_login_alert.txt, emails/NewLoginAlertEmail.tsx
When to send: On a sign-in from a new device or browser (not every sign-in, or people learn to ignore it).
Preview
Placeholders (6)
New sign-in alert placeholders Placeholder What goes in it Example {{device}}Browser and operating system, from the user agent. Chrome on macOS {{signed_in_at}}When the sign-in happened, with the timezone. Format it for people ("October 3, 2026"); if you include a time, add the timezone ("5:00 PM UTC"). September 28, 2026 at 2:14 PM UTC {{email}}The recipient's email address. ann@example.com {{location}}Approximate location from the IP address; say it is approximate. Lisbon, Portugal (approximate) {{ip_address}}The IP address of the sign-in. 203.0.113.24 {{secure_account_url}}A link that reverts the change or locks the account and starts recovery. It must work WITHOUT signing in: whoever made the change may have changed the password too. Single-use, long-lived (days, not minutes). https://app.example.com/security/review/sa_Q8n2 Plain-text part
emails/new_login_alert.txtNew sign-in to your account Your [Your product] account {{email}} was just signed in to from a device we haven't seen before. When: {{signed_in_at}} Device: {{device}} Location: {{location}} IP address: {{ip_address}} If this was you, you can ignore this email. Wasn't you? Secure your account now: it signs everyone out and asks for a new password. This wasn't me: {{secure_account_url}} Questions? Write to [your support email]. -- [Your product] Questions? Write to [your support email].
Sign-up, password reset and magic links
Auth emails usually come from your auth provider. When a run finds Supabase’s, Firebase’s, Cognito’s or WordPress’s stock text, the report includes replacements written for that provider’s own template settings and placeholders; see how a test run works.