Reading and checks
Every tab of an open email, the extracted codes and links, and the quality checks.
Try it in the demo.
Open any email in Mail to see these tabs. They answer "what exactly did my app send?" and "will it look right for the people who get it?".
What it solves
- An email looks fine in your client but breaks on a phone or in Outlook.
- A link in the email points at
localhost, a wrong domain or a page that does not exist. - An attachment or inline image is missing, or the wrong file is attached.
- A header your provider or the recipient's server needs (
List-Unsubscribe,Reply-To) is missing.
The extracted panel
Above the tabs, one-time codes and links found in the email are listed, each with Copy and Open. Codes are found with the patterns in Settings > OTP patterns; add your own if your app's format is not found (Advanced: OTP patterns).
The tabs
| Tab | Shows | Use it to |
|---|---|---|
| HTML | the email in a sandboxed frame. Desktop width, Tablet width and Mobile width resize it; Load remote images fetches images from the web (off by default, so opening an email never calls out); Screenshot saves a PNG | check the layout at each width; attach a screenshot to a bug |
| Text | the plain-text part | check people without HTML get a readable email |
| Source | the raw message, highlighted | see exactly what your app sent, byte for byte |
| Headers | every header as a table, with warnings for a missing or broken List-Unsubscribe | check Reply-To, Message-ID, custom headers |
| Attachments | each file with its size and type, image thumbnails, download | check the right file went out |
| MIME | the tree of parts: type, encoding, size, download per part | debug inline images (cid:) and multipart problems |
| Checks | client support for the HTML and CSS used, link and image check results, the spam score (when SpamAssassin is set in Settings > Email), and unsubscribe header checks | fix what Outlook or Gmail will not show, broken links, spammy content |
| Timeline | received (with a link to the SMTP session or API request), opened, clicked, delivered, bounced, complained, released, replied, forwarded, and each webhook sent | follow everything that happened to this email |
| Template data | for provider API sends that used a template id: the id and the dynamic data | check the data your app passed to SendGrid or Postmark templates |
Opened events
Opening an email in the UI counts as the recipient opening it: SendMock fires email.opened, which your open tracking can receive through a rule. The header shows "Opened N times" with the history. Turn this off, or fire only on the first open, in Settings > Opened events.
- First open only (the default): only the first view counts. Viewing the email again changes nothing: no event, and the open count stays the same.
- Every open: each view fires
email.openedwithfirst_open: falseand a higheropen_count. - Simulate open, the API, CLI and MCP always fire, whatever the setting.
- Mark unread keeps the open count and the first open time.
Each open is kept with its time, its source (ui, manual or api) and the user agent that sent it, if any.
Clicked events
A click on one of an email's links counts as the recipient clicking it, as a provider's click tracking reports it: SendMock fires email.clicked with the link, first_click (the first click on that link), click_count (all clicks on the email), the source and the user agent. The email's Timeline shows each click.
- A link clicked in the HTML preview opens in a new tab as before, and fires
email.clickedwithsource: "ui". A click is always on purpose, so there is no setting to turn this off. - Simulate > Click a link lists the email's links; choosing one fires
email.clickedwithsource: "manual". - The API, CLI and MCP fire it for a link you name, which must be one of the email's links; any other URL is refused with
unknown_link, since a provider only tracks the links it rewrote.
An email provider's webhook template (SendGrid, Mailgun, Postmark, Resend, Amazon SES, Brevo, SparkPost) sends the click in that provider's own shape, so your app's click handler receives what the real provider would send.