Unsubscribe Flow — Technical Overview¶
What It Does¶
RFC 8058-compliant one-click unsubscribe for all transactional emails. When a user clicks "Unsubscribe" (either via their email client's built-in button or the link in the email body), they are immediately opted out of all future emails.
How It Works¶
Email Sending (Outbound)¶
Every outgoing email includes two things:
- RFC 8058 headers —
List-UnsubscribeandList-Unsubscribe-Post. These tell Gmail/Yahoo/Outlook to show a native "Unsubscribe" button in the email UI. - Signed unsubscribe URL — passed as a template variable for the in-body unsubscribe link.
The URL looks like:
https://api.studiojadu.com/api/unsubscribe?email=user@example.com&sig=<hmac_hex>
Token Strategy: HMAC-SHA256 (Stateless)¶
- When sending an email, we compute
HMAC-SHA256(email, UNSUBSCRIBE_SECRET)to produce thesig. - When receiving an unsubscribe request, we recompute the HMAC and compare. If it matches, the request is legit.
- No database lookup needed for validation — fully stateless.
- Prevents anyone from forging unsubscribe URLs for other users without knowing the secret.
Unsubscribe Processing (Inbound)¶
Two endpoints handle unsubscribe requests:
POST /api/unsubscribe — Used by email clients (Gmail, Yahoo) via RFC 8058 one-click mechanism. - Validates HMAC signature - Records the opt-out - Returns empty 200 (per RFC spec)
GET /api/unsubscribe — Used when a user manually clicks the link in the email body. - Validates HMAC signature - Records the opt-out - Returns a branded HTML confirmation page ("You've been unsubscribed")
Data Storage¶
Opted-out emails are stored in a separate unsubscribedUsers collection (not mixed into formSubmissions). This keeps the unsubscribe list decoupled — any future email-sending service can check it independently.
Each record is just { email, unsubscribedAt } with a unique index on email. Duplicate unsubscribe requests are handled gracefully via upsert.
Flow Diagram¶
┌─────────────────┐
│ Send Email │
│ │
│ 1. Generate │
│ HMAC sig │
│ 2. Build URL │
│ 3. Attach │
│ headers + │
│ template │
│ variable │
└────────┬────────┘
│
▼
┌─────────────────┐ ┌──────────────────────┐
│ User receives │ │ Email client shows │
│ email │─────▶│ "Unsubscribe" button│
└────────┬────────┘ └──────────┬───────────┘
│ │
│ clicks link │ one-click (POST)
▼ ▼
┌─────────────────┐ ┌──────────────────────┐
│ GET /api/ │ │ POST /api/ │
│ unsubscribe │ │ unsubscribe │
│ │ │ │
│ → Validate sig │ │ → Validate sig │
│ → Record opt- │ │ → Record opt-out │
│ out in DB │ │ → Return 200 empty │
│ → Show HTML │ │ │
│ confirmation │ │ │
└─────────────────┘ └──────────────────────┘
Security¶
- Anti-forgery: Without
UNSUBSCRIBE_SECRET, no one can craft a valid URL for any email address. - Stateless validation: No DB round-trip to verify the link — just recompute and compare.
- Non-reversible: HMAC is one-way. Even if a
sigleaks, the secret stays safe. - Per-email unique: Each email address produces a different signature.
Deliverability Impact¶
- Gmail and Yahoo require these headers for bulk senders (Feb 2024 rules). Without them, emails risk spam classification.
- Even for low-volume transactional email, the headers improve inbox placement.
- The RFC 8058 POST mechanism is what lets email clients offer the native "Unsubscribe" button without opening a browser.
Environment¶
Requires one secret: UNSUBSCRIBE_SECRET (random 32-byte hex string). Set in Worker secrets/env.
Database¶
Collection: unsubscribedUsers in the lander database.
Index: { email: 1 } (unique).