Docs menuThe email map

The email map

Signup and password reset are the emails everybody remembers to test. The email map is the list of every email your app can send, so the rest get tested too: the invite, the approval, the receipt, the reminder a scheduled job sends at night.

Guide

What the map is

Each project has one map. Each entry is one email your app sends (or, while you build it, should send) to one kind of person, as your agent found it in your code or planned it with you, and where it is: planned, built or tested:

PartExample
Keydraw-approved-contractor
NameDraw approved
KindOne of the report’s email types (welcome, password reset, receipt…), or “other” for app notifications
TriggerThe owner approves every line of a draw.
Goes tocontractor
Where it’s sent fromemails/draw-approved.tsx
StatusPlanned, Built, Tested or Failed, with the run that last tested it

An email that reaches two kinds of people with different content is two entries: when a draw is submitted, the owner gets one email and each subcontractor another.

Building the map

You don’t write the map. Ask your agent:

Ask your agent
Use Flowproof to map every email this app can send, then test each one and tell me what is still untested.

The agent calls get_email_map first. If the project has no map, it reads your code and saves one with save_email_map. It looks in the places email actually leaves an app:

  • calls to email services (resend.emails.send, SendGrid, Postmark, nodemailer, SES, Mailgun, WordPress’s wp_mail) and every caller of your own send-email helper;
  • your auth provider’s templates, such as supabase/templates, including ones your screens never use but the provider’s API can still send;
  • emails your payment provider sends for you: Stripe receipts, trial-ending and failed-payment emails;
  • template folders, and scheduled jobs (vercel.json crons, cron routes, queues) where digests and reminders live.

Templates nothing can send any more are left out, and your agent tells you about them instead. Saving the same key again updates that entry and keeps its test results; an entry is only removed when the agent names it. A project’s map holds up to 300 emails.

Planning the emails of an app you’re still building

If your app is still being built, there is no code to read yet. Ask your agent to plan the emails instead:

Ask your agent
Use Flowproof to plan the emails my app needs (it’s a SaaS with teams and Stripe billing), save them to the email map, then help me build and test each one as we go. Add Flowproof’s email rules to CLAUDE.md.

It calls plan_emails with what your app is and does (accounts, teams, billing, trials…) and gets back the emails it needs: who gets each one, what sends it, why it matters, and what it must have. A password reset says how long its link lasts and works only once; a failed-payment email shows the amount and links straight to where the card is updated; an invite says who invited you, to what. These are the same things a run grades, so an email built to the plan passes. The plan is rules, not AI: the same answers always give the same plan.

The agent saves the emails you keep to the map as Planned, without touching anything already in it. As it builds each one it marks it Built, then tests it, and the run marks it Tested or Failed. The map shows where you are: “9 planned, 12 built, 7 tested”. Flowproof’s email rules in your CLAUDE.md keep your agent doing this for every email it writes afterwards.

How an email gets marked tested

When the agent waits for an email, it passes the entry’s key as map_key to wait_for_email. That ties the email that arrived to the entry. There is no guessing from subjects: the agent names the email it is waiting for, on the address it expects it at.

When the run ends and its final report is built, each entry it tested is marked:

StatusMeans
TestedThe email arrived, and the report found nothing critical or worth a warning in it.
FailedThe email never arrived while the agent waited, or it arrived with a critical or warning-level problem, such as a broken link or a merge tag that leaked into the text.
Built, not tested yetThe code that sends it exists, and no finished run has waited for it.
PlannedYour app should send it and doesn’t yet. A planned email whose message arrives in a run is marked built.

Your agent may also suggest emails (a “we miss you” note, say). A Suggested email is shown apart, after the map, and counts for nothing until you accept it; then it becomes planned. Suggestions explains when and how.

A wait that timed out only counts against the email when nothing at all reached the address. If mail arrived but didn’t match the agent’s filter, that is the filter’s problem, not your app’s.

Flows with more than one person

Invites, approval chains, comments and handoffs are emails one person’s action sends to someone else. Your agent tests them with one test address per role, in the same run, and waits on the recipient’s address:

A contractor and an owner, in one run
start_session      project "acme"
new_address        local "contractor"      acme.s7k2ab.contractor@flowproof.dev
new_address        local "owner"           acme.s7k2ab.owner@flowproof.dev
  ...the agent signs up as the contractor...
mark_step          "contractor invited the owner"
wait_for_email     the owner's address, map_key "project-invite-owner"
  ...the agent accepts the invite as the owner, then approves draw 1...
mark_step          "owner approved draw 1"
wait_for_email     the contractor's address, map_key "draw-approved-contractor"
end_session        both accounts are cleaned up

Naming each address after its role is what lets the report say who received what: when a run’s mail reaches more than one person, the timeline says whose inbox each email landed in (“Sent to contractor”). Every address is cleaned up at the end, including an invitee the agent never signed in as.

Suggestions

Getting every email right is the job. Beyond that, some emails help people get started and keep coming back: a nudge toward the first real step, a note when something first works, a weekly summary someone chose. Your agent offers to look for those once per project, after it saves your email map or after a run that passes, by asking you:

Your agent asks, once
Want me to look for email gaps that could help people get started and keep coming back?

Say no and it isn’t asked again for that project. Say yes and your agent reads your code for the things people do in your app (create a project, submit a first draw, invite the owner, approve a draw) and calls suggest_emails. What comes back is grouped by goal, and every suggestion is written in your app’s own actions: who gets it, what triggers it, and what stops it.

GoalFor example
Getting startedA nudge if nobody has submitted a first draw three days after signing up; a note the first time they do.
Coming backOne note after a quiet spell; a weekly summary of their own work, for people who turn it on.
Bringing others inAfter a first success, a prompt to invite the people who should see it.
Trial to paidDay 1, halfway, and two days before a trial ends.
Before and after leavingWhat goes with a cancellation or deletion before it happens; one win-back note later.
Asking how it wentOne question, after the first success, never before.

Every suggestion comes with its guardrails, and your agent builds them in:

  • whether it is transactional, about your own account, or marketing, and so whether it needs consent;
  • marketing only to people who opted in, with an unsubscribe link and a link to email preferences;
  • one cap across all of them, so nobody gets more than one every two days;
  • never an email about activity someone didn’t choose to hear about;
  • sent in the recipient’s daytime, in their time zone;
  • stopped the moment the thing it asks for happens.

If your app has no unsubscribe link or doesn’t record marketing consent yet, the suggestions say so first. Emails your map already has are skipped. Saved suggestions appear under Suggested in your dashboard and count for nothing. To accept or dismiss them, ask your agent:

Ask your agent
Show me the suggested emails in the Flowproof email map, with their guardrails, and ask which to accept and which to dismiss.

An accepted email becomes planned, to build and test like any other. A dismissed one is removed and never suggested again. Your dashboard doesn’t accept or dismiss them itself, for the same reason you can’t edit the map by hand.

In your dashboard

The Projects page shows each project’s map, grouped by kind, with a count like “12 of 38 tested”. Each email says Planned, Built, Tested or Failed, and links to the run that last tested it, where you can read the email and what the report found. Suggestions come last, under their own heading.