Marketing email and transactional email have fundamentally different engagement profiles. Marketing email generates higher complaint rates, lower open rates, and more unsubscribes than password resets, receipts, and account alerts. When they share an IP and sending domain, the reputation damage from a poorly-performing campaign delays or blocks the delivery of time-sensitive transactional messages — password resets that users are actively waiting for, order confirmations that trigger support tickets when delayed. Traffic separation is the architectural guarantee that a bad marketing campaign cannot block the transactional email infrastructure.
Low because traffic mixing does not cause immediate problems when campaigns perform well, but a single high-complaint marketing campaign creates a direct path to delayed transactional delivery for the same users who filed the complaints.
Define separate sending configurations for marketing and transactional email and route explicitly at the send-call level. Both configs should be authenticated independently:
// In src/lib/email/config.ts
const SENDING_CONFIG = {
transactional: {
from: 'noreply@tx.company.com',
smtpHost: process.env.SMTP_HOST_TRANSACTIONAL,
ipPool: process.env.IP_POOL_TRANSACTIONAL,
},
marketing: {
from: 'news@mail.company.com',
smtpHost: process.env.SMTP_HOST_MARKETING,
ipPool: process.env.IP_POOL_MARKETING,
}
} as const
export function getSendingConfig(type: 'transactional' | 'marketing') {
return SENDING_CONFIG[type]
}
Call getSendingConfig('transactional') for password resets, receipts, and alert emails. Call getSendingConfig('marketing') for newsletters, drip campaigns, and promotions. Authenticate tx.company.com and mail.company.com with independent SPF, DKIM, and DMARC records.
ID: deliverability-engineering.domain-ip-strategy.traffic-separation
Severity: low
What to look for: Count all email routing configurations and classify each by traffic type. Check whether marketing/campaign email and transactional email (password resets, receipts, order confirmations) are sent from different IPs, IP pools, or subdomains. Marketing email typically has lower engagement and higher complaint rates, and should not pollute the reputation of transactional sending infrastructure which carries critical user communications.
Pass criteria: At least 2 distinct sending configurations exist separating marketing and transactional email by IP or subdomain (e.g., marketing from mail.company.com, transactional from tx.company.com). The code explicitly routes different email categories to different sending configurations.
Fail criteria: Marketing and transactional email share the same IP and same sending domain. A marketing campaign that damages reputation also delays or blocks transactional emails.
Skip (N/A) when: The project sends only one type of email (all transactional or all marketing).
Detail on fail: "Marketing campaigns and transactional email use the same sending domain and IP pool — a reputation hit from a campaign would delay or block password reset and receipt emails" or "No email traffic categorization found in sending configuration"
Remediation: Route email type to appropriate sending configuration:
const SENDING_CONFIG = {
transactional: {
from: 'noreply@tx.company.com',
smtpHost: process.env.SMTP_HOST_TRANSACTIONAL,
ipPool: process.env.IP_POOL_TRANSACTIONAL,
},
marketing: {
from: 'news@mail.company.com',
smtpHost: process.env.SMTP_HOST_MARKETING,
ipPool: process.env.IP_POOL_MARKETING,
}
}
export function getSendingConfig(emailType: 'transactional' | 'marketing') {
return SENDING_CONFIG[emailType]
}
// Example: password reset always uses transactional config
await sendEmail({
...getSendingConfig('transactional'),
to: user.email,
subject: 'Reset your password',
html: resetTemplate
})