The transactional subdomain split
How to isolate sender reputation by routing transactional and marketing email through separate subdomains.
Your app sends three kinds of email: a password reset when someone is locked out, a receipt when a subscription charges, and a monthly newsletter about new features.
All three are “email,” so the natural instinct is to send them the same way, through one domain, one API key, and one from address.
That instinct quietly breaks your product.
When everything shares a domain, the spam complaints a marketing campaign collects can push your password-reset email into the spam folder.
Nothing reports the failure, so to your product team it just looks like signups mysteriously dropped.
Transactional mail vs marketing mail
Section titled “Transactional mail vs marketing mail”Every decision in this lesson depends on which of two categories a message falls into.
Transactional mail is triggered by a specific action the user just took, and it carries information they expect and need to operate their account: a verification code at signup, a password-reset link, a receipt for a charge, a “new sign-in from an unrecognized device” alert. The user did something, and the email is the system responding.
Marketing mail goes out on the sender’s schedule, not the user’s. It’s promotional or informational content the user opted into at some point but didn’t trigger today: the monthly newsletter, a drip campaign welcoming new trial users over their first week, a “we’ve shipped a new feature” announcement to your whole user base, a “we miss you” win-back to someone who’s gone quiet. You decided to send these, not them.
The line is operational, not aesthetic: it changes how you’re allowed to send, not just how the email looks.
There’s a legal dimension. Anti-spam law (CAN-SPAM in the US, and its cousins elsewhere) requires marketing mail to carry a working one-click unsubscribe. Transactional mail is exempt, for a sound reason: a user can’t “unsubscribe” from password resets and keep a working account. The email is part of the account’s machinery, not a campaign. So your transactional subdomain carries no unsubscribe header. Unsubscribe lives on the marketing side of the line.
There’s also a deliverability dimension, which is the one this whole lesson turns on. Mailbox providers, Gmail, Yahoo, and Microsoft, track the reputation of these two streams separately, because the recipient’s expectation pattern differs so sharply between them.
The following exercise gives you eight real sends to sort. A few are deliberately on the edge, because the edges are where this skill gets tested. For each one, ask: did the user trigger it, and do they need it to run their account?
Sort each send by whether the user *triggered* it and *needs* it to operate their account. Drag each item into the bucket it belongs to, then press Check.
If the trial-ending email gave you pause, that’s the trap. It feels transactional: it’s about the user’s specific account, and the deadline is real. But look at what triggers it: not the user, a timer. And look at what it’s for: not “here’s the state of your account” but “upgrade now.” A scheduled send whose purpose is to sell is marketing, no matter how account-specific the body reads, and it’s exactly the kind of case the decision tree later in this lesson is built to resolve.
Why the two streams earn different reputations
Section titled “Why the two streams earn different reputations”Why do mailbox providers score these two streams separately, and why does keeping them apart matter so much? The answer is a short cause-and-effect chain.
Start with how a provider like Gmail decides where your mail goes. It doesn’t read your email and judge the writing; it watches signals: what fraction of recipients open your mail, how many mark it as spam, how many delete it unread, and whether you send to addresses that don’t exist. From those signals it computes a reputation score for your sending domain. A high score lands your mail in the inbox; a low score lands it in spam or gets it rejected outright.
Now look at what each stream does to that score.
Transactional mail earns a strong, stable reputation almost for free. People open password resets, because they asked for them thirty seconds ago. Almost nobody marks a receipt or verification code as spam, since it’s mail they were waiting for. And you only send it to someone who just took an action, so there’s no blast-to-a-list volume. High opens, near-zero complaints, and no junk sends add up to a clean, excellent reputation.
Marketing mail earns a weaker, more volatile reputation, by the nature of the stream. Open rates are lower, because not everyone cares about this month’s newsletter. Complaints are materially higher, often from people who forgot they opted in three years ago and reach for “mark as spam” as a faster unsubscribe. And every campaign sheds some list churn.
Here is the link that breaks your product. When both streams send from the same domain, they share one reputation score, so the marketing complaints drag down the single number Gmail keeps for that domain. When that number dips, Gmail doesn’t surgically spam-folder only your newsletters; it spam-folders the whole domain. So your verification email, the one a brand-new user is watching their inbox for, lands in spam. The user gives up, and your signup funnel quietly loses conversions. The cause stays invisible to the people who could fix it: the product team sees “signups are down,” not “Gmail is spam-foldering our verification mail because last week’s campaign drew too many complaints.”
The fix follows from the diagnosis: give each stream its own domain, so each carries its own score. The diagram below makes the contrast concrete. The first tab shows the trap, both streams pouring into one reputation meter; the second shows the fix, the same two streams feeding two independent meters. Watch where the verification email lands in each.
One domain, one score. Marketing complaints drag the shared meter into the danger zone, and the verification email, which did nothing wrong, lands in spam.
Two domains, two independent scores. Password-reset and verification mail ride send.yourapp.com, untouched by whatever the campaign on marketing.yourapp.com is doing this week.
Apex, send., and marketing. subdomains
Section titled “Apex, send., and marketing. subdomains”So you split, into three zones. The names below are conventions, not requirements, but you’ll see them across most production apps.
The apex domain , yourapp.com with nothing in front of it, is for humans: employee mailboxes, the founder replying to a customer, support typed by a person.
Often it does no automated sending at all, and that is the point.
The moment your app blasts automated mail from the apex, your founders’ own outbound rides the same reputation as your app’s volume.
One bad deliverability week for the app, and the CEO’s email starts landing in spam too.
Keep the humans’ domain decoupled from the app’s incidents.
The send.yourapp.com subdomain (you’ll also see mail.) is the transactional workhorse: every automated send from Resend goes out from here.
This is the subdomain whose SPF and DKIM records you published in the previous lesson, with DMARC inherited from the apex, and it’s the only sending subdomain the project builds.
The marketing.yourapp.com subdomain (or news.) is the marketing stream’s home, stood up only once you actually send marketing mail.
It’s a separate verified domain in Resend with its own DKIM and SPF, and that separation is exactly what gives it a separate reputation.
The project sends transactional only, so you won’t build this one, but it’s named here so you don’t conflate it with send. later.
Two things to nail down.
First, those names are convention, not protocol: no email standard requires the word “send”, and mail. or tx. would work too.
What matters is that you pick names and keep them identical across your dev, preview, and production environments, so a from address doesn’t silently change shape between deploys.
Second, a watch-out that ties back to the previous lesson.
If you add marketing. later, you’ll walk it through the same DMARC none → quarantine → reject progression you did for send..
The subtle part: once your apex DMARC policy is at reject, any new subdomain that isn’t yet aligned through its own DKIM gets hard-bounced, because subdomains inherit the apex policy by default.
A new marketing subdomain isn’t a five-minute DNS edit you can do the morning of a campaign; it’s a warmup project, so plan it ahead.
One bit of vocabulary you’ll see throughout the email world: an ESP is the service that actually puts your mail on the wire, and Resend is yours. “Use a separate ESP for marketing” means route that stream through a different provider account — a heavier separation than just a separate subdomain.
Partial vs full separation
Section titled “Partial vs full separation”“Use separate subdomains” raises the question: separate how? There are two thresholds, and the difference matters.
Mailbox providers track reputation per domain and sending IP. Full separation means two genuinely independent identities on the wire: different IP ranges, different DKIM selectors, different DMARC report streams. Route both streams through the same provider on its same shared IP pool and you’ve separated the domains but not the IPs, so the separation is partial.
Partial separation is the early-stage default: one Resend account, two verified subdomains, both sending over Resend’s shared IP pool. Separate subdomains with separate DKIM keys buy you roughly 80% of the reputation isolation of a full split, with zero IP-management overhead. The shared pool works in your favor because Resend keeps it warm and clean across all its customers, so your low volume rides on a reputation far better than you could build alone.
The remaining 20% is dedicated IPs, separate provider accounts, and message-stream isolation. It only earns its weight at real volume, north of roughly 50,000 emails a month.
Choosing the from-address local part
Section titled “Choosing the from-address local part”This is the one piece of concrete syntax in the lesson.
Recall the anatomy of a from string: 'Display Name <local-part@verified.subdomain>'.
The display name and subdomain were covered in the first lesson; the part worth a rule here is the local part , the bit before the @.
Name the local part for the intent the user reads off the From line, not for the system that sent it.
A user glancing at their inbox reads meaning from those few characters.
Addresses like notifications@, billing@, and security@ tell them what kind of mail this is and let their filters route it.
An address like auth-service-prod@ tells them nothing and leaks your internal architecture into their inbox.
Here is the convention table for a transactional setup.
Note where each address lives: the automated ones on send., the genuinely human ones back on the apex.
| Address | Used for | Replyable? |
|---|---|---|
noreply@send.yourapp.com | Verification codes, magic-links: automated, no reply expected | No. Must pair with a replyTo to a monitored inbox |
billing@send.yourapp.com | Invoices, dunning | Yes. Replies go to billing |
security@send.yourapp.com | Security alerts, the kind users filter to high priority | Yes. Replies go to a security/support inbox |
support@yourapp.com / hello@yourapp.com | Genuinely human conversation | Yes. A real Google Workspace mailbox on the apex |
The noreply@ row carries a trap.
Someone will hit reply on a receipt or a security alert, and with no replyTo set their message vanishes: bad UX, and a faint negative deliverability signal too, since Gmail trusts senders who are never replied to slightly less.
The fix is to keep the bot identity in from and point replyTo at a mailbox a human reads.
The two variants below show the wrong shape and the right one, reusing the sendEmail call from the first lesson.
await sendEmail({ to: customer.email, subject: 'Your receipt', react: <Receipt invoice={invoice} />,});No replyTo set. The from defaults to the noreply@ identity, and a user who hits Reply on this receipt reaches no one.
await sendEmail({ to: customer.email, replyTo: 'support@yourapp.com', subject: 'Your receipt', react: <Receipt invoice={invoice} />,});One line earns the reply. The inbox still shows noreply@, so the user knows it’s a system message, but Reply now lands in support@ where a human reads it.
Make this routine: every transactional send either sets replyTo to a monitored mailbox or uses a from address that is itself monitored.
Deciding the borderline cases
Section titled “Deciding the borderline cases”Most sends classify themselves at a glance: a password reset is transactional, a newsletter is marketing. The decision tree is for the ambiguous ones, like the trial-ending upsell from the earlier exercise, a billing-flavored newsletter, or an opted-in weekly digest. For those, walk the questions in the order an experienced engineer asks them; the order is the whole point.
The tree below is that order, made clickable. Follow your send down the branches to the subdomain it should send from. Try it with the trial-ending upsell and watch where it lands.
It’s triggered by the user (or genuinely one-off and account-specific) and it’s pure account information.
Send it from send.yourapp.com, where it earns the clean reputation that keeps it in the inbox.
It’s on your schedule, or it carries promotion, or you couldn’t be sure.
Send it from marketing.yourapp.com so its complaint churn never touches the transactional reputation.
When in doubt, this is the safe default — the asymmetry of the two mistakes makes it so.
Every path ends at a subdomain, not just a label: the classification resolves directly into the from domain the send uses.
The tie-breaker drives the rest.
When you can’t decide, you default to marketing, because one wrong answer is expensive and the other is free.
Send from your domain, not the tenant’s
Section titled “Send from your domain, not the tenant’s”In B2B SaaS your customer is a company, say Acme Corp at acme.com, and it feels natural to send their billing receipts and invitations from acme.com.
Don’t.
In a multi-tenant SaaS, all mail flows from your verified domain, send.yourapp.com, with the tenant’s content inside the message: a user at Acme gets an email from send.yourapp.com that talks about Acme.
Sending genuinely from the tenant’s own domain (per-tenant CNAME records and DKIM delegation, so Acme’s domain authorizes your servers to sign as them) is a separate, heavyweight feature, rarely worth building until a paying customer makes it a deal-breaker.
Split on day one
Section titled “Split on day one”Splitting transactional and marketing onto separate subdomains is nearly free on day one and brutally expensive later.
Doing it now costs almost nothing: two DNS verifications instead of one, and two from addresses instead of one.
Pay that before you send a single email.
External resources
Section titled “External resources”If you want to go deeper, these three pair well with this lesson: the provider’s own take on structuring sending domains, a fuller treatment of the transactional-versus-marketing line, and the authoritative source for the bulk-sender rules referenced throughout.
The provider's guide to adding, verifying, and structuring sending domains and subdomains.
A fuller treatment of the line you drew here, with the engagement and deliverability data behind the split.
The authoritative source for the bulk-sender, authentication, and one-click-unsubscribe rules this lesson assumes.