All articles
SalesJuly 10, 202610 min read

Cold Email Without Burning Your Domain

Deliverability is an engineering problem, not a copywriting one. SPF, DKIM, DMARC, suppression lists, one-click unsubscribe — and the GET request that quietly unsubscribes everyone.

By Niraj Kumar

Most advice about cold email is about the writing. Better subject lines, shorter paragraphs, a sharper call to action.

That advice assumes a fact not in evidence: that your email arrived.

A domain with a broken sending setup lands in spam regardless of how good the copy is. And the failure is silent — nobody bounces, nobody complains, your dashboard shows 400 emails "delivered", and your reply rate is zero. You conclude the copy is bad. You rewrite it. It's still zero, because the copy was never the problem, and now you've spent three weeks proving it.

Worse: a sending domain that gets this wrong doesn't just fail at cold email. It fails at all email. Password resets, receipts, invoices — the same domain, the same reputation. You can destroy your transactional email in a fortnight by running a careless outbound campaign against it. That's the actual stake here, and it's why this is an engineering problem before it is a marketing one.

Part 1: Prove you are who you say you are

Three DNS records. Without them, you're an anonymous stranger claiming to be your own domain, and every serious mail provider treats that claim with the suspicion it deserves.

Record What it says Failure mode without it
SPF "These servers may send mail as me" Anyone can forge your domain; receivers distrust all of it
DKIM "This message is cryptographically signed by me" No proof the message wasn't altered or forged
DMARC "Here's what to do when SPF and DKIM fail" Nobody knows how to treat a forgery, so they guess

SPF

A TXT record listing who's allowed to send on your behalf:

v=spf1 include:_spf.google.com include:sendgrid.net -all

The -all at the end means "and nobody else — reject the rest." The softer ~all (softfail) is what most people ship, and it means "probably reject, up to you", which is a shrug where a policy should be.

The trap: SPF has a hard limit of 10 DNS lookups. Each include: costs at least one. Add Google, your ESP, your CRM, your helpdesk and your invoicing tool and you will silently blow past ten — at which point SPF returns permerror and, on many receivers, stops working entirely. It doesn't warn you. Check your lookup count.

DKIM

Your ESP gives you a public key to publish as a DNS record; it signs outgoing mail with the matching private key. A receiver verifies the signature and knows the message genuinely came from you and wasn't modified in transit.

Nothing subtle here — just do it, and use a 2048-bit key.

DMARC

DMARC is where the other two become policy. It tells receivers what to do when a message claiming to be from you fails both checks, and — the underrated part — it asks them to send you reports.

Start in monitoring mode:

v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com

p=none changes nothing about how mail is treated. It just starts the reports flowing. Read them for a month, find the legitimate senders you'd forgotten about (there's always one — the invoicing tool, the helpdesk, a marketing site from 2019), fix them, then tighten to p=quarantine and eventually p=reject.

Going straight to p=reject before reading a month of reports is how people discover that their payroll system was sending from their domain, by turning it off.

Part 2: Do not send from your shared address

Here's the mistake that isn't in any checklist, because it isn't a DNS record — it's an architecture decision, and it's the one I feel most strongly about.

If your product sends transactional mail from hello@yourdomain.com — alerts, digests, receipts, password resets — then cold outreach must not go out from that address, or from that domain.

One recipient hitting "this is spam" on a cold email damages the reputation of the sending domain. That reputation is shared with every other message that domain sends. So a single stranger's annoyance at an unsolicited pitch degrades the deliverability of your password reset emails — which real, paying customers are waiting for, and which they will now find in spam, if at all.

The blast radius of a cold-email complaint should be the cold-email domain, and nothing else.

The rule that follows: use a separate, verified sending identity for outreach. A subdomain (mail.yourdomain.com) or a dedicated domain, warmed separately, with its own reputation to lose. If you're using a platform to send on your behalf, it should require you to bring your own verified sender rather than quietly sending from a shared pool — because a shared pool means you inherit the reputation of every other customer on it, including the one running a scam this morning.

Part 3: The legal minimum is not optional, and it's small

CAN-SPAM in the US, and its equivalents elsewhere, require less than people fear. For commercial email, the parts that actually bite:

  • A real physical postal address in every message. Not a placeholder. Not "coming soon". An actual mailing address. This surprises people every time, and it is not negotiable.
  • A working opt-out mechanism, honoured promptly — within 10 business days under CAN-SPAM, but there is no reason it shouldn't be instant.
  • No deceptive headers or subject lines. The From must be who it says it is; the subject must describe the email.
  • Identify the message as an ad if the relationship isn't obvious.

Note what's not on that list: consent. CAN-SPAM is an opt-out regime — cold email is legal in the US. GDPR and PECR in Europe are a different matter, and the short version is: don't assume the US rules apply to a recipient in Berlin. Know which regime your recipient sits under.

The engineering consequence of the address requirement is worth stating plainly: if your outreach tool lets a model write the whole email, the model will drop the footer. It doesn't know what it's for. A model told to "write a friendly sales email" produces a friendly sales email, not a legally compliant one. The footer — postal address, opt-out link, one line saying why they're receiving this — has to be injected by code, after the model is done, every single time. It cannot be part of the prompt, because a prompt is a request and this is a requirement.

