Loading course…

Read as text

I was buying a whole extra domain for every product I shipped, purely so it could send email. Renewals, DNS, verification, all of it multiplied by the number of products. It turns out none of that was necessary — and the fix is one DNS record deep. The whole thing in one line: your email provider wants an MX record on the domain it sends from, and Google Workspace already owns the MX on your root domain. Those two facts collide — and the answer is a sending subdomain, not another domain.

WHY YOUR ROOT DOMAIN FIGHTS YOU Set up a transactional provider on yourproduct.com and it asks for three things: RECORD

What it's for

Txt (spf)

says which servers may send as you

Txt (dkim)

signs your mail so it can be verified

receives bounces and complaints — this is the one that breaks everything

If you run Google Workspace on that domain, your MX already points at Google so your actual inbox works. Point it at the email provider instead and you stop receiving mail. Keep Google's and the provider never verifies. There is no version of this where one root domain does both jobs. That's the wall I hit. My read was “fine, this product needs its own domain” — so I bought one. Then the next product needed one. An extra domain per product, bought only so email could leave the building.

The fix nobody tells you about

Send from send.yourproduct.com A subdomain has its own MX record, completely independent of the root. Your inbox on you@yourproduct.com keeps working through Google, untouched, while send.yourproduct.com carries the provider's MX, SPF and DKIM and does the sending. Mail still arrives from your brand. No new domain, no renewal, no extra invoice.

1 Add the sending subdomain

Your email provider

In your provider (Resend, Postmark, SES — same shape everywhere), add the domain as send.yourproduct.com , not yourproduct.com . It hands you back three records. Nothing you paste will touch your root domain's MX, so your Workspace inbox can't break.

2 Put the records in DNS

Where it gets boring

Three records, by hand, per product, in a dashboard. That's the part worth automating — and the part this guide exists for.

3 Send from a name, not from “noreply”

Deliverability

From: Product Name <hello@send.yourproduct.com> . Nobody has ever been pleased to hear from noreply@ . Reply-To: your real inbox on the root domain. Replies land in Google Workspace where you already read mail. One subdomain per product. A product that gets marked as spam damages only its own sending reputation, not every other product you run.

4 Set DMARC once, on the root

The bit people skip

One record on the root domain covers every subdomain under it. Start in report-only mode, read what comes back for a week, then tighten. # TXT record at _dmarc.yourproduct.com v=DMARC1; p=none; rua=mailto:you@yourproduct.com; pct=100

Move p=none to p=quarantine once the reports look clean. Gmail and Yahoo now expect bulk senders to have DMARC at all — missing it costs you the inbox.

STOP DOING IT BY HAND Once you've done this twice you'll never want to do it again. Both sides have an API — the email provider that issues the records, and the DNS host that stores them. Ninety lines of glue turns a twenty-minute dashboard chore into one command. SETUP-EMAIL.MJS — NEW PRODUCT, SENDING DOMAIN LIVE // node setup-email.mjs myproduct.com const root = process.argv[2] const subdomain = `send.${root}` // 1. ask the email provider for the domain + its DNS records const created = await (await fetch('https://api.resend.com/domains', { method: 'POST', headers: { Authorization: `Bearer ${process.env.RESEND_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ name: subdomain, region: 'us-east-1' }), })).json() // 2. write every record it asked for into DNS (Cloudflare here — // swap this call for your own host, the shape is the same) for (const r of created.records) { await fetch(`https://api.cloudflare.com/client/v4/zones/${process.env.ZONE}/dns_records`, { method: 'POST', headers: { Authorization: `Bearer ${process.env.CF_TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ type: r.type, // TXT / MX name: r.name, content: r.value, priority: r.priority, ttl: 60, proxied: false, }), })

// send, resend._domainkey.send … // MX only // never proxy mail records

} // 3. poll until the provider says verified for (let i = 0; i < 20; i++) { const d = await (await fetch(`https://api.resend.com/domains/${created.id}`, { headers: { Authorization: `Bearer ${process.env.RESEND_KEY}` } })).json() if (d.status === 'verified') { console.log('live:', subdomain); break } await new Promise(r => setTimeout(r, 15000)) }

Two things that will bite you: leave the records unproxied (an orange cloud on a mail record breaks it), and don't trust the dashboard's green tick as proof — send one real email to a real inbox and read the headers. SPF: pass , DKIM: pass , DMARC: pass , or it isn't done.

THE CHECKLIST, PER PRODUCT Roughly two minutes once the script exists. Tick all seven before you let real signups through. Sending subdomain, not the root. send.yourproduct.com . Your Workspace MX is never touched. All three records in place — SPF, DKIM, and the provider's MX on the subdomain. Unproxied. DMARC on the root, starting at p=none with a reporting address you actually read. From a human name, Reply-To your real inbox, so a reply reaches you instead of vanishing. Send a real test to Gmail and check the headers say pass, pass, pass. The dashboard tick is not evidence. Handle bounces. That MX exists for a reason — a hard bounce means stop sending to that address, permanently. Keep login and receipt mail separate from marketing. Different subdomain, or one unsubscribe complaint takes your OTPs down with it.

WHAT THIS ACTUALLY SAVED A domain per product, every year, forever — plus the renewal you forget until the email stops. Now every product sends from its own name under one domain I already own, and standing up a new one is a single command. The wider lesson, and the reason I keep making these: the wall you hit is usually one DNS record wide. I spent real money working around a problem that had a documented fix, because the error message said “verification failed” and not “use a subdomain”.

Building products this way? This is the kind of thing I work through on camera every day — the plumbing nobody writes a tutorial for. More guides and the courses are at sanskartiwari.io.

One domain, every product — transactional email — free guide by Sanskar Tiwari - Sanskar Tiwari