This chapter answers the legal questions developers and founders hit when sending email: who you may email, what you must tell them, how you must let them leave, and which laws apply when. It covers the major regimes (CAN-SPAM, GDPR, CASL, ePrivacy, and others), the unsubscribe and consent-record rules they impose, the identification and physical-address requirements, and the technical headers mailbox providers now require from bulk senders.
⚠️ This is NOT legal advice. This chapter is a practical engineering summary, not a substitute for professional counsel. Laws change, penalties are updated, and how a rule applies to your business depends on facts this document cannot know. Consult a qualified attorney for your specific situation before relying on any of it. The figures, deadlines, and penalties below are drawn from public sources current as of early 2026; do not treat them as guaranteed current legal advice.
Quick answers (the questions this chapter covers):
- Which laws apply to my email program?
- What does CAN-SPAM require in the US?
- What does GDPR require in the EU?
- What does CASL require in Canada?
- What about Australia, the UK, Brazil, and elsewhere?
- How fast must I process unsubscribes, and how long must the link work?
- Do I really need a
List-Unsubscribeheader? - How do I implement RFC 8058 one-click unsubscribe correctly?
- What are the February 2024 (and May 2025) bulk-sender requirements?
- Can a preference center replace a one-click unsubscribe?
- What consent records must I keep?
- How long can I keep subscriber data?
- What must my privacy policy say?
- How do I stay compliant when sending across borders?
- How do I handle data-subject requests (access, deletion, portability)?
How compliance, deliverability, and authentication overlap
Before the law, one structural point: email compliance is now enforced on two layers at once, and engineers conflate them constantly.
- Statutory layer, what governments require (CAN-SPAM, GDPR, CASL, ePrivacy/PECR, the Spam Act, LGPD …). Violations are enforced by regulators with fines and lawsuits. Slow-moving, jurisdiction-bound, complaint-driven.
- Mailbox-provider layer, what Gmail, Yahoo, and Microsoft require to accept your mail at all. These are not laws. They are private acceptance policies, but for a real sending program they bite far harder and far faster than any regulator: a non-compliant bulk send is rejected or junked in minutes, not litigated in years.
The two layers rhyme but are not identical. A one-click List-Unsubscribe header is a mailbox-provider requirement (and a deliverability safeguard), not a statutory one, yet implementing it also happens to satisfy "make it easy to leave," which is statutory under GDPR and CASL. SPF/DKIM/DMARC are pure mailbox-provider requirements with no direct statutory mandate, but failing them gets your mail rejected long before any compliance question arises.
A mental model that keeps the two straight:
| Concern | Statutory layer | Mailbox-provider layer |
|---|---|---|
| Consent to send | Required (GDPR/CASL opt-in; CAN-SPAM opt-out) | Indirectly enforced via spam-complaint rate |
| Identify the sender | Required (physical address, accurate headers) | Required (functional From/Reply-To; authentication) |
| Easy unsubscribe | Required | Required (RFC 8058 one-click for bulk) |
| Authentication (SPF/DKIM/DMARC) | Not directly required | Required for bulk senders since Feb 2024 / May 2025 |
| Spam-complaint rate < 0.3% | Not directly required | Required |
This chapter covers both layers. Authentication mechanics (the DNS records themselves, signing, alignment) live in Deliverability; here we cover which of them are now mandatory and why, plus the headers that straddle both layers.
Which laws apply to my email program?
Probably several at once. Email crosses borders effortlessly, so a single campaign can fall under US, EU, Canadian, and other rules simultaneously. The law that applies usually follows the recipient's location, not yours, so if your list spans countries, you are subject to the strictest applicable rule on each point. GDPR is explicit about this extraterritorial reach (Article 3): it applies to any organization, anywhere, that offers goods or services to people in the EU, regardless of where the company is incorporated or hosted.
The three you will meet most often, at a glance:
| Law | Region | Consent model | Key requirement | Penalty ceiling |
|---|---|---|---|---|
| CAN-SPAM | US | Opt-out | Opt-out mechanism + physical address | ~$53,088/email |
| GDPR (+ ePrivacy) | EU/EEA | Opt-in | Explicit opt-in consent | €20M or 4% of global revenue |
| CASL | Canada | Opt-in | Express consent + opt-out mechanism | $1M (individual) / $10M (organization) CAD |
A few things to notice immediately:
- The consent models differ fundamentally. CAN-SPAM is an opt-out regime (you may email until told to stop, with conditions), while GDPR and CASL are opt-in regimes (you need permission before you send). When in doubt, follow the stricter opt-in model.
- The penalties are severe. GDPR fines scale with company revenue (the greater of €20M or 4% of global annual revenue), so they can be existential for large firms. CAN-SPAM penalties are assessed per email, so a single bad campaign multiplies fast. (The CAN-SPAM per-violation maximum is inflation-adjusted by the FTC periodically; it stood at $53,088 as of the FTC's 2025 adjustment, up from the often-quoted "$43,792" and the older "$16,000.")
- GDPR and ePrivacy are two laws, not one. GDPR governs personal data; the ePrivacy Directive (transposed nationally, e.g. PECR in the UK) governs electronic communications and consent for marketing specifically. For email marketing inside the EU/UK you must satisfy both, ePrivacy is what makes prior opt-in mandatory for marketing email; GDPR sets the standard for what valid consent and records look like.
The practical takeaway: if you build one program that satisfies GDPR-grade consent plus CASL-grade unsubscribe longevity plus the Feb-2024 mailbox-provider rules, you are compliant almost everywhere. See "How do I stay compliant when sending across borders?" below.
Decision table: do I need consent before this send?
| Message | Example | CAN-SPAM (US) | GDPR/ePrivacy (EU) | CASL (Canada) |
|---|---|---|---|---|
| Order/transaction confirmation | Receipt, shipping notice | No consent needed | Lawful basis: contract | Exempt (transactional) |
| Account/security | Password reset, login alert | No consent needed | Lawful basis: contract / legitimate interest | Exempt |
| Service announcement (non-promo) | Outage notice, ToS change | No consent needed | Legitimate interest (document it) | Generally exempt |
| Marketing to a customer | "New feature, upgrade now" | Opt-out OK | Soft opt-in possible* | Implied consent (time-limited) |
| Cold marketing to a stranger | Purchased/scraped list | Opt-out OK (risky) | Prohibited without consent | Prohibited without consent |
* The ePrivacy "soft opt-in" lets you email existing customers about similar products without prior opt-in, provided you gave them a clear chance to refuse at collection and in every message. It does not cover cold prospects, and several EU member states interpret it narrowly. Treat it as a fallback, not a strategy.
For the boundary between transactional and marketing, the single most consequential classification in this chapter, see Email Types and the Transactional Catalog.
What does CAN-SPAM require in the US?
CAN-SPAM (Controlling the Assault of Non-Solicited Pornography And Marketing Act of 2003, enforced by the FTC) is the US federal law governing commercial email. It is an opt-out regime: you do not need prior consent to send, but you must meet strict honesty and unsubscribe requirements, and you must stop when asked.
The requirements, every one of which is a hard gate on a commercial send:
- Accurate header information (From, To, Reply-To, and the originating domain): no spoofing, no disguising the sender, no falsified routing.
- Non-deceptive subject lines: the subject must reflect the actual content of the message.
- Identify the message as an ad where applicable: clear and conspicuous disclosure that the message is an advertisement (the law allows flexibility in how, but it must be obvious).
- Physical mailing address in every email: a valid physical postal address. This may be your street address, a registered post-office box, or a registered Commercial Mail Receiving Agency (CMRA) maintained under USPS rules. A fake or non-deliverable address fails.
- Clear opt-out mechanism: a visible, working way to unsubscribe in every commercial message.
- Honor opt-out within 10 business days, and the opt-out method must stay functional for at least 30 days after you send.
- Monitor what others do on your behalf. You remain liable even if a contractor, affiliate, or agency sends the mail. You cannot outsource the liability.
Do transactional emails need consent under CAN-SPAM? No. Transactional or relationship messages can be sent without opt-in. But CAN-SPAM hinges on a "primary purpose" test, and this is where engineers get caught:
- If the message's primary purpose is transactional or relationship content, the full set of commercial rules does not apply (though the no-falsified-headers rule always does).
- If the primary purpose is commercial, it is a commercial message no matter how you label it.
- For a mixed message, the FTC looks at the subject line and at what content appears first/most prominently. A receipt with a small cross-sell at the bottom is still transactional; a "receipt" whose subject line and top half push a sale is commercial and needs the address + opt-out.
The safe engineering rule: keep promotional content out of transactional templates entirely, or route any message with meaningful promotional content through the marketing path with full compliance headers. See Transactional Emails for keeping those templates clean.
Penalty: up to ~$53,088 per email in violation (FTC inflation-adjusted figure). Because the penalty is per-message, the cost of a non-compliant bulk send compounds with the size of the list. Treat the requirements above as a hard gate on every commercial send.
Common mistakes:
- Omitting the physical address from automated marketing emails (or putting it only on the website, not in the email).
- Using a "no-reply" From name or address that misrepresents who is sending.
- Letting unsubscribe links break after a template change or domain migration, a dead link is a violation even if it worked yesterday.
- Charging a fee, requiring a login, or requiring more than an email address to opt out, all prohibited.
- Continuing to send for the full 10 business days as a matter of policy. Ten days is a ceiling, not a target; mailbox providers and other regimes expect far faster.
What does GDPR require in the EU?
GDPR (the EU General Data Protection Regulation) is the EU's comprehensive data-protection regulation. For email it is an opt-in regime built on the principle that personal data (including an email address) belongs to the individual, who must actively consent to its use. For marketing email specifically, the prior-consent rule actually comes from the ePrivacy Directive (national transpositions such as the UK's PECR); GDPR then defines what valid consent and proper records look like.
The six lawful bases (Article 6). You may only process personal data if you have a lawful basis. For email, three matter:
- Consent, the basis for marketing email. Must be a clear affirmative act.
- Contract, the basis for transactional email tied to fulfilling an order or service (receipts, shipping, account notices).
- Legitimate interest, a possible basis for some operational/relationship messages, but it requires a documented balancing test and is not a safe basis for cold marketing under ePrivacy.
What valid consent looks like (Article 4(11), Article 7):
- Explicit opt-in, not pre-checked boxes: silence, inactivity, or a pre-ticked box is never consent (confirmed by the CJEU Planet49 ruling).
- Freely given, specific, informed, and unambiguous: the user understands exactly what they agreed to, with no coercion and no bundling of marketing consent into unrelated terms (you cannot make signup conditional on accepting marketing).
- Granular: separate consents for separate purposes (e.g. product email vs. partner offers).
- As easy to withdraw as to give (Article 7(3)): leaving must be no harder than joining.
- Demonstrable (Article 5(2), the accountability principle): you must be able to prove consent. No proof = no consent in a regulator's eyes.
Data-subject rights you must be able to honor: access (Article 15), rectification (16), erasure / "right to be forgotten" (17), restriction (18), data portability (20), and objection to direct marketing (21, which is absolute, you cannot refuse a marketing objection). See "How do I handle data-subject requests?" below.
Process unsubscribe / objection immediately: no grace period; act "without undue delay."
Do transactional emails need marketing consent under GDPR? No. They rest on the contract lawful basis (you cannot fulfill an order without confirming it) or, for some operational messages, legitimate interest. You do not need separate marketing consent to send an order confirmation, password reset, or security alert. You still must respect the data minimization and security principles.
Penalty: up to €20M or 4% of global annual revenue, whichever is greater (the higher tier, for consent/lawful-basis violations). Because it scales with revenue, GDPR is the regime that most often dictates a "comply globally" strategy. The UK applies equivalent rules via UK GDPR + PECR post-Brexit (see below).
Common mistakes:
- Treating a pre-ticked checkbox, or "by signing up you agree to receive marketing," as consent.
- Bundling marketing consent with terms-of-service acceptance (consent must be freely given and unbundled).
- Storing consent as a single boolean flag with no timestamp, source, or scope, which makes it unprovable, and therefore worthless.
- Treating "legitimate interest" as a way around consent for cold outreach. ePrivacy requires prior consent for marketing email regardless of GDPR's lawful bases.
- Forgetting that an IP-address or device identifier captured at signup is itself personal data subject to the same rules.
What does CASL require in Canada?
CASL (Canada's Anti-Spam Legislation, in force since 2014) is among the strictest regimes in the world. It is an opt-in regime, and its defining feature is a sharp distinction between two kinds of consent, one permanent, one with a clock.
The two consent types:
-
Express consent: an explicit, affirmative opt-in. This is ideal because it does not expire as long as it is not withdrawn. You must keep proof of when, how, and what was consented to.
-
Implied consent: arises automatically from certain relationships, but is time-limited:
- An existing business relationship (e.g. a purchase, contract, or paid subscription) gives implied consent for 2 years from the relevant event.
- An inquiry (someone asked about your product/service) gives implied consent for 6 months from the inquiry.
- A conspicuously published business email address (with no statement refusing CEMs) can also imply consent for messages relevant to that person's role.
When the implied-consent window lapses, you may no longer email that contact without obtaining express consent first.
The operational requirements (every commercial electronic message, or CEM, must include):
- Clear sender identification, who you are and how to reach you, valid for 60 days after send.
- A working unsubscribe mechanism, also functional for at least 60 days after send.
- Process unsubscribe no later than 10 business days (faster is expected and safer).
- Keep consent records for proof; a common operational standard is 3 years after the consent expires or is withdrawn, to survive an investigation window.
CASL also reaches beyond email to texts and other electronic messages, and it includes provisions against installing software and altering transmission data without consent, out of scope here but worth knowing the law is broad.
Penalty: up to $1M CAD for an individual and $10M CAD for an organization per violation. Directors and officers can be held personally liable. The combination of time-limited implied consent and long record-keeping obligations makes consent tracking (with timestamps, source, and the type of consent) essential for any program sending into Canada.
Common mistakes:
- Continuing to email a contact after their 2-year (or 6-month) implied-consent window lapses, because nothing in the system tracked the clock.
- Failing to log which relationship or inquiry created the implied consent, so you cannot prove when the clock started, or that it ever did.
- Treating a single purchase as permanent permission. Implied consent from a transaction is the 2-year kind, not express.
- Sending to role addresses (
info@,sales@) on the assumption they are exempt, they are only covered if conspicuously published and the message is relevant to the role.
What about Australia, the UK, Brazil, and elsewhere?
Beyond the big three, other jurisdictions impose their own rules. The common thread is consent plus a functioning unsubscribe plus accurate sender identification.
| Region | Law | Consent model | Unsubscribe / identification | Notes |
|---|---|---|---|---|
| Australia | Spam Act 2003 | Opt-in (express or inferred) | Honor unsubscribe within 5 business days; every message must identify the sender | Shortest unsub window in this set |
| UK | UK GDPR + PECR | Opt-in for marketing | Same standard as GDPR/ePrivacy | Post-Brexit equivalent of EU rules |
| Brazil | LGPD | Opt-in (explicit for marketing) | Data-subject rights mirror GDPR | ANPD is the regulator |
| EU (all members) | GDPR + ePrivacy (national laws) | Opt-in | "Soft opt-in" possible for existing customers | National transpositions vary |
| California (US) | CCPA/CPRA | Opt-out of "sale/share" | "Do Not Sell or Share My Personal Information" link | Layers on top of CAN-SPAM |
Notes:
- Australia has the shortest unsubscribe window of the set, 5 business days, so a process tuned only to CAN-SPAM's 10-day ceiling could still fall short there. Australia also recognizes inferred consent from an existing relationship, similar in spirit to CASL's implied consent.
- UK post-Brexit retains GDPR-equivalent protection through UK GDPR and the PECR (Privacy and Electronic Communications Regulations); the ICO is the regulator. PECR is the UK's transposition of the ePrivacy Directive and is what mandates prior opt-in for marketing email.
- Brazil's LGPD closely mirrors GDPR, including explicit consent for marketing and a full set of data-subject rights; the ANPD can impose fines up to 2% of Brazilian revenue (capped per infraction).
- California's CCPA/CPRA does not regulate the sending of email the way CAN-SPAM does, but it gives California residents rights over personal data (including email addresses) and may require a "Do Not Sell or Share" link and honoring of opt-out-preference signals. Several other US states (Virginia, Colorado, Connecticut, Texas, and more) have passed comparable laws.
This table is not exhaustive. Many other countries (across Asia, the Middle East, and Latin America, e.g. Japan's APPI, Singapore's PDPA, India's DPDP Act) have their own regimes. When entering a new market, verify local rules with counsel.
How fast must I process unsubscribes, and how long must the link work?
It depends on the regime, but the safe engineering default removes the guesswork. Every regime requires a working unsubscribe; the timing and longevity rules differ. This table consolidates the key differences:
| Law | Process within | Link must stay valid | Notes |
|---|---|---|---|
| CAN-SPAM (US) | 10 business days | ≥ 30 days after send | Opt-out must be free, no login |
| GDPR/ePrivacy (EU) | Immediately ("without undue delay") | As easy as opting in | Objection to marketing is absolute |
| CASL (Canada) | 10 business days | ≥ 60 days after send | |
| Spam Act (Australia) | 5 business days | Functional, low-cost | |
| Gmail/Yahoo/Microsoft | 48 hours (one-click) | Functional | Mailbox-provider rule, not statute |
The safe engineering default is to process unsubscribes immediately and to keep unsubscribe links valid for at least 60 days. Doing both satisfies the strictest column in every row, including Australia's 5-day window and the mailbox-provider 48-hour expectation.
Why "immediately" is also the cheap option. Suppression should be a synchronous write to a suppression list that the send path checks before every send, not a batch job that runs nightly. The cost of getting this wrong is asymmetric: emailing someone after they unsubscribed is both a compliance violation and a near-certain spam complaint, which damages your sender reputation. See List Management for the suppression-list design and Deliverability for the reputation impact.
Universal unsubscribe best practices (these satisfy all regimes at once):
- Prominent link: easy to find in every commercial message.
- One-click when possible: minimal friction; never multi-step for the base "unsubscribe from all" action.
- No login required: never gate it behind authentication.
- Free: no cost to the recipient.
- Confirm the action: show the user they have been removed (and ideally log it).
- Honor it globally, not just per-list: a hard opt-out should suppress all marketing, with separate granular controls available in a preference center.
Worked example: an idempotent unsubscribe endpoint
A robust unsubscribe must be idempotent (clicking twice, or a provider retrying the POST, must not error) and must work for both the human GET (browser landing page) and the machine POST (RFC 8058 one-click). Provider-agnostic sketch:
Code for your engineers (TypeScript)
// Routes for one unsubscribe URL, signed token in the path/query.
// `verifyToken` validates an HMAC so the link can't be forged or enumerated.
// `suppression` is your own store; `emailClient` is whatever provider wrapper you use.
import { verifyToken } from "./unsub-token";
import { suppression } from "./suppression";
// POST = RFC 8058 one-click (mailbox provider calls this directly, no JS, no UI)
export async function handleUnsubscribePost(req: Request): Promise<Response> {
const token = new URL(req.url).searchParams.get("t");
const claim = token && verifyToken(token);
if (!claim) {
// Never reveal whether the address exists; just acknowledge.
return new Response(null, { status: 202 });
}
// Idempotent: suppress() is a no-op if already suppressed.
await suppression.suppress({
email: claim.email,
scope: claim.listId ?? "all-marketing",
reason: "one-click-unsubscribe",
at: new Date().toISOString(),
});
// RFC 8058: respond 200/202 with an empty body. No confirmation click.
return new Response(null, { status: 200 });
}
// GET = human lands here from the visible footer link → render a real page
export async function handleUnsubscribeGet(req: Request): Promise<Response> {
const token = new URL(req.url).searchParams.get("t");
const claim = token && verifyToken(token);
if (!claim) {
return htmlPage("This unsubscribe link is invalid or expired.", 400);
}
await suppression.suppress({
email: claim.email,
scope: claim.listId ?? "all-marketing",
reason: "footer-link-unsubscribe",
at: new Date().toISOString(),
});
// Confirm + offer a preference center as a secondary option.
return htmlPage("You've been unsubscribed. Manage preferences instead?", 200);
}
Signed-token helper (generic Node crypto, no provider dependency):
Code for your engineers (TypeScript)
import { createHmac, timingSafeEqual } from "node:crypto";
const SECRET = process.env.UNSUB_SECRET!; // 32+ random bytes, not in source
type Claim = { email: string; listId?: string };
export function signToken(claim: Claim): string {
const payload = Buffer.from(JSON.stringify(claim)).toString("base64url");
const sig = createHmac("sha256", SECRET).update(payload).digest("base64url");
return `${payload}.${sig}`;
}
export function verifyToken(token: string): Claim | null {
const [payload, sig] = token.split(".");
if (!payload || !sig) return null;
const expected = createHmac("sha256", SECRET).update(payload).digest("base64url");
const a = Buffer.from(sig);
const b = Buffer.from(expected);
if (a.length !== b.length || !timingSafeEqual(a, b)) return null;
try {
return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
} catch {
return null;
}
}
Why sign the token. An unsigned ?email= link can be edited by anyone to unsubscribe other people, and crawlers/scanners will hit it. A signed token makes the link unforgeable and lets you embed the list scope without exposing a database ID.
Do I really need a List-Unsubscribe header?
Yes, if you send in bulk. As of February 2024, Gmail, Yahoo, and (from May 2025) Microsoft require bulk senders to support one-click unsubscribe via the List-Unsubscribe headers on marketing/promotional mail. Without them, bulk marketing email may be rejected, throttled, or filtered to spam.
There are two related but distinct headers, both standardized:
List-Unsubscribe(RFC 2369, 1998), a list of unsubscribe URIs (mailto:and/orhttps:). Old; supported for decades.List-Unsubscribe-Post(RFC 8058, 2017), opts the message into one-click unsubscribe, where the mailbox provider issues an HTTPPOSTto your HTTPS URI without loading your page or requiring user interaction.
To satisfy the modern bulk-sender requirement you need both headers, and the List-Unsubscribe header must include an HTTPS URI (a mailto:-only header does not qualify for one-click).
The required headers (provider-agnostic; set them however your sending layer exposes custom headers):
Code for your engineers (TypeScript)
const headers = {
// RFC 2369: provide an HTTPS URI (and optionally a mailto: fallback).
"List-Unsubscribe":
"<https://example.com/u?t=SIGNED_TOKEN>, <mailto:unsubscribe@example.com?subject=unsubscribe>",
// RFC 8058: this exact value opts the message into one-click.
"List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
};
// Generic provider wrapper, NOT tied to any specific ESP.
await emailClient.send({
from: "news@example.com",
to: recipient.email,
subject: "...",
html: body,
headers,
});
Why this matters now: these headers moved from "nice to have" to mandatory for bulk senders. They enable the native one-click unsubscribe button mailbox providers render at the top of the message, which means fewer recipients reach for "Report Spam" instead. The header is both a compliance-adjacent requirement and a deliverability safeguard. See Deliverability for the broader sender-requirements picture.
Common mistakes:
- Including only the
mailto:form of the header (one-click needs the HTTPS POST form). - Omitting
List-Unsubscribe-Postentirely (you then get the old multi-click flow, not the one-click button, and may fail the bulk requirement). - Requiring a confirmation click on the landing page for the one-click flow, the POST must process the unsubscribe directly and return 200/202.
- Putting a transactional email through the bulk/marketing path so it inherits an unwanted unsubscribe header (transactional mail is exempt from the one-click requirement and should not carry it).
- Signing the message in a way that breaks DKIM when the provider adds headers, keep
List-Unsubscribe/List-Unsubscribe-Postinside the DKIM-signed header set so they survive in transit.
How do I implement RFC 8058 one-click unsubscribe correctly?
RFC 8058 defines a precise contract. Get any part wrong and either the one-click button does not appear or the unsubscribe silently fails.
The contract, step by step:
- The message carries both
List-Unsubscribe(with anhttps:URI) andList-Unsubscribe-Post: List-Unsubscribe=One-Click. - Both headers must be covered by your DKIM signature (RFC 6376). If a header the provider relies on is not signed, the provider may distrust the one-click affordance.
- When the user clicks the provider's native "Unsubscribe" button, the provider sends an HTTP POST to your
https:URI with bodyList-Unsubscribe=One-ClickandContent-Type: application/x-www-form-urlencoded. No cookies, no JavaScript, no redirect to a page. - Your endpoint must not require any further interaction. It processes the unsubscribe and returns 200 or 202.
- You must act on it within 48 hours (the mailbox-provider expectation).
End-to-end build using the helpers above:
Code for your engineers (TypeScript)
// 1) When sending each marketing message, mint a per-recipient signed token.
const token = signToken({ email: recipient.email, listId: "weekly-digest" });
const unsubUrl = `https://example.com/u?t=${token}`;
const headers = {
"List-Unsubscribe": `<${unsubUrl}>, <mailto:unsubscribe@example.com>`,
"List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
};
// 2) The /u endpoint handles POST (one-click) and GET (footer link) as shown
// in the idempotent-endpoint example above. The POST handler returns 200,
// writes to the suppression store, and never renders a UI.
Verifying it actually works (provider-neutral):
# Confirm the headers are present and well-formed on a real received message
# (view source in your mailbox, or pipe a saved .eml through grep):
grep -i '^List-Unsubscribe' message.eml
# Simulate the provider's one-click POST against your own endpoint:
curl -i -X POST 'https://example.com/u?t=SIGNED_TOKEN' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'List-Unsubscribe=One-Click'
# Expect: HTTP/1.1 200 (or 202), empty body, and a new suppression record.
Failure modes and how to diagnose them:
| Symptom | Likely cause | Fix |
|---|---|---|
| No native unsubscribe button in Gmail/Yahoo | Missing List-Unsubscribe-Post, or List-Unsubscribe lacks an https: URI | Add both headers; include HTTPS URI |
| Button shows but unsubscribe "fails" | Endpoint requires a confirmation click, or returns non-2xx | Process the POST directly; return 200/202 |
| One-click works for some recipients only | Header not DKIM-signed; survives only when no intermediary rewrites it | Add the headers to the signed header list |
| Suppression doesn't take effect | Send path doesn't check suppression before sending, or checks a stale cache | Check suppression synchronously at send time |
| Mass unsubscribes from a security scanner | Unsigned/guessable token, scanner POSTs the link | Use signed tokens (see helper above) |
| Compliant but still landing in spam | Spam-complaint rate ≥ 0.3%, or failing auth | Fix authentication + list hygiene (Deliverability) |
For the events your endpoint and provider emit when an unsubscribe or complaint occurs (and how to ingest them), see Webhooks & Events.
What are the February 2024 (and May 2025) bulk-sender requirements?
This is the mailbox-provider layer, and it is now the most operationally important set of rules in the chapter, because failing it gets your mail rejected regardless of statutory compliance.
Who is a "bulk sender"? Gmail and Microsoft define it as a domain sending 5,000 or more messages per day to their users. Yahoo uses similar volume-based thresholds. The 5,000/day count is per provider and can be reached in aggregate across subdomains, so a busy program crosses it sooner than founders expect. Below the threshold the rules are strongly recommended; above it they are enforced.
The requirements (effective February 1, 2024 for Gmail/Yahoo; effective May 5, 2025 for Microsoft Outlook/Hotmail/Live):
- Authenticate your mail, all three of:
- SPF (RFC 7208), DNS TXT record authorizing sending IPs.
- DKIM (RFC 6376), cryptographic signature over the message.
- DMARC (RFC 7489), a published policy, at minimum
p=none, with alignment: the domain inFrom:must align with the SPF and/or DKIM domain.
- Keep the user-reported spam-complaint rate below 0.3% (Google strongly recommends staying under 0.1%). Above 0.3% sustained, your mail is throttled or rejected, and you become ineligible for mitigation until you stay under 0.3% for several consecutive days.
- Support one-click unsubscribe (RFC 8058) on marketing mail, and process unsubscribes within 48 hours (covered above).
- Send from a valid, functional From/Reply-To, with valid forward and reverse DNS (PTR) on sending IPs, and use TLS for transmission.
- Format messages to the RFC 5322 standard and don't impersonate Gmail/Yahoo/Microsoft
From:headers.
Copy-paste-ready DNS records (replace the example values):
; SPF, RFC 7208. One TXT record, one "v=spf1", end with a qualifier.
; ~all = softfail (monitor), -all = hardfail (enforce) once you're confident.
example.com. IN TXT "v=spf1 include:_spf.your-provider.example -all"
; DKIM, RFC 6376. Your provider gives you the public key; publish it at the
; selector they specify. Selector here is "s1".
s1._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ...IDAQAB"
; DMARC, RFC 7489. Start at p=none with reporting, then tighten to quarantine/reject.
_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com; adkim=s; aspf=s; pct=100"
Verify with dig before you send a single bulk campaign:
Verification commands
# SPF
dig +short TXT example.com | grep spf1
# DKIM (use your real selector in place of s1)
dig +short TXT s1._domainkey.example.com
# DMARC
dig +short TXT _dmarc.example.com
# Reverse DNS (PTR) on your sending IP must resolve to a hostname in your domain
dig +short -x 203.0.113.10
Where this is enforced vs. where to read the mechanics. The Feb-2024/May-2025 rules are what is required. The how, generating DKIM keys, choosing SPF includes, reading DMARC aggregate (rua) reports, the path from p=none to p=reject, ARC (RFC 8617) for forwarded mail, MTA-STS (RFC 8461) and TLS-RPT (RFC 8460) for enforced transport encryption, and BIMI for verified logos, is covered in depth in Deliverability. This chapter's job is only to flag that authentication crossed from optional to mandatory for bulk senders, and to give you the records to publish.
Common mistakes:
- Publishing two SPF records (only one
v=spf1TXT is allowed per domain; a second one breaks SPF entirely). - Exceeding the SPF 10-DNS-lookup limit with too many
include:chains, causing apermerror. - A DMARC policy that passes SPF/DKIM but fails alignment because the
From:domain differs from the authenticated domain (alignment is the part people miss). - Jumping straight to
p=rejectwithout reading aggregate reports first, then silently blackholing your own legitimate mail. - Assuming sub-5,000/day means "exempt forever", providers apply many of these rules to everyone, and your volume grows.
Can a preference center replace a one-click unsubscribe?
No. Most legislation, and the mailbox-provider rules, require a genuine, single-action unsubscribe. A preference center ("manage preferences") is a valuable addition that can lower your unsubscribe rate, but it does not replace the unsubscribe itself. Offer both.
In practice, a preference center (where users reduce frequency or pick topics) is excellent for retention, but it can never stand in for a real "unsubscribe from everything" option. If a regime or provider requires one-click unsubscribe and your only exit is a multi-step preference form, you are not compliant, and the one-click List-Unsubscribe-Post flow will break because it cannot navigate a form.
The correct pattern:
- The POST one-click flow unsubscribes from the relevant list (or all marketing) immediately, no UI.
- The GET landing page confirms the unsubscribe and then offers the preference center as a secondary, optional path ("Prefer fewer emails instead? Manage preferences").
- The preference center is never the only exit, and never a precondition for leaving.
See Marketing Emails for the UX side of preference centers and List Management for modeling list-scoped vs. global suppression.
What consent records must I keep?
Across GDPR, ePrivacy, and CASL especially, you must be able to prove consent. The GDPR accountability principle (Article 5(2)) makes this explicit: it is not enough to have consent, you must be able to demonstrate it. That means recording consent as structured, queryable, immutable-history data, not as an assumption or a single flag.
Record these fields for every consent event:
- Email address (and any other identifier the consent applies to)
- Exact date/time of consent (UTC, ideally to the second)
- Method (form submission, checkbox, double-opt-in confirmation click, imported with proof, etc.)
- What they consented to (the scope: which lists/purposes, in granular form)
- Source (which page, form, or campaign, and the consent-text version they saw)
- Consent type (CASL: express vs. implied, and for implied, the triggering event and its date, so you can compute expiry)
- Evidence of the affirmative act (e.g. the double-opt-in token and the IP/timestamp of the confirmation click)
How should I store it? Use a database with timestamps, an append-only audit trail of changes, and a link to the user account. The audit trail matters because consent state changes over time (granted → modified → withdrawn), and regulators may ask you to reconstruct that history. Never overwrite the previous state in place; write a new row.
Worked example: a consent-event schema
Code for your engineers (TypeScript)
// Append-only. Never UPDATE a consent row; always INSERT a new event.
// The current consent state is the latest event per (email, scope).
type ConsentEvent = {
id: string; // uuid
email: string; // lowercased, the subject of consent
userId: string | null; // link to account if known
scope: string; // e.g. "weekly-digest", "product-news", "all-marketing"
action: "granted" | "withdrawn" | "modified";
consentType: "express" | "implied"; // CASL-relevant
// For implied consent (CASL): what created it + when, so expiry is computable.
impliedBasis?: "purchase" | "inquiry" | "published-address";
impliedSince?: string; // ISO timestamp of the triggering event
method: "double-opt-in" | "single-opt-in" | "import" | "checkout-checkbox";
source: string; // form/page/campaign id
consentTextVersion: string; // which exact wording the user agreed to
ip?: string; // captured for proof (itself personal data)
userAgent?: string;
occurredAt: string; // ISO 8601, UTC
};
// Computing CASL implied-consent expiry:
function impliedConsentExpiry(e: ConsentEvent): Date | null {
if (e.consentType !== "implied" || !e.impliedSince) return null;
const since = new Date(e.impliedSince);
const months = e.impliedBasis === "inquiry" ? 6 : 24; // 6mo inquiry, 2yr business
// Don't mutate `since`; build a new Date for the expiry.
const expiry = new Date(since);
expiry.setMonth(expiry.getMonth() + months);
return expiry;
}
Consent record checklist
- Email address stored (normalized/lowercased)
- Exact UTC timestamp of consent captured
- Method of consent recorded (which form / checkbox / double-opt-in)
- Scope recorded (granular: what exactly they agreed to)
- Source recorded (page/campaign + consent-text version)
- Consent type recorded (express vs. implied; for implied, basis + start date)
- Evidence of the affirmative act preserved (double-opt-in token / confirmation click)
- Append-only audit trail of subsequent changes maintained
- Records linked to the user account where one exists
For capturing these fields at signup (and double-opt-in mechanics), see Email Capture.
How long can I keep subscriber data?
Only as long as you have a reason to. Holding data forever is itself a compliance risk under the GDPR storage-limitation principle (Article 5(1)(e)). Two regimes set explicit, opposing-looking expectations:
| Law | Requirement |
|---|---|
| GDPR | Keep personal data only as long as necessary; delete when no longer needed |
| CASL | Retain consent records to prove permission (commonly 3 years after expiry/withdrawal) |
These are not contradictory, they apply to different data:
- Retain the consent record (proof of permission, timestamp, method, scope) for as long as you might need to defend it, per CASL and the GDPR accountability principle.
- Do not hoard the broader personal data (behavioral history, profile attributes, message logs) you have no current, lawful use for. Minimize and delete that.
The practical resolution: when you delete a subscriber's profile under a GDPR erasure request, you may still retain a minimal suppression record (a hashed or plain email plus "do not contact" flag and the timestamp), because keeping someone off your list is itself a legitimate, often legally required, reason to retain that one fact. Document this in your retention policy so the retained suppression entry is defensible.
Best practice:
- Have a clear, written retention policy stating how long each data type is kept and why.
- Honor deletion requests promptly (the GDPR "right to be forgotten," Article 17), see the next section.
- Review and clean regularly: prune inactive subscribers and stale profile data on a schedule rather than letting it accumulate. (Re-engagement before pruning protects deliverability; see List Management.)
- Keep a suppression-only record after deletion so you don't accidentally re-add and re-email a person who left.
How do I handle data-subject requests (access, deletion, portability)?
Under GDPR, LGPD, UK GDPR, and the US state privacy laws, individuals can demand action on their data, and you generally have a statutory deadline (GDPR: one month, extendable; CCPA/CPRA: 45 days). Build these as first-class operations, not ad-hoc support tickets.
The requests you must handle for email data:
- Access (Article 15), produce the personal data you hold about them, including their consent history and what messages you've sent. Your consent-event log makes this straightforward.
- Erasure (Article 17), delete their personal data, except the minimal suppression record needed to keep them off your list.
- Portability (Article 20), export their data in a structured, machine-readable format (JSON/CSV).
- Objection to direct marketing (Article 21), absolute: you must stop marketing to them, immediately and without exception. Functionally identical to an unsubscribe, but triggered by any channel (email reply, support ticket, web form), so route all of them into the same suppression action.
Verify identity before acting, but proportionately. Do not demand a passport scan to honor an unsubscribe; the request itself, arriving from the address in question, is usually sufficient for marketing objections. For full data exports, stronger verification is warranted.
Provider-neutral request-handling sketch:
Code for your engineers (TypeScript)
// One entry point for all data-subject requests, so deadlines and audit are uniform.
async function handleDataSubjectRequest(req: {
email: string;
type: "access" | "erasure" | "portability" | "marketing-objection";
}) {
switch (req.type) {
case "marketing-objection":
// Absolute + immediate. Same path as unsubscribe.
await suppression.suppress({ email: req.email, scope: "all-marketing", reason: "art21-objection", at: now() });
return ack("Marketing stopped.");
case "access":
case "portability":
// Gather from your own stores; provider-side message logs may need an API/export.
const profile = await store.exportProfile(req.email); // your DB
const consents = await store.exportConsents(req.email); // consent-event log
return exportBundle({ profile, consents }, req.type === "portability" ? "json" : "html");
case "erasure":
await store.deleteProfile(req.email);
// Retain ONLY a minimal suppression record so they're never re-added.
await suppression.suppress({ email: req.email, scope: "all", reason: "erasure-retain-suppression", at: now() });
// Also request deletion from your sending provider's stored contacts/logs
// via whatever API it exposes (generic call, not ESP-specific here).
await emailClient.deleteContact?.(req.email);
return ack("Data erased; retained suppression only.");
}
}
Common mistakes:
- Treating a marketing objection or unsubscribe arriving via a reply or support ticket as informal and not logging it, it carries the same legal weight as the footer link.
- Deleting someone so thoroughly that you lose the suppression record and later re-import and re-email them.
- Missing the statutory response deadline because requests live in a support queue with no SLA.
- Forgetting that data held by your sending provider (contact lists, event logs) is also in scope, erasure must propagate there too.
What must my privacy policy say?
A compliant program needs a published privacy policy that, at minimum, covers:
- What data you collect (including email address, IP at signup, engagement/open-click data, and any inferred attributes)
- The lawful basis for each processing purpose (consent for marketing, contract for transactional)
- How you use the data
- Who you share data with, explicitly including your email-sending provider as a processor/sub-processor (Amazon SES, Postmark, SendGrid, Mailgun, Resend, Brevo, SparkPost, Mandrill, or whichever you use), plus analytics and CRM
- How long you keep it (your retention policy, summarized)
- User rights (access, deletion, portability, objection) and how to exercise them
- How to contact you about privacy (a real address/channel)
- International transfers, if data leaves the EU/EEA (and the safeguard relied on, e.g. SCCs)
This is both a legal requirement under GDPR/LGPD-style regimes and a trust signal to subscribers. Two engineering-adjacent obligations people forget:
- Name your sub-processors. Your ESP processes personal data on your behalf; GDPR requires a Data Processing Agreement (DPA) with them and disclosure of sub-processors. Most providers publish a standard DPA, execute it.
- Keep it current and linked. Link the policy from your signup forms (so consent is "informed") and your email footers. If your data practices change materially, update the policy; for new purposes you may need fresh consent.
How do I stay compliant when sending across borders?
Build one program for the strictest rule. Because email ignores borders, a single campaign can simultaneously fall under CAN-SPAM, GDPR, ePrivacy, CASL, and more, plus the Gmail/Yahoo/Microsoft acceptance rules on top.
Best practice: follow the most restrictive requirement on each dimension (usually GDPR/ePrivacy for consent, CASL for longevity). Rather than maintaining a separate compliance posture per country, design one program that satisfies the strictest applicable rule on each axis:
- Consent: use explicit opt-in (GDPR/CASL) everywhere, with double-opt-in where deliverability and proof matter most.
- Identification: include a real physical address and accurate, functional From/Reply-To in every commercial message (CAN-SPAM + CASL + Spam Act).
- Unsubscribe timing: process immediately (GDPR) everywhere; never wait out the 10-day ceiling.
- Unsubscribe longevity: keep links valid for at least 60 days (CASL) everywhere.
- Australia's 5-day window: covered automatically if you process immediately.
- Authentication: SPF + DKIM + DMARC with alignment on every sending domain (Feb-2024/May-2025 bulk rules), required to be delivered at all.
- Records: log consent with timestamp, method, scope, source, and type; keep an append-only audit trail.
This "comply with the strictest" strategy is simpler to operate and audit than juggling per-jurisdiction rules, and it keeps you safe as you expand into new markets.
International compliance checklist
- Explicit opt-in used for all marketing recipients (double-opt-in where practical)
- Consent records stored with timestamp, method, scope, source, and type (express/implied)
- Physical mailing address + accurate From/Reply-To in every commercial message
- Unsubscribe processed immediately (suppression checked before every send)
- Unsubscribe link valid for at least 60 days
-
List-Unsubscribe+List-Unsubscribe-Post(RFC 8058 one-click) on bulk marketing sends - SPF (RFC 7208) + DKIM (RFC 6376) + DMARC (RFC 7489) published and aligned
- Spam-complaint rate monitored and kept below 0.3% (target < 0.1%)
- Data-subject requests (access/erasure/portability/objection) handled with an SLA
- Published, current privacy policy that names your ESP sub-processor; DPA executed
- Retention policy in place; deletion requests honored; suppression-only record retained after erasure
Common cross-border mistakes
- Applying CAN-SPAM's permissive opt-out model to EU or Canadian recipients because "that's how we do it in the US."
- Buying or scraping lists, illegal as a basis for marketing under GDPR/CASL, and a fast route to a spam-complaint rate above 0.3%.
- Geo-segmenting consent rules but using a single sending path that ignores the segment at send time.
- Treating the Feb-2024 mailbox-provider rules as US-only, Gmail/Yahoo/Microsoft enforce them globally, by recipient domain.
What should I read next?
- Email Capture: implement consent forms and double opt-in, and capture the consent fields above
- Marketing Emails: consent and unsubscribe requirements in practice, plus preference-center UX
- List Management: suppression lists, handling unsubscribes, and deletion requests
- Deliverability: the full authentication mechanics (SPF/DKIM/DMARC/ARC/MTA-STS/TLS-RPT/BIMI) and the
List-Unsubscribeheader in context - Email Types: Transactional vs Marketing: which rules apply to which messages
- Transactional Catalog: keeping transactional templates free of promotional content
- Webhooks & Events: ingesting unsubscribe and complaint events
- Index
