Health checks
Before an email goes out, Flyanote checks it: that it builds, that its merge tags work, that it fits in the inbox, that its links are sound, and that it has what the law requires. Each finding names its check.
How serious a finding is depends on the check and on the email’s category, marketing or transactional. These are the severities:
- Reject (
reject) - The save is refused and nothing is stored. The API answers 422.
- Block (
block) - The draft saves, but it can’t be published, scheduled, or sent until it’s fixed. A block marked “also stops a test send” stops that too.
- Warning (
warning) - Worth a look first. The app asks you to confirm; the API sends and lists it in
warnings[]. - Tip (
info) - A suggestion. It never stops anything.
- Our bug (
bug) - A block that is our fault, not yours. We’re alerted, your draft is saved, and trying again usually works.
- Added for you (
applied) - A note that we changed something on our own, such as adding the footer the law requires.
This page is generated from the checks themselves, so it lists exactly the checks we run. Each finding’s docs_url links to its check’s entry here, at #<id-with-dashes>, such as #compliance-identity for compliance.identity.
Building the email
Every email must build. These checks make sure the document is valid, everything it names exists, and what we build from it is sound.
render.schema: The document isn’t valid
The email document doesn’t match the format: a property is missing, misspelled, or the wrong type, or a rule across properties is broken. Each problem has a JSON Pointer and a code from the format’s error catalog.
- For: marketing, transactional email
- Runs at: save, validate
- Version: 5
| Severity | When |
|---|---|
| Reject | Any problem: the save is refused. |
How to fix it
- Fix each problem in
errors:pointersays where, and itsdocs_urllinks to the fix on the email format page. - Check a document without saving it with the validate call.
render.context: Something the email names is missing
The email names something your account doesn’t have: a brand kit color, font, or logo, an uploaded image, a saved link, or a merge tag Liquid can’t read. It can’t be built until that’s fixed.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 3
| Severity | When |
|---|---|
| Block, also stops a test send | While drafting, and on a new publish. |
| Warning | A published template whose kit or uploads changed since it was published: it still sends, using a fallback, and warns. |
How to fix it
- Pick something that exists, such as another kit color, or add what’s missing to your brand kit or uploads.
- For a merge tag, fix the problem the finding names; its
codeis on the email format page.
render.compile: We couldn’t build the email
With the code template_too_large, the email is too big to build: its HTML, the code for its layout included, would be over the 500 KB a template can hold. With colors_too_many, it uses too many different colors: over 2,000 pairings of a color (text, border, or background) with the background behind it, each of which needs its own dark-mode color. Any other failure is on our side: it isn’t caused by anything you did, and we’ve been alerted.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 3
| Severity | When |
|---|---|
| Our bug | When the failure is ours. |
| Block, also stops a test send | When the email is too big to build (template_too_large). |
| Block, also stops a test send | When the email uses too many different colors (colors_too_many). |
How to fix it
- For
template_too_large, shorten or remove sections, or replace long lists with links, then try again. - For
colors_too_many, reuse a smaller set of text, border, and background colors. If you pasted or generated the HTML, simplify the colors in that source, then try again. A color used the same way on the same background counts once, however often it appears. - For any other failure, try again. Your draft is saved.
- If that keeps happening, contact support with the
request_id.
Gmail also cuts off emails over about 100 KB. That limit is on the finished email, and we check it once the email builds.
render.invariants: What we built broke a rule
Every email we build is checked against rules we promise: a language, alt text on images, the footer in place, no scripts, and a plain-text part with every link. One failed, which is our bug.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 5
| Severity | When |
|---|---|
| Our bug | Always. |
How to fix it
- Try again. Your draft is saved.
- If it keeps happening, contact support with the
request_id.
Merge tags and Liquid
Merge tags fill in each person’s details. These checks catch tags that would fail or print nothing.
liquid.syntax: A merge tag can’t be read
A Liquid tag isn’t written correctly: a missing }} or %}, say, or a tag that’s never closed.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 3
| Severity | When |
|---|---|
| Block, also stops a test send | While drafting, and on a new publish. |
How to fix it
- Fix the tag at the finding’s line and column, or remove it.
liquid.profile: A Liquid feature we don’t support
The email uses Liquid that our templates don’t allow, such as a tag or filter outside the supported set, or a brand value your kit doesn’t have.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 5
| Severity | When |
|---|---|
| Block, also stops a test send | Always. |
| Warning | A published template whose kit changed since it was published, so a brand value it names is gone: it still sends, with that value empty, and warns. |
How to fix it
- Use a supported tag or filter, or remove it. The email format page’s Liquid section explains each rule, such as
/docs/email-format#liquid-tag-unknownfor a tag we don’t support.
liquid.unknown_field: A contact field that doesn’t exist
A merge tag reads a contact.* field your contacts don’t have, so it would print nothing. If one is close, we suggest it.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Always. |
How to fix it
- Use the suggested field, or one your contacts have.
- Or add the field to your contacts.
liquid.no_fallback: A merge tag with no fallback
If someone has no value for this field, nothing shows where it goes, which can leave a sentence broken.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Marketing email. |
| Tip | Transactional email, where your app usually sends every field. |
How to fix it
- Add a fallback: what to show when the field is empty, such as “there” in “Hi there”.
liquid.render: The email failed to build for someone
Filling in the email failed for some of the people or sample data we tried, so people with data like theirs would get nothing.
- For: marketing, transactional email
- Runs at: test send, publish and send, validate
- Version: 2
| Severity | When |
|---|---|
| Block, also stops a test send | Always. |
How to fix it
- Check the field the finding names, and give it a fallback or simplify the Liquid that reads it.
- Send a test with the same data to see it.
liquid.missing_data: A field missing from a message’s data
A transactional message’s data didn’t include a field the template reads, so that spot was left empty.
- For: transactional email
- Runs at: publish and send
- Version: 2
| Severity | When |
|---|---|
| Warning | Each message sent without it. The message is still sent. |
How to fix it
- Send every field the template reads: its
meta.variableslists them. - Or give the merge tag a fallback.
Size
Gmail cuts off emails over about 100 KB, hiding the end and the unsubscribe link. Sizes count the email as it will be sent, with names and tracked links filled in.
size.clip: The email is too large for Gmail
Gmail cuts off emails at about 100 KB and shows “Message clipped”, hiding the end of the email and its unsubscribe link.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 18
| Severity | When |
|---|---|
| Warning | From 80,000 bytes. |
| Block | From 100,000 bytes. Transactional email blocks only when a template is published; a message is never held for size. |
How to fix it
- Remove or shorten sections. The finding lists the biggest ones.
- Link to long content, such as a full list, instead of including it.
size.style: Too many style rules
Gmail ignores an email’s style rules past 16 KB, which turns off phone layouts and dark-mode colors. Each custom color adds rules.
- For: marketing, transactional email
- Runs at: save, preview, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | From 14,000 bytes of style rules. |
How to fix it
- Use your brand kit’s colors instead of exact ones where you can.
size.table: A large table
One table adds a lot to the email’s size, bringing Gmail’s limit closer.
- For: marketing, transactional email
- Runs at: save, preview, validate
- Version: 18
| Severity | When |
|---|---|
| Tip | From 15,000 bytes for one table. |
How to fix it
- Link to the full list, or split it across emails.
Links
Links that are unsafe, empty, or misleading hurt how inboxes treat your mail, and they let readers down.
link.scheme: A link type that isn’t allowed
A link uses a scheme that’s never allowed, such as javascript: or data:, or it’s a relative address. In your own HTML, only https:, http:, mailto:, and tel: links are allowed. These are refused because they’re unsafe or don’t work in email.
- For: marketing, transactional email
- Runs at: save, validate
- Version: 2
| Severity | When |
|---|---|
| Reject | Always. |
How to fix it
- Use a full web address starting
https://, or amailto:ortel:link.
link.from_contact: A link address from contact data
The start of a link’s address comes from a contact field. Anyone filling in your signup form can set that field, so they could point your email’s link anywhere.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Always. |
How to fix it
- Start the link with your own web address, and put contact data only after it.
link.empty: A link that goes nowhere
A link or button has no address yet, or still has the starter’s.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Always. |
How to fix it
- Add the address it should go to, or remove the link.
link.http: A link that isn’t secure
A link starts with http: rather than https:. Some inboxes and browsers warn readers before opening it.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 2
| Severity | When |
|---|---|
| Warning | Always. |
How to fix it
- Use the
https://address, if the site has one.
link.shortener: A link shortener
A link goes through a shortener like bit.ly. Spam filters distrust them, since they hide where a link goes.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 2
| Severity | When |
|---|---|
| Warning | Always. |
How to fix it
- Use the full address the short link leads to.
link.text_mismatch: Link text that names another site
A link’s text reads as one website’s address, but the link goes to a different site. Readers and spam filters treat that as phishing. A link to another part of the same site is fine.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 2
| Severity | When |
|---|---|
| Warning | Always. |
How to fix it
- Make the text match where the link goes, or use words instead of an address.
link.personal_data: A link that shares contact data
A link to another website includes a contact field, such as an email address in its query string. In the EU and UK, sharing it that way needs the reader’s consent.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 2
| Severity | When |
|---|---|
| Warning | Marketing email. |
| Tip | Transactional email, where links like these are often expected. |
How to fix it
- Remove the field from the link, or make sure your readers have agreed to it.
link.utm: Links without UTM tags
Links to your website have no utm_ tags, so your site’s analytics won’t count visits from this email.
- For: marketing email
- Runs at: save, preview, test send, publish and send, validate
- Version: 2
| Severity | When |
|---|---|
| Tip | Always. |
How to fix it
- Turn on UTM tags in your account’s settings, and we add them to every link to your site.
link.broken: A link that didn’t work
When we checked a link, its site couldn’t be found, refused or dropped the connection, had a certificate error, answered that the page wasn’t found or is gone, or had a server error twice. Sites go down for a while, so this is a warning, never a block.
- For: marketing, transactional email
- Runs at: test send, publish and send, validate
- Version: 3
| Severity | When |
|---|---|
| Warning | Always. |
How to fix it
- Open the link to see whether it works now.
- If it doesn’t, fix the address or remove the link.
If you run a website and found FlyanoteLinkCheck in your logs: it checks the links in an email one of our customers is about to send. It asks at most once a day for each link and account, whatever it found, a missing name or a refused connection included. After a timeout, a 429, or no route to your site, it asks again at the next check.
For a link it sends HEAD, and only if that answers 400 or more, GET with Range: bytes=0-0. For an image it sends GET for the first 1 MB. It follows at most 5 redirects, and asks once more after a server error. No request carries cookies or credentials, and all come from AWS’s shared Lambda addresses in us-east-1.
A 401, 403, or 429 answer is fine: we count the link as unchecked, never broken.
link.unchecked: A link we couldn’t check
We couldn’t check a link: its address is filled in from merge tags, its site didn’t let us in or didn’t answer in time, or it points somewhere we don’t fetch.
- For: marketing, transactional email
- Runs at: test send, publish and send, validate
- Version: 3
| Severity | When |
|---|---|
| Tip | Always. |
How to fix it
- Send yourself a test and open the link.
link.dropped: A link or image we left out
A link or image gets its address from your data, and the data gave it a kind of address we don’t send. A link keeps only an https, http, mailto, or tel address, and an image only an https or http one, so the email goes without it: a link’s text stays and stops being a link, and an image isn’t shown. The finding gives each one’s place, never its address.
- For: marketing, transactional email
- Runs at: test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | A link or image left out. |
| Our bug | We couldn’t check the email’s links with this data, so it can’t be shown or sent: a problem on our side, and we’re alerted. |
How to fix it
- Check the data you render or send with: the field behind that link or image should hold a full web address.
- Render it again with the same data to see what a send would carry.
Words
Your subject, preview text, and copy, checked against common problems and your brand kit’s voice.
content.subject_length: A long subject
The subject is over 60 characters. Phones cut long subjects off.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | Over 60 characters. |
How to fix it
- Put the key words first, or shorten it.
content.preheader: Preview text missing, long, or repeated
Inboxes show preview text beside the subject. Without it they show the email’s first words; past about 100 characters it’s cut off; and repeating the subject wastes the space.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | Always. |
How to fix it
- Add preview text that says something the subject doesn’t, and put the key words first.
content.caps: Capitals
Capitals read as shouting, and spam filters notice them in subjects.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | A subject with 3 words in capitals in a row, or mostly capitals. |
| Tip | A paragraph that is mostly capitals. |
How to fix it
- Write it in sentence case.
content.banned_words: A word your brand kit avoids
The email uses a word on your brand kit’s list of words to avoid.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Always. |
How to fix it
- Use another word, or change the list in your brand kit.
content.preferred_words: Your brand kit spells it differently
Your brand kit prefers another spelling or word for this one.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | Always. |
How to fix it
- Use the kit’s spelling.
content.starter_text: Starter text left in
Part of the email is still the starter text it came with.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Always. |
How to fix it
- Replace it with your own words, or remove it.
content.review: Text to check
Part of the email was filled in for you, by AI or from a template, and nobody has checked it yet.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Always. |
How to fix it
- Read it, and change anything that isn’t right.
content.empty: An empty email
The email has nothing in it outside the footer.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Block | Always. |
How to fix it
- Add a section with your message.
The law and the inbox
Marketing email must say who sent it and offer a way to unsubscribe. We add the footer that does both, and these checks make sure it’s there and readable.
compliance.identity: Your organization’s details are missing
The law requires marketing email to show who sent it: your organization’s name and postal address. We put them in your footer for you once you’ve added them. A test send shows a placeholder.
- For: marketing email
- Runs at: save, preview, test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Block | Marketing email. |
How to fix it
- Add your organization’s name, postal address, and contact details in your account’s settings.
compliance.unsubscribe: The unsubscribe link
Marketing email needs an unsubscribe link and a preferences link, both in the footer and in the plain-text part. We add them; if either is missing after that, it’s our bug. When the footer’s colors or size were too faint or small to read, we change them, and this says what we changed.
- For: marketing email
- Runs at: save, preview, test send, publish and send, validate
- Version: 2
| Severity | When |
|---|---|
| Our bug | Either link missing from the footer or the plain text: our bug. |
| Added for you | We changed the footer’s colors or size so it reads. |
How to fix it
- Nothing, if we changed it for you. To choose the change yourself, edit the footer’s colors or text size.
- For our bug: try again, and contact support if it keeps happening.
compliance.address: Your postal address
Marketing email must show your organization’s name and postal address, and we put them in the footer. Transactional email shows them too unless you turn them off. When transactional email’s footer colors or size were too faint or small to read, we change them, and this says what we changed.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 3
| Severity | When |
|---|---|
| Our bug | Marketing email without them after we add them: our bug. |
| Added for you | Transactional email: we added them. |
| Added for you | Transactional email: we changed the footer’s colors or size so it reads. |
How to fix it
- Nothing, if they were added for you. In transactional email, you can leave them out in the footer’s settings.
- Nothing, if we changed the footer for you. To choose the change yourself, edit the footer’s colors or text size.
compliance.from: The sender’s name and address
The sender name includes a web or email address that isn’t the one you send from, which inboxes treat as impersonation. Or marketing email comes from a no-reply address, so readers can’t answer.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | A sender name with another address in it. |
| Tip | Marketing email from a no-reply address. |
How to fix it
- Use your organization’s name as the sender name, and send from an address someone reads.
compliance.subject_reply: A subject that looks like a reply
The subject starts with “RE:” or “FW:” (or the same in another language), making the email look like part of a conversation. Regulators treat that as misleading.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Marketing email. |
| Tip | Transactional email. |
How to fix it
- Take it out of the subject.
compliance.tx_promotional: Transactional email that reads like marketing
A transactional email has offers in it, such as a discount code, a sale, or “Shop now”. Marketing needs an unsubscribe link and people’s agreement, so it can’t go out as transactional.
- For: transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Transactional email. |
How to fix it
- Take the offers out, or send it as marketing email.
compliance.ai_marking: Marked as written with AI
Parts of this email were written with AI, so we mark it as AI-generated in its headers.
- For: marketing, transactional email
- Runs at: publish and send
- Version: 1
| Severity | When |
|---|---|
| Added for you | At send, when AI wrote part of the email. |
How to fix it
- Nothing: it’s a note.
Accessibility
Everyone should be able to read your email, including people using screen readers and people on small screens. None of these stops a send.
a11y.main_heading: No single main heading
Screen readers use the main heading (level 1) to say what an email is about. An email with headings should have exactly one. An email with no headings at all, such as a letter, gets only a tip.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Headings, but none at level 1 or more than one. |
| Tip | No headings at all. |
How to fix it
- With no main heading: make the first heading level 1. The finding’s fix does it.
- With several: keep one at level 1 and make the rest level 2. Each finding’s fix does it for its heading.
a11y.heading_order: A heading skips a level
A heading is more than one level below the one before it, such as a level 3 right after a level 1. Screen reader users move through an email by its headings, and a gap suggests something is missing.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | Any skipped level, in text blocks or your own HTML. |
How to fix it
- Change the heading to the next level down. The finding’s fix does it.
a11y.contrast: Text that’s hard to read
Text, a link, or a button label doesn’t stand out enough from what’s behind it, by the WCAG 2.2 contrast ratio in light mode. Text over a background image is checked against the section’s background color, which shows while images are off. Dark mode has its own checks.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Under {a11y.min_contrast}:1, or {a11y.min_contrast_large}:1 for large text (24 px, or 18.66 px bold). |
How to fix it
- Use the suggested color: the nearest one that passes. When the color is set on the element, the finding’s fix applies it.
- Or change the background, or pick a brand kit color with more contrast.
a11y.small_text: Small text
Body text this small is hard to read on a phone. The footer is left out: it’s small by design.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | Under {a11y.min_body_px} px. |
How to fix it
- Make the text larger. When the size is set on the element, the finding’s fix sets it to {a11y.min_body_px} px.
a11y.text_sizes: Many text sizes
The email uses a lot of different text sizes, which makes it look busy and harder to scan. Your brand kit’s sizes for headings and body text are usually enough.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | More than {a11y.max_text_sizes} sizes, the footer left out. |
How to fix it
- Use your brand kit’s sizes instead of setting one on each piece of text. The finding lists the sizes.
a11y.link_text: Link text that doesn’t say where it goes
A link or button says only “click here”, “read more”, or the same in the email’s language. Screen reader users often list an email’s links on their own, where words like that say nothing.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | The whole link text is one of those phrases. |
How to fix it
- Say what’s at the other end, such as “See the class schedule”.
a11y.table_header: A table column without a header
Every table has a header row, and some of its cells are empty. On phones, each value is shown with its column’s header as a label, and so is the plain-text part, so a column without one has values with no label.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Any empty header cell. |
How to fix it
- Give every column a header.
a11y.lang: The language doesn’t match the text
The email’s language setting doesn’t match the language its text is in. Screen readers pronounce text by that setting, and some inboxes use it to offer a translation. We only say so when the text clearly reads as another language.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | The text clearly reads as another language, or is in another script. |
How to fix it
- Change the email’s language. When we can tell which language it is, the finding’s fix sets it.
a11y.glyphs: A font without the characters it needs
Some text is in a writing system, such as Hebrew, Arabic, or Thai, that its font has no characters for, so inboxes show that text in a different font. Another font on our list has them.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | When another font on our list covers the writing system. If none does, inboxes use the reader’s own. |
How to fix it
- Change the font to the one the finding names, for this text or for the whole email.
Images
Many inboxes hide images until the reader allows them, and large files load slowly on phones. These checks look at what each image says, weighs, and needs.
img.alt_quality: An image description that doesn’t describe it
An image’s description (its alt text) is a file name, a web address, or a word like “image” or “logo” on its own. People using screen readers, and anyone with images off, get the description instead of the image.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Any such description. |
How to fix it
- Say what the image shows, or what it’s for, in a few words.
- If it’s only decoration, mark it decorative instead.
img.too_large: A large image file
A large image loads slowly on phones and mobile data, and some readers give up before it does.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Over 1,000,000 bytes. |
How to fix it
- Use a smaller file: a JPEG for photos, saved at the size it shows at, or twice that for sharp screens.
img.total: Heavy images in total
The email’s images add up to a lot to download, which is slow on mobile data. Each file counts once.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | Over 3,000,000 bytes in all. |
How to fix it
- Use fewer images, or smaller files.
img.soft: An image that may look soft
The image file isn’t much wider than the space it fills, so it may look blurry on sharp (retina) screens, which need about twice the pixels.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | Under 1.5 times as wide as the space. |
How to fix it
- Use a file about twice as wide as the space. The finding suggests a width.
img.no_width: An image in your HTML with no width
An image in your own HTML has no width in pixels. Classic Outlook then shows it at the file’s full size, which can break the layout.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | An <img> without a pixel width attribute. |
How to fix it
- Add a
widthattribute in pixels. The finding suggests one when we know the file’s size.
img.format: An image format some inboxes can’t show
WebP, AVIF, HEIC, SVG, BMP, and TIFF images don’t show as they are in every inbox we test against, by caniemail.com’s data: classic Outlook can’t show WebP, AVIF, HEIC, or TIFF, and Gmail turns SVG into a flat picture. The finding names the inboxes. Uploaded images are converted for you; this is for images from other websites or your own HTML.
- For: marketing, transactional email
- Runs at: save, test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Any such image. While drafting it goes by the address’s file extension; at a test send and a send, by the file itself. |
How to fix it
- Use a JPEG, PNG, or GIF, or upload the image.
img.animated: An animated image
Classic Outlook shows only the first frame of an animated GIF.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | Any animated image. |
How to fix it
- Make sure the first frame says what matters on its own.
img.heavy: Mostly images
Images cover most of the email. Readers with images off see much less, and spam filters treat image-heavy mail with suspicion. Images we don’t know the size of aren’t counted.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Marketing email whose images cover more than 0.6 of its area. |
| Tip | The same, in transactional email. |
How to fix it
- Move the key words out of images and into text.
img.only: Almost all images
The email has images but almost no text outside the footer. Many inboxes hide images at first, so readers see next to nothing, and spam filters distrust it.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Fewer than 100 characters of text, with at least one image. |
How to fix it
- Add text that says what the email is about.
img.external: An image from another website
The image loads from another website, or from an address a merge tag fills in. If that site moves or removes it, it breaks in every copy of the email, even ones already sent.
- For: marketing, transactional email
- Runs at: save, preview, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | Any image that isn’t uploaded, except your brand kit’s logo. |
How to fix it
- Upload a copy, so it’s served with your email.
Your own HTML
HTML you write yourself is checked when you save it, and anything unsafe is refused. These checks catch what saves but may not show as you meant, in the inboxes we test against.
html.balance: An element left open
Your HTML opens an element or a comment it never closes. We close it at the end of your HTML, so it can’t spill into the rest of the email, but the result may not be what you meant.
- For: marketing, transactional email
- Runs at: save, preview, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Any element or comment left open. |
How to fix it
- Add the closing tag where it belongs. The finding points at where the element opens.
html.unsupported: HTML or CSS some inboxes don’t support
Your HTML uses an element, attribute, or CSS property that an inbox we test against doesn’t support, by caniemail.com’s data. The finding names those inboxes. What they support only partly is one tip for the whole piece of HTML.
- For: marketing, transactional email
- Runs at: save, preview, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Not supported in at least one inbox we test against. |
| Tip | Only partly supported. |
How to fix it
- Use something that works everywhere, or check the email in a test send to those inboxes.
html.width: Too wide for a phone
Part of your HTML has a fixed width a phone can’t fit, and phones can’t scroll sideways in an email.
- For: marketing, transactional email
- Runs at: save, preview, validate
- Version: 1
| Severity | When |
|---|---|
| Warning | Wider than 600 px, or wider than 375 px with no percentage or max-width. |
How to fix it
- Use 600 px or less with a
max-width, or a percentage width.
html.layout_table_role: A table without a role
A table in your HTML has no header cells and no role="presentation", so screen readers read it cell by cell, as if it held data.
- For: marketing, transactional email
- Runs at: save, preview, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | Any such table. |
How to fix it
- If it’s for layout, add
role="presentation". If it holds data, give it header cells (<th>).
html.font: A font not on our list
Your HTML names a font that isn’t one of the fonts installed with Windows and macOS, so most readers see the next font in its list instead.
- For: marketing, transactional email
- Runs at: save, preview, validate
- Version: 1
| Severity | When |
|---|---|
| Tip | A font list that starts with a font not on our list. |
How to fix it
- Start the font list with a font from our list, or check that the fallback looks right.
html.color: Colors rewritten in your HTML
Your HTML wrote some colors as names, RGB or HSL values, or three-digit hex. We wrote them as six-digit hex so dark mode can adjust them; they look the same.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Added for you | When we rewrote at least one color. |
How to fix it
- Nothing: it’s a note.
html.color_added: Colors added to your HTML
A link in your HTML had no color, or some text sat on a background of its own with no color. A link with no color shows each inbox’s default blue, which dark mode can’t adjust, so we gave it your email’s link color (your brand kit’s, unless the email sets its own). Text on its own background keeps the color it already had, written out so dark mode can adjust it for that background.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 1
| Severity | When |
|---|---|
| Added for you | When we gave at least one link or background’s text a color. |
How to fix it
- Nothing: it’s a note. To use another color for a link, give the link a color of its own.
Dark mode
Mail apps in dark mode change your email’s colors, each in its own way, and as far as we know, leave images as they are. We predict what Apple Mail, Gmail, and Outlook do from published models, and these checks warn where light mode works and dark mode likely doesn’t. Each finding names the apps and never stops a send, and in the API its `confidence` says how sure the prediction is.
dark.text_contrast: Text that’s hard to read in dark mode
Text, a link, or a button label reaches the contrast it needs in light mode ({a11y.min_contrast}:1, or {a11y.min_contrast_large}:1 for large text), but not after a mail app’s dark mode changes its colors.
- For: marketing, transactional email
- Runs at: save, preview, validate
- Version: 2
| Severity | When |
|---|---|
| Warning | Under the contrast it needs in at least one app’s dark mode. |
| Tip | Only in the apps that use our dark-mode colors, which we’re still tuning: a note, since it isn’t yours to fix. |
How to fix it
- Use the suggested color: the nearest one that passes in light mode and in each app we predict.
- Set the color for dark mode in your brand kit, for the apps that use it.
dark.image_vanishes: An image that disappears in dark mode
An image with a transparent background shows against a light background but not against the one a mail app puts behind it in dark mode, like a black logo on a dark gray page. We read it from the image’s own pixels. That analysis is coming, for uploads and, when sending, for images from other sites; until it’s in, this check finds nothing.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 2
| Severity | When |
|---|---|
| Warning | Most of the image no longer stands out from the background in at least one app. |
| Tip | An image with a white border shows as a white box on a dark background. |
How to fix it
- Add a light outline around the image.
- For your logo, add a version for dark backgrounds to your brand kit.
- For a white box, use an image with a transparent background.
dark.text_over_image: Text over an image in dark mode
Mail apps change the color of text but never the image behind it, so text that was readable over a picture may not be. We check it against the background color shown with images off and, once we analyze images’ pixels, against the image’s lightest or darkest part.
- For: marketing, transactional email
- Runs at: save, preview, test send, publish and send, validate
- Version: 2
| Severity | When |
|---|---|
| Warning | Readable in light mode and not in at least one app’s dark mode. |
How to fix it
- Put the text on a solid panel, which changes color with it.
- Move the text below the image.
dark.divider: A line that disappears in dark mode
A divider or border you can see in light mode almost disappears in dark mode. Classic Outlook darkens the background but leaves lines as they are, so a dark line vanishes there. A button’s outline needs 3:1.
- For: marketing, transactional email
- Runs at: save, preview, validate
- Version: 2
| Severity | When |
|---|---|
| Warning | At least 1.5:1 in light mode and under 1.3:1 in an app’s dark mode. |
How to fix it
- Use the suggested color.
- Draw the divider as a thin band of background color instead.
dark.brand_shift: A brand color that changes in dark mode
Gmail and classic Outlook change colors themselves in dark mode, and a brand color can come out as another color, like yellow turning brown. Apple Mail and Outlook.com use the dark-mode colors in your brand kit instead.
- For: marketing, transactional email
- Runs at: save, preview, validate
- Version: 2
| Severity | When |
|---|---|
| Warning | It becomes another named color. |
| Tip | Only its lightness flips, like navy turning light blue. |
How to fix it
- Preview it in dark mode in the named apps. If you’re happy with it, leave it.
- For a decorative area whose color matters, an image keeps it in every app, since apps leave images as they are. Keep text off it.
dark.unmodeled: HTML we can’t predict in dark mode
Your HTML uses styles we can’t work out, such as CSS variables, calc(), or selectors our model doesn’t follow, so we can’t say what dark mode does to it.
- For: marketing, transactional email
- Runs at: save, preview, validate
- Version: 2
| Severity | When |
|---|---|
| Tip | Any part we can’t predict. |
How to fix it
- Send yourself a test and open it in the named apps with dark mode on.