Skip to content

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:

  1. RFC 8058 headers — List-Unsubscribe and List-Unsubscribe-Post. These tell Gmail/Yahoo/Outlook to show a native "Unsubscribe" button in the email UI.
  2. 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 the sig.
  • 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 sig leaks, 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).