You know you need to comply with email regulations. You've probably heard of CAN-SPAM and GDPR. But here's the part most developers get wrong: transactional emails and marketing emails have different rules. A password reset doesn't need an unsubscribe link. A trial-ending reminder might. The line between “transactional” and “marketing” is where most companies get fined.
This guide covers the compliance rules for transactional emails under CAN-SPAM, GDPR, and CASL — with specific guidance for SaaS teams shipping email from Next.js.
$51,744
Max CAN-SPAM fine
Per non-compliant email sent
4%
Max GDPR fine
Of annual global turnover
$10M CAD
Max CASL fine
Per violation (individuals)
The legal definition of “transactional”
Under CAN-SPAM, a transactional email is one whose primary purpose is to facilitate, confirm, or complete a transaction the recipient has already agreed to. Specifically:
- Purchase confirmations and receipts
- Shipping notifications
- Account security (password resets, login alerts)
- Subscription changes (plan upgrades, cancellations)
- Service updates that affect the user's account
What's not transactional, even if it feels like it:
- “Your trial ends in 3 days” — this is a conversion nudge, not a transaction confirmation
- “Check out our new feature” — product announcement, clearly marketing
- “Users like you also bought...” — upsell content in a receipt email makes the whole email commercial
- “We miss you” re-engagement — marketing, full stop
CAN-SPAM uses a “primary purpose” test. If the email's main goal is to promote a product or service, it's commercial — even if it contains transactional information. Adding a coupon to a receipt email can reclassify the entire email.
CAN-SPAM requirements (United States)
CAN-SPAM applies to any “commercial electronic message” sent to US recipients. Transactional emails get exemptions from some requirements, but not all.
- No unsubscribe link required
- No physical address required
- Subject line just needs to not be deceptive
- From address must be accurate
- Must not contain false header info
- Unsubscribe link required (must work within 10 days)
- Physical mailing address required
- Subject must not be misleading about content
- Must identify as an advertisement
- Must honor opt-out within 10 business days
GDPR requirements (EU/EEA)
GDPR doesn't distinguish between transactional and marketing emails the way CAN-SPAM does. Instead, it cares about your legal basis for processing personal data (which includes email addresses).
Legal bases for sending email
- Contract performance (Art. 6(1)(b)): You can send transactional emails without explicit consent because they're necessary to fulfill a contract. Password resets, purchase confirmations, and account alerts fall here.
- Legitimate interest (Art. 6(1)(f)): Some product emails (onboarding sequences, feature announcements for active users) may qualify, but you must document a Legitimate Interest Assessment (LIA).
- Consent (Art. 6(1)(a)): Marketing emails, newsletters, and promotional content require explicit, freely given consent with a clear opt-in.
What GDPR requires for all emails
- Data minimization: Only include personal data necessary for the email's purpose.
- Transparency: Your privacy policy must explain what emails you send and why.
- Right to object: Users can object to processing based on legitimate interest. For marketing, this means easy unsubscribe.
- Data retention: Don't keep email logs forever. Set a retention policy (90 days for tracking data is reasonable).
- Processing records: Document your email processing activities (who, what, why, retention period).
CASL requirements (Canada)
Canada's Anti-Spam Legislation is the strictest of the three. Unlike CAN-SPAM (opt-out model), CASL requires express consent before sending commercial messages.
- Transactional emails are exempt from consent requirements (same categories as CAN-SPAM)
- All commercial emails need express or implied consent
- Express consent means a clear opt-in (not pre-checked boxes)
- Implied consent has a 2-year expiry from last transaction
- Every commercial email must identify the sender, include contact info, and have an unsubscribe mechanism
The gray areas (where companies get in trouble)
Most compliance violations happen in the gray zone between transactional and marketing. Here are the common traps:
1. Upsells in transactional emails
Adding “Upgrade to Pro for 20% off” to a receipt email can reclassify the entire email as commercial under CAN-SPAM. Under GDPR, it changes your legal basis from contract performance to legitimate interest (or consent).
// RISKY: This receipt email now contains commercial content
export function ReceiptEmail({ order, promoCode }: ReceiptProps) {
return (
<BaseLayout previewText={`Receipt for order #${order.id}`}>
<OrderDetails order={order} />
{/* This one section can reclassify the entire email */}
<Section>
<Text>Loved your purchase? Use code {promoCode} for 20% off!</Text>
</Section>
</BaseLayout>
);
}
// SAFER: Keep transactional emails purely transactional
export function ReceiptEmail({ order }: ReceiptProps) {
return (
<BaseLayout previewText={`Receipt for order #${order.id}`}>
<OrderDetails order={order} />
{/* No promotional content */}
</BaseLayout>
);
}2. Onboarding sequences
The first “Welcome to your account” email is transactional. The third “Have you tried feature X?” email is marketing. The line blurs around emails 2-4.
3. Renewal reminders
“Your subscription renews on March 15 for $49” is transactional. “Your subscription renews soon — upgrade to annual and save 20%!” is commercial. The difference is whether the primary purpose is informing or selling.
Implementation patterns for compliance
Here's how to build compliance into your email infrastructure, not bolt it on after:
Categorize emails at the template level
type EmailCategory = "transactional" | "marketing" | "hybrid";
type EmailTemplate = {
name: string;
category: EmailCategory;
legalBasis: "contract" | "legitimate-interest" | "consent";
requiresUnsubscribe: boolean;
retentionDays: number;
};
// Define compliance requirements per template
const TEMPLATES: Record<string, EmailTemplate> = {
"password-reset": {
name: "Password Reset",
category: "transactional",
legalBasis: "contract",
requiresUnsubscribe: false,
retentionDays: 30,
},
"welcome": {
name: "Welcome Email",
category: "transactional",
legalBasis: "contract",
requiresUnsubscribe: false,
retentionDays: 90,
},
"trial-ending": {
name: "Trial Ending Reminder",
category: "hybrid",
legalBasis: "legitimate-interest",
requiresUnsubscribe: true, // Safer to include
retentionDays: 90,
},
"feature-announcement": {
name: "New Feature Announcement",
category: "marketing",
legalBasis: "consent",
requiresUnsubscribe: true,
retentionDays: 90,
},
};Check consent before sending
export async function sendEmail({
to,
userId,
template,
data,
}: SendEmailParams) {
const templateConfig = TEMPLATES[template];
if (!templateConfig) throw new Error(`Unknown template: ${template}`);
// Transactional emails skip consent check
if (templateConfig.legalBasis === "consent") {
const hasConsent = await db.emailConsent.findFirst({
where: {
userId,
category: "marketing",
consentedAt: { not: null },
revokedAt: null,
},
});
if (!hasConsent) {
console.log(`Skipping ${template} for ${userId}: no marketing consent`);
return;
}
}
// Add unsubscribe header for marketing/hybrid emails
const headers: Record<string, string> = {};
if (templateConfig.requiresUnsubscribe) {
headers["List-Unsubscribe"] =
`<${process.env.APP_URL}/api/unsubscribe?userId=${userId}>`;
headers["List-Unsubscribe-Post"] = "List-Unsubscribe=One-Click";
}
await emailProvider.send({ to, subject, html, headers });
}One-click unsubscribe
As of February 2024, Gmail and Yahoo require one-click unsubscribe via the List-Unsubscribe header for bulk senders (5,000+ emails/day). Even if you're below that threshold, implement it — it's a deliverability signal.
import { NextRequest, NextResponse } from "next/server";
import { db } from "@/lib/db";
// Handles both GET (link click) and POST (one-click header)
export async function POST(req: NextRequest) {
const userId = req.nextUrl.searchParams.get("userId");
if (!userId) return new NextResponse("Missing userId", { status: 400 });
await db.emailConsent.updateMany({
where: { userId, category: "marketing" },
data: { revokedAt: new Date() },
});
// Must process within 2 days (Gmail requirement)
return new NextResponse("Unsubscribed", { status: 200 });
}
export async function GET(req: NextRequest) {
const userId = req.nextUrl.searchParams.get("userId");
if (!userId) return NextResponse.redirect(new URL("/", req.url));
await db.emailConsent.updateMany({
where: { userId, category: "marketing" },
data: { revokedAt: new Date() },
});
// Redirect to a confirmation page
return NextResponse.redirect(
new URL("/unsubscribed?success=true", req.url)
);
}The physical address requirement
CAN-SPAM and CASL require a valid physical postal address in commercial emails. If you're a remote-first company, you have options:
- Registered agent address
- PO Box or virtual mailbox
- Coworking space address (if contractually allowed)
Put it in the footer of marketing emails. Transactional emails don't need it under CAN-SPAM, but including it doesn't hurt.
Compliance audit checklist
Classify every email template
Go through each template and classify it as transactional, marketing, or hybrid. Document the legal basis for each.
Add unsubscribe to marketing emails
Both a visible link in the footer and the List-Unsubscribe header. Must work within 10 business days (CAN-SPAM) or 2 days (Gmail/Yahoo).
Check consent flows
Verify that marketing email consent is explicit, freely given, and recorded with a timestamp. Pre-checked boxes don't count under GDPR or CASL.
Review email content for commercial creep
Check if transactional emails contain promotional content that could reclassify them. Remove upsells from receipts and security emails.
Set data retention policies
Define how long you keep email logs, tracking data, and consent records. Document this in your privacy policy.
Add physical address to marketing footers
A valid postal address is required in every commercial email under CAN-SPAM and CASL.
The bottom line
Email compliance isn't about checking boxes. It's about not being the company that turns a receipt email into a marketing vehicle and gets hit with a five-figure fine. Keep transactional emails purely transactional. Get real consent for marketing. And when you're in the gray zone, err on the side of caution — adding an unsubscribe link to a borderline email costs nothing.