The transactional subdomain split
How to isolate sender reputation by routing transactional and marketing email through separate subdomains, so a noisy campaign can never spam-folder a password reset.
Your app sends three kinds of email: a password reset when someone forgets how to get in, a receipt when a subscription charges, and, once marketing gets their way, an opt-in monthly newsletter about new features. All three are “email,” and the easy instinct is to send all three the same way, with one domain, one API key, and one from address. That instinct quietly breaks your product, and this lesson is about why. When all three share a domain, a marketing campaign’s spam complaints can push your password-reset email into the spam folder. The failure never shows up in any dashboard, so to your product team it just looks like signups mysteriously dropped. By the end you’ll be able to draw the line between transactional and marketing mail, you’ll understand the send.yourapp.com versus marketing.yourapp.com architecture that the first lesson of this chapter already assumed, and you’ll know how to write a from/replyTo pair for each purpose.
Transactional mail vs marketing mail
Section titled “Transactional mail vs marketing mail”Before anything else, you need a clear line between two categories of email, because every decision in this lesson depends on which side of that line a message falls on.
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 defining trait is in the name. There’s a transaction: the user did something, and this 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. Nobody pressed a button to make these arrive; you decided to send them.
This line is worth drawing precisely because it’s 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 from that requirement, for a sound reason: a user can’t reasonably “unsubscribe” from password resets while keeping a working account. The email is part of the account’s machinery, not a campaign they can opt out of. The previous lesson named the modern one-click-unsubscribe bar that bulk senders have to clear, and the takeaway here is its mirror image. Your transactional subdomain carries no unsubscribe header, because the mail on it isn’t the kind you’re allowed to unsubscribe from. Unsubscribe is a marketing concern, and it lives on the marketing side of the line.
There’s also a deliverability dimension, which is the one this whole lesson turns on. The mailbox providers, Gmail, Yahoo, and Microsoft, track the reputation of these two streams separately, because the recipient’s expectation pattern is so different between them. We’ll unpack exactly why in a moment. First, commit to the line.
The following exercise gives you eight real sends to sort. A few are obvious, and a few are deliberately on the edge, because the edges are where this skill actually 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, and it’s worth naming before we move on. It feels transactional: it’s about the user’s specific account, and the deadline is real. But look at what triggers it. The user didn’t do anything today; a timer did. And look at what it’s for. The headline isn’t “here’s the state of your account,” it’s “upgrade now.” A scheduled send whose purpose is to sell is marketing, no matter how account-specific the body reads. Hold onto that example, because it’s exactly the kind of case the decision tree later in this lesson is built to resolve.
Why the two reputations diverge
Section titled “Why the two reputations diverge”So why do the mailbox providers bother to score these two streams separately, and why does it matter so much that you keep them apart? The answer is a short cause-and-effect chain, so take it one link at a time.
Start with how a single mailbox provider, say Gmail, decides where your mail goes. It isn’t reading your email and judging the writing. It’s watching signals: what fraction of recipients open your mail, how many mark it as spam, how many delete it unread, and whether you’re sending to addresses that don’t exist. From those signals it computes, in effect, 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 at the door.
Now look at what each of your two streams 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 a verification code as spam, since it’s mail they were waiting for. And you only send it to people who just took an action, so there’s no “blasted to a list” volume. High opens, near-zero complaints, and no junk sends add up to a clean, boring, excellent reputation.
Marketing mail earns a weaker, more volatile reputation, and there’s nothing wrong with your marketing team; it’s just 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 genuinely forgot they opted in three years ago and reach for “mark as spam” as a faster unsubscribe. And every campaign sheds some list churn. None of this is a failure. It’s simply what a promotional stream looks like.
Here’s the link that breaks your product. When both streams send from the same domain, they share one reputation score. The marketing complaints don’t stay politely in their lane; they drag down the single number Gmail keeps for that domain. And 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 so they can finish signing up, lands in spam. The user gives up, and your signup funnel quietly loses conversions. The cause is invisible to the people who’d want to fix it: the product team sees “signups are down,” not “Gmail is spam-foldering our verification mail because last week’s campaign got too many complaints.”
The fix follows directly 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, with both streams pouring into one reputation meter. The second shows the fix, with the same two streams now feeding two independent meters. Watch where the verification email lands in each.
One domain, one score. The marketing complaints dragged the shared meter into the danger zone, and the verification email, which did nothing wrong, lands in spam.
Two domains, two independent scores. The password-reset and verification mail ride send.yourapp.com, untouched by whatever the campaign on marketing.yourapp.com is doing this week.
That is the whole argument in one image. Two domains give you two scores, which insulates the stream you can’t afford to lose from the stream that’s inherently noisy.
The subdomain architecture
Section titled “The subdomain architecture”So you split. The 2026 default is three zones, and the names are conventions you’ll see across most production SaaS apps. The tree below lays them out, and the rest of this section walks through what each zone is for and why it sits where it does.
The apex domain , yourapp.com with nothing in front of it, is for humans: employee mailboxes behind Google Workspace, the founder who replies to a customer from their own address, support correspondence typed by a person. Often it does no automated sending at all. Keep it that clean for a reason. The moment your app starts blasting automated mail from the apex, your founders’ own outbound, the investor update or the reply to a key customer, is riding the same reputation as your app’s send 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. used for the same role) is the transactional workhorse. Every automated transactional 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. It’s the one in scope for the project, the only sending subdomain you’ll actually build.
The marketing.yourapp.com subdomain (or news.) is the marketing stream’s home, and you only stand it up when you actually start sending marketing mail. It’s a separate verified domain in Resend, with its own DKIM keys and its own SPF, and that’s the whole point: a separate domain is what gives it a separate reputation. You’re not building this one in the project, since the project sends transactional only, but it’s named here so you don’t accidentally conflate it with send. later, and so the architecture in your head is complete.
Two things to nail down. First, those names are convention, not protocol. Nothing in any email standard requires the word “send”; you could use mail. or tx. or anything else. 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 forward-looking watch-out that ties back to the previous lesson. If you add that marketing. subdomain down the road, you’ll have to walk it through the same DMARC none → quarantine → reject progression you did for send.. Here’s the subtle part: once your apex DMARC policy is at reject, any new subdomain that isn’t yet properly aligned through its own DKIM will get hard-bounced by receivers, 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. Resend is yours. When people say “use a separate ESP for marketing,” they mean route the marketing stream through a different provider account. That’s a heavier form of separation than just a separate subdomain, which is the next thing we need to pin down.
What “separate” actually means, and how far to take it
Section titled “What “separate” actually means, and how far to take it”“Use separate subdomains” raises an obvious question: separate how, exactly? This is where beginners reliably go wrong in one of two directions, so let’s get the threshold right.
Mailbox providers track reputation per domain and sending IP. So full separation of two streams means different IP ranges, different DKIM selectors, and different DMARC report streams: two genuinely independent identities on the wire. If instead you route both streams through the same provider on the same shared pool of IPs, you’ve separated the domains, but they still share the underlying IPs, so the separation is partial.
The senior judgment here is to set the default before reaching for conditionals: partial separation is the right default, and it’s more than enough for a long time.
The early-stage default is one Resend account, two verified subdomains, both sending over Resend’s shared IP pool. That single move, separate subdomains with separate DKIM keys, buys you roughly 80% of the reputation isolation you’d get from a full split, with zero IP-management overhead. The shared IP pool is fine 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. Ship this. It’s the right answer for essentially every app until it’s big.
The remaining 20% is dedicated IPs, separate provider accounts, and message-stream isolation of the kind Postmark builds in. It only starts earning its weight at real volume, somewhere north of roughly 50,000 emails a month. Below that, reaching for it doesn’t just waste effort, it can actively hurt you. A dedicated IP starts with no reputation, and you need a steady, substantial volume to warm it up. Send a trickle from a cold dedicated IP and you’ll get worse placement than you’d have had on the shared pool.
The thing to internalize is that “separate subdomains” and “separate provider or dedicated IP” are different sizes of separation, and you almost certainly want the smaller one. Take the cheap 80% on day one, and defer the expensive 20% until your volume actually asks for it.
From-address conventions: the local part as an intent signal
Section titled “From-address conventions: the local part as an intent signal”Now the one piece of concrete syntax in this lesson, the addresses themselves. Recall the anatomy of a from string from the first lesson: 'Display Name <local-part@verified.subdomain>'. The display name is UX, and the subdomain is the hard requirement. The interesting part is the local part , the bit before the @, and one principle governs it.
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 before the @. Addresses like notifications@, billing@, and security@ tell the user what kind of mail this is and let their own filters route it. An address like auth-service-prod@ tells the user nothing useful and leaks your internal architecture into their inbox; it’s a code smell. Spend the local part on meaning the recipient can use.
Here’s the convention table for a transactional setup. Note where each address lives: the automated ones on send., and 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 |
That noreply@ row carries a trap worth its own example. A noreply@ address with no replyTo is a dead end: someone will hit reply on a receipt or a security alert, and their message vanishes into nothing. That’s bad UX, and it’s a faint negative deliverability signal too, because Gmail notices senders whose mail is consistently never replied to and trusts them slightly less. The fix is to keep the bot identity in from but point replyTo at a mailbox a human actually reads. The two variants below show the wrong shape and the right one, reusing the exact 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 nothing is set for replies. A user who hits Reply on this receipt is talking to a black hole: their message goes nowhere and nobody ever sees it. That’s bad UX, and a faint negative signal to the mailbox providers that watch for senders who are never replied to.
await sendEmail({ to: customer.email, replyTo: 'support@yourapp.com', subject: 'Your receipt', react: <Receipt invoice={invoice} />,});One line earns the reply. The from identity in the inbox is the same automated one, so the user still sees noreply@ and knows it’s a system message, but Reply now lands in support@, where a human reads it. The user gets an honest signal and a way to reach a person.
Make this routine: every transactional send either sets replyTo to a monitored mailbox, or uses a from address that is itself monitored. There is no third option where replies just disappear.
Deciding the borderline cases
Section titled “Deciding the borderline cases”Most sends classify themselves the instant you look at them. A password reset is transactional, a newsletter is marketing, and you don’t need a framework. The framework is for the genuinely ambiguous cases: the trial-ending upsell from the exercise earlier, a billing-flavored newsletter, 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 decision tree below is that order, made clickable. Start at the top and follow your specific send down the branches; each path ends at a concrete subdomain to send from. Try it with the trial-ending-upsell email 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.
Notice that every path ends at a subdomain, not just a label, and that’s deliberate. The classification isn’t an academic exercise; it resolves directly into which from domain the send uses. Notice the tie-breaker too: when you genuinely can’t decide, you default to marketing. That’s not a coin flip, it’s the asymmetry of the two mistakes. Send a marketing email through the transactional subdomain and you pollute the reputation you most need to protect. Send a borderline email through the marketing subdomain and you’ve lost nothing, because the user still gets it and deliverability is identical. When one wrong answer is expensive and the other is free, you lean toward the free one.
Multi-tenant sending: your domain, not the tenant’s
Section titled “Multi-tenant sending: your domain, not the tenant’s”One more guardrail, because it trips up almost everyone building B2B SaaS for the first time. When your customer is a company, say Acme Corp at acme.com, it feels natural to think their billing receipts and invitations should come from acme.com. They shouldn’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 who triggers a billing receipt gets an email from send.yourapp.com that talks about Acme, not an email from acme.com. 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 product feature with real operational cost, and it’s rarely worth that tax until a specific paying customer demands it as a deal-breaker. Name it, know it exists, and leave it out of scope.
Why this is a day-one decision
Section titled “Why this is a day-one decision”The thing to remember from this lesson is the timing. Splitting transactional and marketing onto separate subdomains is nearly free to do on day one and brutally expensive to do later, and almost everyone gets the timing wrong.
Run back through the failure modes, because together they make the case. Mixing the two streams on one subdomain is a silent reputation killer: nothing alerts you, and signup just quietly erodes while the product team chases the wrong cause. Sending automated mail from the apex chains your founders’ own inboxes to the app’s deliverability incidents. A noreply@ with no replyTo is bad UX and a faint negative signal at once. Each of these is avoidable for the price of a decision you make before you send your first email.
The retrofit is the part to weigh most carefully.
So make the split on day one. Concretely it costs you almost nothing now: two DNS verifications instead of one, and two from addresses instead of one. That’s the entire bill. Pay it before you send a single email, and the silent failure that breaks signup for reasons nobody can see simply never has a way to happen.
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.