Part 4: The unsubscribe bug that will ruin your week

This is the one I most want people to know, because it's subtle, it's silent, and it looks exactly like a marketing failure rather than a bug.

You put an unsubscribe link in your email:

https://yourapp.com/unsubscribe/abc123

Someone clicks it, your server receives a GET, and you mark them unsubscribed. Simple, obvious, and catastrophically wrong.

Here's why. Gmail, Outlook, and every corporate mail security gateway prefetch links in emails to scan them for malware. They send a GET to every URL in the message before a human has looked at it. So the scanner unsubscribes your recipient the moment the mail arrives. Nobody clicked anything.

The symptom is that your list quietly empties. Your open rates look fine. Your unsubscribe rate is inexplicably enormous, and the natural conclusion is "our copy is terrible and everyone hates us." It isn't and they don't. You wrote a mutating GET.

The rule: a GET must never change state. It's the oldest rule in HTTP and this is the most expensive place to break it.

The correct shape:

  • GET /unsubscribe/<token>renders a confirmation page. Changes nothing. Let every scanner in the world fetch it.
  • POST /unsubscribe/<token>performs the unsubscribe. A form button on that page, or the mail client's own one-click header.

One-click unsubscribe, properly

Gmail and Yahoo now effectively require bulk senders to support one-click unsubscribe. That's two headers, and the second one is the one people forget:

List-Unsubscribe: <https://yourapp.com/unsubscribe/abc123>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

List-Unsubscribe-Post (RFC 8058) is what tells the mail client to send a POST rather than a GET — which is precisely the distinction above, and it's why the header exists at all. Ship the first header alone and you've told every mail client in the world to fetch your mutating URL.

Part 5: Suppression is not a feature you add later

The three things that must immediately and permanently stop mail to an address, with no human in the loop:

  1. A hard bounce. The address doesn't exist. Sending again tells the receiving provider you don't validate your list, which is the defining characteristic of a spammer.
  2. A spam complaint. They hit the button. This is the most expensive signal in email — treat it as final and never contact them again through any channel.
  3. An unsubscribe. Obviously.

All three need to land in a suppression list that is checked immediately before every single send — not just at the moment someone is added to a campaign.

That timing detail matters more than it sounds. A sequence runs over days or weeks. Someone enrolled on Monday receives step 2 on Thursday. If you only checked the suppression list on Monday, you will mail someone on Thursday who unsubscribed on Tuesday — and that is the complaint that costs you, because they already told you to stop and you did it anyway.

One gate, checked at send time, that every path goes through. If the "may I email this person?" check exists in three places in your codebase, those three places will eventually disagree, and the way you'll find out is by mailing someone who unsubscribed.

Wire up the bounce webhook before the first send

Every serious ESP will POST bounce and complaint events to you. Handle them on day one, not in a later phase — a campaign that keeps mailing dead addresses while you "get to it next sprint" is actively burning the domain you're going to need later.

Two details on that endpoint:

  • Verify the signature. An unauthenticated webhook that writes suppressions is a vandalism API — anyone who finds the URL can suppress your entire list. If the signing secret isn't configured, the endpoint should refuse to run, not cheerfully accept anonymous POSTs.
  • Prefer under-sending to double-sending. If a worker dies between "I sent this" and "the provider confirmed it", you cannot know whether the mail went out. Do not retry. A duplicate cold email is worse than a missing one — one is a non-event, the other is the thing people report as spam.

The scoreboard

Before you send anything to anyone:

  • SPF published, ending in -all, under 10 DNS lookups
  • DKIM signing with a 2048-bit key
  • DMARC at p=none, with reports going somewhere a human reads
  • A separate verified sender for outreach — never the transactional address
  • A real physical postal address in the footer, injected by code
  • GET /unsubscribe renders; POST /unsubscribe acts
  • List-Unsubscribe and List-Unsubscribe-Post headers, both
  • The bounce/complaint webhook live and signature-verified, before the first send
  • One suppression check, run immediately before every send
  • A daily send cap you cannot accidentally exceed

Ten items. Most take an afternoon. Skip them and the best cold email ever written lands in a spam folder, and you'll spend a month blaming the subject line.

And then: send fewer, better emails

All of the above is table stakes — it earns you the right to be delivered. It does not earn you a reply.

The thing that earns a reply is relevance, and relevance comes from knowing why you're writing to this specific company today. "I noticed your team was reading our security documentation last week" is a real reason. "I wanted to reach out and touch base" is not — and no amount of DKIM will save it.

Which is why outbound works best when it's triggered by something that actually happened: a company that visited your pricing page three times this week is a reason to write. A name on a purchased list is not. We built the two halves together on purpose — the identification is what makes the email worth sending, and the sending discipline above is what makes it arrive.

The best deliverability strategy is still the boring one: send less mail to people who have a reason to hear from you. Every rule in this post is really just a technical restatement of that.

Stop guessing what to do next

Connect your website and get your first growth action plan today.

No credit card required · Setup in under 2 minutes