Vetrix Docs

One-click unsubscribe flow

Vetrix implements RFC 8058 one-click unsubscribe. Every notification-category email carries two headers:

List-Unsubscribe: <mailto:unsubscribe@...>,
                  <https://<host>/email/unsubscribe?token=<raw>>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

Transactional slugs (password_reset, smtp_test) never carry these headers — those emails don't represent recurring contact.

Token lifecycle

  1. The Deliverer mints a 32-byte random token on every notification-category send when email.list_unsubscribe_enabled is true.
  2. Only the SHA-256 hex of the token is persisted (in email_unsubscribe_tokens); the raw token appears exclusively in the outbound header.
  3. Tokens expire after 30 days.

Consume paths

  • POST /api/v1/email/unsubscribe?token=<raw> — the one-click endpoint Gmail / Yahoo hit automatically. Returns { "status": "unsubscribed", "slug": "issue.assigned" } on success, 410 Gone on expired / already-used / missing tokens.
  • GET /api/v1/email/unsubscribe?token=<raw> — redirects to /email/unsubscribed for clients that still issue GETs on the header URL.
  • /email/unsubscribe — public server-rendered page that server-side POSTs the consume endpoint and renders one of four states (success, already-consumed, expired, invalid).

Security

  • Token is single-use — the consume path runs UPDATE email_unsubscribe_tokens SET revoked_at = NOW() WHERE token_hash = $1 AND revoked_at IS NULL AND expires_at > NOW() RETURNING * so a concurrent double-click can't both succeed.
  • Rate-limited at 10 requests per IP per hour via the existing internal/ratelimit harness.
  • The invalid state on the public page never distinguishes missing from malformed tokens → no enumeration oracle.