Emails — Mimeo docs
Emails
Every message you send lives in the email library. You write the body in markdown with Liquid for personalization, and a template wraps it in your branding.
The library
The library lists one row per email. It's the single place your content lives, which means the same email can be used in more than one context without being copied around.
Where an email comes from
Everywhere an email gets attached — a new broadcast, a new sequence step, or swapping the email on either — Mimeo asks the same question first, rather than guessing:
- Create a new email from scratch — starts blank, and joins your library as its own email.
- Use an email from your library — start from something you've already written.
If you pick the library, there's a second question, and it's the one that matters later:
- Use the library email directly — one shared source. Editing it here changes it everywhere it's used, and its opens and clicks all count toward the one email. Use this when the content should stay identical everywhere, like a standard welcome.
- Copy it to create a new version — an independent copy with its own stats. Edits stay here, the original is untouched, and people who already received the original are eligible for this one. Use this when you want a variation.
There is deliberately no default on that second question. Sharing and copying are opposite answers to "what does my next edit change", and picking wrong is the usual source of "why did that email change?"
Broadcasts always get their own email
A broadcast never shares a library email, so it isn't asked the second question. Whichever way you start it — blank, or from a library email — the broadcast gets a new row of its own, added to your library. That's what keeps a sent broadcast honest: what it said stays what it said, however you later edit the email you started from.
Splitting a shared email off later
If an email is shared and you realise this one place should go its own way, you don't have to start over. Open the email from inside that broadcast or step, go to Used in, and choose Make a separate copy for this broadcast (or step). Everything written so far comes with it, including edits you haven't finished typing.
Confirm it and three things change, all of them only for this one place: edits stop reaching everywhere else, the copy's opens and clicks start from zero, and anyone who already received the original counts as not having received this one.
Finding what's reusable
Because every broadcast mints an email, the library fills up with rows that exist for a single send. That's expected. Use the Used in filter above the list to sort them out: pick any of Broadcasts, Sequences, Flows, or Flows (one-offs only), and match emails used in any of them, in all of them, or in none of them. "None of them" is how you find emails nothing points at any more.
The library's own Duplicate row action still exists for making a spare copy that isn't attached to anything; it prefixes both the title and the subject with "Copy of" so the two are tellable apart in the list. A copy made for a specific broadcast or step keeps the original title and subject, since that subject is what recipients see.
Title and subject
An email is two things at once: a message going to someone, and a thing you keep in a library and drop into sequences. The subject serves the first job. The title serves the second — it's what the email is called in the library list, in a sequence's steps, and in any picker. Nobody receiving the email ever sees it.
Leave the title blank and the subject stands in, so an email is never nameless and there's nothing to fill in twice. Set one when the subject makes a bad label: "Try Builder Methods Pro again ($50 off as a returning member)" is a fine subject line and a useless row in a list of five steps, where "BMP winback" tells you what you need at a glance. Clearing the title hands the job back to the subject.
The editor
Opening an email gives you one screen. Its title is the heading at the top — click it to rename. Below that sit the three lines a recipient reads before deciding whether to open anything — From, Subject, Pre-header — in the order an inbox shows them.
Under those, an Edit / Preview switch decides what fills the rest of the screen: the markdown you're writing, or the email as it will arrive. They are the same space rather than two pages, and the preview is of the draft in front of you rather than the last time it saved — so switching to it is reading the result rather than imagining it. Whatever the switch is set to, the rest of that row belongs to it: the insert menu while you're writing, and who the email is rendered for and at what size while you're looking. The preview opens at desktop width — the narrower measures are a deliberate check on one window rather than the default way to read what you just wrote.
Send test sits on the same row and stays there in both modes, because it answers the same question they do: what does this actually look like when it arrives. It opens a dialog to pick who it goes to — one of your sending addresses, or any address you type. That is the one check the preview can't make, since only a real send exercises the provider, the domain, and whatever the receiving client does with the HTML. Anything typed but not yet saved is written first, so a test is never of the previous draft.
A narrow panel on the right holds everything about the email that isn't the email: the layout it compiles through, its labels, sending yourself a test, and everywhere it's used. All set-once or read-once, which is why they're over there and not in the way. The panel collapses, and on a phone it moves below everything else.
Everything saves itself a couple of seconds after you stop typing. Save changes and ⌘S are there for when you'd rather not wait.
Writing: markdown plus Liquid
The editor takes markdown. Headings, lists, links, bold, and emphasis all work the way you expect, and the template turns them into styled HTML at send time.
Person variables
| Variable | Resolves to |
|---|---|
{{ person.email }} |
Their email address |
{{ person.first_name }} |
First name |
{{ person.last_name }} |
Last name |
{{ person.name }} |
Full name |
{{ person.fields.<key> }} |
Any custom field, by the key you declared under Settings → Fields |
Defaults
Real lists have blanks. Always give personalized text a fallback with the default filter:
Hi {{ person.first_name | default: "there" }},
Without it, the people missing a first name get "Hi ," — which reads as exactly the kind of automation you're trying not to look like.
Conditionals
{% if person.fields.plan == "pro" %}
You're on Pro, so this is already included in your plan.
{% else %}
Pro subscribers get this included — here's what's in it.
{% endif %}
{% if %}, {% else %}, and {% endif %} are supported. Keep the logic shallow: an email that needs deeply nested branches is usually two emails.
Snippets
A snippet pulls in a reusable component you've defined once — a signature, a promo block, a standard footer note:
{% component "signature" %}
The key matches a component in your templates. Edit the component and every email using it updates.
Buttons
Buttons are a tag rather than hand-written HTML, so they render correctly across mail clients:
{% button label:"Start your trial" url:"https://example.com/signup" style:"primary" %}
style is primary, secondary or tertiary — the three buttons your theme defines. Leave style out and you get the primary one. The colours come from the theme rather than from the tag, which is what keeps buttons consistent across every email instead of drifting one message at a time.
Images
Everything you upload lands in the media library under Content → Media — one place for images, PDFs and zips, each with a hosted URL and (for images) the alt text carried into every insert. In the editor, Insert → From media library… drops any of it into the body.
Where uploads live is a Settings decision. Four options under Settings → Storage & media, all valid. Because media hosting rides a domain you already send from, the section stays closed until you have a connected domain and a sender.
- Cloudflare R2 — uploads go to your R2 bucket and serve from a subdomain of one of your sending domains. You pick the subdomain (
filesis the usual choice) and Mimeo builds the host from the domain you choose beside it, so files ride your own domain and never a shared bucket URL. - Amazon S3 — uploads go to your S3 bucket and serve from the bucket's own public URL. There is no domain or DNS to set up; the bucket just has to allow public reads. Set a base URL only if you want CloudFront in front of it.
- Built-in local hosting — upload and your Mimeo serves it. Simplest; nothing else to configure. Right for single-server installs.
- External URLs — paste a URL to an image you already host. Nothing is uploaded. This is the default until you choose a backend: the upload button stays off rather than quietly storing images somewhere you didn't pick.
Templates
Templates are full HTML and CSS — this is deliberately not a drag-and-drop builder. There are two kinds, plus the themes that supply their colours and type.
The editor puts the rendered template on the left and the code on the right. Typing redraws the preview a moment later, so you are reading the result rather than imagining it. The preview can be shown at mobile, tablet or desktop width and in light or dark, and the code pane hides away when you want the full width for the picture. For a layout, the code pane switches between its HTML and its CSS; a component has HTML only, for the reason below.
Every layout, component and theme has a Duplicate action in its row menu, which is usually how the second one gets made: a variant is nearly always the original with two things changed. The copy opens in the editor under the same name with copy appended, is never the default whatever it was copied from, and — for a component — is given a {% component %} key of its own, since two components answering to one key is a template that renders whichever it finds first.
Layouts
A layout is the shell around your content: header, branding, typography, footer. Two rules:
- It must contain
{{ content }}, which is where the rendered email body is inserted. - Its footer should contain
{{ unsubscribe_url }}. That resolves per recipient to a signed link tied to that person and that exact email. Leaving it out means sending mail with no opt-out. - The footer should also contain
{{ mailing_address }}, which resolves at send time to the mailing address set under Settings → Domains & senders. Anti-spam law (CAN-SPAM and its equivalents) requires every marketing email to show a physical mailing address — a P.O. box works.
One layout is marked default and is used by emails that don't specify another.
A new layout doesn't start empty. It comes with markup and a stylesheet that already make a plain, well-set email — one column at a readable measure, and every element an email actually uses already styled: headings, lists, quotes, rules, buttons, links. It's meant to be edited down to your brand rather than admired, but it means the first thing you see is an email rather than a page of unstyled HTML.
That starter stylesheet is written entirely against your theme — every colour and size in it is a {{ theme.… }} tag rather than a literal, including a @media (prefers-color-scheme: dark) block with a counterpart for every colour above it. So the pickers on the Themes tab drive it without you opening the CSS at all, and the light dark meta tags in the markup stop mail clients inverting the email themselves.
Two things to know when you edit the CSS. Theme tags are resolved when the email is compiled, before anything else runs, and a tag naming a variable that doesn't exist is refused when you save, with the bad tag named — a typo can't quietly compile to a hole. And the stylesheet is rendered as Liquid along with the markup, so other Liquid syntax written into it — even inside a comment — is a tag the renderer will try to run, and an unknown one stops the render.
Pasting in a layout from another tool is the usual way to start, and the usual snag: most exports carry that tool's own slot name — Kit's {{ message_content }}, for instance. Rename it to {{ content }} and the layout saves. The editor flags a missing slot as you type and names the one it found instead, so nothing you've pasted is lost to a rejected save.
Components
Components are the reusable fragments that {% component %} pulls in — the same full HTML control as a layout, scoped to a piece rather than the whole message.
A component has no stylesheet of its own. It is expanded into the email body, which is then wrapped in a layout, so the layout's CSS is what styles it — which is also why the component editor previews it inside your default layout rather than on its own. A theme picker on the editor draws that preview under any of your themes; it's preview-only, since which theme a real email uses is decided by its layout. It opens on the default theme first, then remembers your last pick per component in that browser. Style a component from the layout's CSS, or inline on the element.
Colours belong in the layout's CSS, not inline. An inline style can't change with the colour scheme, so an inline colour — hard-coded or via a theme tag — shows its one value in dark mode too. Give the element a class instead, and colour that class from the layout's stylesheet with a theme tag plus a dark counterpart in the @media (prefers-color-scheme: dark) block. The starter stylesheet ships two such classes, correct in both schemes: muted for text that sits back from the body copy — the starter's own footer wears it — and standout for text in the same ink as headlines and bold, without borrowing a strong tag for its colour. Links inside either keep the link colour: a link is a link, whatever ink surrounds it.
Layouts and components are equally editable by your agent — the same create, edit, retire and delete abilities are on the Templates API and the MCP server, so "make the header narrower on every email" is a thing you can ask for rather than hand-edit.
Themes
A theme holds the creative decisions your layouts are built from: the colours, the font stacks, and the shape of the page. You set them with colour pickers and a handful of fields; your layouts refer to them by name. The point is that a brand colour lives in one place rather than in every stylesheet that ever quoted the hex code — change it here and every email on the theme is rebuilt with it, drafts included.
A layout's CSS refers to a theme value as a tag:
.email-body { color: {{ theme.body_text_color }}; }
.email-body a { color: {{ theme.link_color }}; }
Every colour has a light value and a dark one, and both matter: an email that states only one gets the other invented for it by the mail client. {{ theme.dark.body_text_color }} is the dark half, which is what the @media (prefers-color-scheme: dark) block in your stylesheet is built from. The theme editor previews in both, so you can check the half you aren't looking at.
What a theme sets: the body and headline font stacks; the email background and the content background; headline and bold text, body text, muted text — the footer and quoted text — links, dividers and the content border; three button styles; and the shape of the content area, meaning its width, border thickness, corner radius, and the padding and margin holding it off each edge.
The font pickers offer the stacks that actually resolve in mail clients, grouped into sans serif, serif and monospace, with Custom for typing your own. Leave the headline font on "Same as body font" and headlines are set in the body's. Padding and margin are set as top, left & right, and bottom — left and right differing is a thing almost nobody wants and everybody would have to skip past.
Buttons. A theme defines three — primary, secondary and tertiary — each with a background and text colour in both schemes, sharing one corner radius. They preview on both papers as you edit, because a button is only readable relative to what's behind it. A button whose two schemes differ gets a dark-mode rule written for it automatically; one that looks the same in both doesn't need one and doesn't get one.
Using more than one theme
You can keep several. One is the default, and each layout picks the theme it's built from in the selector on the layout editor's toolbar. Leave a layout on "Default theme" and it follows whichever theme is the default, so promoting a different theme reaches it without opening its CSS.
A layout can also reach across to a named theme for a single value by putting its key in the middle — {{ theme.brand-b.link_color }}, or {{ theme.brand-b.dark.link_color }} for the dark half. A new theme starts at Mimeo's standard defaults; to make a variant of a theme you've already built, duplicate that theme instead and adjust the copy.
None of this takes the code away from you. A theme tag is only a value in a stylesheet you own — replace one with a hex code and that declaration simply stops following the theme, while the rest keep going. A layout written with no theme tags at all is a perfectly good layout; it just won't answer to the pickers.
Themes are equally editable by your agent, through the Themes API and the MCP server — so "warm the brand colour up a little and show me" is a thing you can ask for.
Where an email is used
An email can be shared by more than one place, so Used in — in the panel beside the editor, with a count on it — lists everywhere it is: the flows that send it, the sequences that contain it, and the broadcasts it has gone out as, each group behind its own toggle. Opened from inside a broadcast or a step it shows the full listings instead, says how many places share the email, and is where you split this one off into its own copy.
A flow counts as using an email two ways: a send step that names it, or a step that starts a sequence containing it. Both are listed, and a flow that does both appears once with both routes noted.
Report beside it goes straight to this email's numbers in Reporting.
Preview
The preview renders the real thing against real data: components expanded, buttons rendered, the layout wrapped around it and every variable resolved for one actual person. It is the compiled email, not an approximation of it — the same two stages a real send goes through, minus the tracking, which is only ever added on the way out.
The person icon beside the address it names decides who it's rendered for:
- A pinned preview person — someone you keep coming back to because you know what their record should render as. Pin whoever you're looking at with the pin beside it.
- Anyone at random — good for catching missing fields you'd never hit with your own tidy test record.
- Someone in a segment — a person who would actually receive this, whose merge fields are the real ones.
- Search for a specific person by email when you're debugging what one particular subscriber saw.
Beside it, toggle mobile, tablet and desktop widths and light and dark appearance. Mobile and tablet hold the email in a 375px and 600px window; desktop gives it the whole pane, the way a maximized mail client would. Dark mode is where email designs break most often — a logo that's black-on-transparent disappears entirely. When a fixed width is wider than the room left for it, the preview scales down rather than cropping, and says what it's showing you.
The light/dark toggle answers the email's own @media (prefers-color-scheme: dark) rules rather than the setting on your machine, so you can check both appearances from a laptop that's fixed in one of them.
Your agent can read the same thing: the Emails API and the MCP server both render an email for a person, including a body that hasn't been saved — which is how an agent checks its own work before writing it.
Send test
Sends the compiled email through your real provider. It's the last check before a real send, and the only one that proves the whole chain works: compile, provider handoff, delivery, tracking. The preview answers what the email says; this answers whether it arrives.
It goes to your own address by default, but the picker takes any of your sending addresses, or one you type. That matters because a test exists to be read in the client your readers actually use — which is rarely the inbox you're signed in with. An address that isn't in People yet is added there, since a send needs somebody to send to. The send is marked as a test, so nobody's sent-status or place in a sequence moves because of it.
Your agent can run one too — the Emails API and the MCP server both take an address and send to it. Worth knowing it can, and worth knowing it's the one step that costs something: a preview is free, a test lands in somebody's inbox.
Reset sent-status for everyone
Each email tracks who has already received it, so nobody gets the same message twice. Reset sent-status for everyone clears that, restoring eligibility so the email can go out again to people who already got it.
The send log is not touched. Your history of what was actually sent stays complete and accurate — this only affects future eligibility, not the record of the past.
Archiving and deleting
Every row's menu in the library — and the actions menu on the email editor itself — has Archive. Archiving retires an email without destroying anything: it leaves the default listing (find it again under the Archived filter) and its send history stays intact. It's the right retirement for any email that has been sent — the record of what went out keeps pointing at it.
Delete permanently lives in the same menus and always asks for confirmation, but it only goes through for an email nothing points at: never sent, nothing queued, and not attached to any sequence, flow or broadcast. For anything else the dialog explains what's holding the email and offers archiving instead. Deletion removes the email from the library entirely and can't be undone. Use it to clear out drafts and abandoned experiments; archive everything else.
This library is what automation sends. Sequences and flows and broadcasts all send emails from here rather than holding private copies somewhere you can't see them. Whether an edit reaches one send or all of them is the shared versus copied choice you made when you attached it.