Email sandbox

On this page

Email quality checks

Every captured message has a Quality tab in the message viewer. It combines three checks, each with a summary badge at the top:

  • Links: every link and image in the HTML and text parts is requested, to find broken links, timeouts, DNS failures and redirect chains, plus problems visible without a request, such as staging hosts.
  • Accessibility: a WCAG-oriented review of the HTML, with a score out of 100.
  • Authentication: SPF, DKIM, DMARC, ARC and BIMI results, explained in plain language with the DNS records to add or fix.

The HTML preview also has a color mode selector to see the message as dark-mode mail clients show it.

Plans

Check Free Team Business Enterprise
Accessibility check, dark-mode preview Yes Yes Yes Yes
Link check, authentication results No Yes Yes Yes

On a plan without them, the link and authentication endpoints answer 402 with { "code": "feature_unavailable", "limit": "qualityChecks", "upgradeUrl": "/billing" }. Any team member, and any API key (read-only ones included), can read the checks.

The link check runs in the background. The first request starts it:

bash
curl -H "Authorization: Bearer $TESTLIE_API_KEY" https://app.testlie.com/api/messages/$MESSAGE_ID/links
# {"status":"running","startedAt":"2026-10-23T10:30:05.000Z"}

Poll the same URL until it answers { "status": "done", "report", "cached" } or { "status": "failed", "error" }. The report is kept with the message; ?refresh=1 runs the check again (a refresh within 10 seconds of the last result returns that result). A check that stops answering is restarted after 90 seconds.

How URLs are requested:

  • At most 50 unique http(s) URLs per message, 5 at a time, 5 seconds per URL in total.
  • HEAD first, then GET if HEAD fails or answers 4xx or 5xx (many servers mishandle HEAD). Bodies are never downloaded.
  • Up to 5 redirects are followed and recorded, with the URL and status of each hop.
  • Hosts that resolve to private, loopback or link-local addresses are not requested; they are reported as "Not checked". No cookies or credentials are sent.

Each URL in report.links has url, occurrences (where it appears: kind link or image, source html or text, the visible text and the line), checked, status, finalUrl, redirects, method, timeMs and problems (code, level, message):

Code Level Meaning
broken error The final response is 4xx or 5xx
timeout error No response within 5 seconds
dns error The host does not resolve
unreachable error The connection was refused or reset
too_many_redirects error More than 5 redirects
blocked warn Not requested: a private or loopback target
insecure warn An http:// link, or an https:// link that redirects to http://
dev_host error A development or staging host leaked into the email: localhost, private IPs, *.local, *.localhost, *.internal, *.test, staging., dev., qa., uat. and preview. subdomains, -staging names, non-standard ports
mismatched_text warn The visible link text is a URL or domain other than where the link goes (it looks like phishing)
invalid_mailto error A mailto: link without a valid address
invalid_tel error / info A tel: link with invalid characters or digit count (info: no + country code)
missing_utm info A campaign-looking message (with List-Unsubscribe, Precedence: bulk, or other UTM-tagged links) has a link without utm_source
empty, relative, javascript, malformed error An unusable link

Accessibility

GET /api/messages/:id/accessibility answers at once with { report, cached }. The report is kept with the message; ?refresh=1 computes it again. It has a score (0 to 100), summary counts, stats (text characters, images, links, headings, tables, text per image) and findings grouped by rule, each with a title, level, help (how to fix it), count and up to 10 items (a snippet of the element, its line and a detail).

Rule Level What it checks
html-lang error <html lang> is missing
doc-title warn <title> is missing or empty
img-alt error An <img> without alt
img-decorative warn Spacers, 1×1 pixels and other decorative images without alt=""
img-alt-filename warn An alt that is a file name (IMG_1234, hero.png)
link-name error A link with no text, image alt or aria-label
link-vague warn Link text such as "click here", "here", "read more" or "learn more"
table-layout warn A table without th, caption or thead, and without role="presentation"
contrast error Text below WCAG AA contrast: 4.5:1, or 3:1 for large text (24px, or 18.7px bold)
font-small warn Text below 14px
heading-order warn Skipped heading levels (h2 to h4)
all-caps warn Long passages in capitals, including text-transform: uppercase
text-part warn The message has no text/plain part
image-ratio warn Mostly images: under 100 characters of text, or under 50 per image

Colors come from what email clients honour: inline color, background and background-color, <font color> and bgcolor, inherited from parent elements. Text over background images or gradients is skipped rather than guessed.

Authentication results

GET /api/messages/:id/auth works like the link check: it runs in the background, answers { "status": "running" } until the result is ready, and takes ?refresh=1. DNS queries time out after 3 seconds each and the whole check after 20 seconds; a timeout becomes a temperror result, not an error.

The report has:

Field Contents
context The connecting client's ip, its helo name, the envelope mailFrom, the fromDomain, and privateIp
overall pass, warn, fail or neutral, driven by DMARC
spf, dkim, dmarc, arc, bimi One section each, with result (the raw keyword), verdict, summary (one plain sentence), details, fix and the record found in DNS. dkim lists each signature (domain, selector, algorithm, result, aligned); dmarc adds the policy and alignment; bimi the location
authResults The Authentication-Results header a receiving server would add

fix suggests the DNS records to publish: your SPF record with the sending IP added, the <selector>._domainkey record, or a starter _dmarc record.

Reading the results for captured mail. Messages reach Testlie from your application or relay, not from your production mail provider, so SPF is evaluated for whatever server connected to Testlie. An SPF fail may only mean that server is not in your SPF record, which matters only if it also sends your production mail. Messages sent from private addresses (CI, Docker) get a note saying SPF is not meaningful for them. DKIM and DMARC do not depend on the connecting server: they are the results to act on.

Dark-mode preview

The preview's color mode selector has three settings:

Mode What it does
Light The message as sent
Dark Applies the message's own @media (prefers-color-scheme: dark) styles, as Apple Mail and Outlook.com do
Dark (inverted) Inverts a light design and re-inverts images, as the Gmail and Outlook apps do

Over the API, add ?dark=respect or ?dark=invert to GET /api/messages/:id/html or GET /api/messages/:id/preview-url.

  • Spam reports: spam score, inbox placement and the Gmail/Yahoo bulk-sender checklist.
  • Viewing messages: which HTML and CSS features each mail client supports.
  • Email API: every message endpoint.