Skip to content

Read the raw .eml, not the rendered preview: three email-template defects that are invisible in a mail client

Context

A CRM (Attio) was about to run a 93-recipient cold-outreach campaign from a merge template. One .eml was exported and read as raw MIME rather than opened in a mail client. Three separate defects fell out, none of which a rendered preview shows.

1. Empty merge variables are camouflaged by quoted-printable

The plaintext part contained:

Calculator: =20
Google Sheet: =20

=20 is quoted-printable for a single space. So those lines are literally Calculator: and Google Sheet: — label, colon, nothing. The HTML part confirmed it: <div>Calculator: <br/></div>, no <a> element at all.

Why this survives review: in a mail client the label renders normally and the eye supplies the missing value, especially in a signature-heavy template. And in the raw source, =20 looks like content — it is three visible characters sitting exactly where a URL should be. The subject line was "I turned your video into a calculator", and the email contained no calculator link.

Two more blanks were only detectable from prose shape, not from any marker:

  • A body line began " Plug in numbers so your audience can act on your advice." — leading space, and a sentence with no subject. That is a variable that rendered empty immediately before it, leaving the space that followed the placeholder.
  • The greeting was a generic "Hey there!" where a salutation variable should have produced a name.

So: a leading space, or a sentence that starts mid-thought, is the signature of an empty merge field. There is no error, no placeholder text, no {{var}} left behind.

The HTML part carried real hrefs:

check out this <a href="https://www.loom.com/share/…">1m intro</a>
and let's <a href="https://cal.com/…">hop on a call</a>!

The plaintext part rendered as check out this 1m intro and let's hop on a call!both URLs gone. The generator stripped tags instead of resolving anchors to text (url).

The asymmetry is the tell: two other links in the same message survived into plaintext, because their anchor text was the URL (https://finfam.app/about). So descriptive anchors lose their destination and bare URLs don't — which means a spot-check that happens to land on a bare URL concludes the plaintext is fine.

Any recipient or client rendering text/plain gets calls to action with nothing to click.

3. Headers settle "how does this vendor actually send?" — and which copy you are holding

The question that mattered for DMARC policy was whether the CRM sends through its own SMTP (a new, unaligned source) or through the connected mailbox. The headers answer it outright:

Received: from <id> named unknown by gmailapi.google.com with HTTPREST
Message-Id: <CANt+7MS…@mail.gmail.com>

gmailapi.google.com with HTTPREST, plus a genuine @mail.gmail.com Message-Id, means the CRM hands the message to the Gmail API on the OAuth-connected mailbox. Delivery therefore egresses through Google Workspace and inherits its SPF/DKIM alignment — no new sending source, no SPF include to add.

But note what the file did NOT contain: no Authentication-Results, no DKIM-Signature, no Received-SPF. Those are added by the receiving side (and DKIM by the sending gateway on egress). Their absence means this was the copy as handed to the API — a Sent-folder or vendor-stored copy — not the delivered copy. So it establishes the path and says nothing about the outcome.

To verify alignment you need the message as received: open the inbox copy and read Authentication-Results for spf= / dkim= / dmarc=. Exporting from Sent and concluding "DKIM is missing" is a false alarm, and concluding "alignment is fine" is unsupported.

Procedure before any merge-template campaign

  1. Export one .eml and read it as text. Grep for =20$ and for : *$ on label lines.
  2. Diff the text/plain part against the text/html part for link count — every href in HTML should appear as a URL in plaintext.
  3. Send one test to a record other than your own. A self-addressed test merges against your own contact record, which legitimately has none of the campaign's custom attributes, so every variable renders empty and the test cannot distinguish "template broken" from "wrong record".
  4. Read the Received chain to confirm the egress path, then read Authentication-Results on the received copy to confirm alignment.
  5. Check for List-Unsubscribe. Absent is a spam-complaint risk regardless of whether volume triggers a provider requirement.
No signals yet