DataTalksClub / DataTalksClub/website

Extend account email preferences for event and newsletter delivery

Open
#227 25 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

auth data-migration email enhancement events frontend integration P0 security testing
Dominant language
Python
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Parent product decision: #22 (resolved)
Relay ownership authority: #21

Product outcome

Extend the website-owned account email-preference mechanism with two separate optional categories:

  • event email for a recipient actively registered for the relevant event; and
  • marketing/newsletter email.

Preferences are enforced before Relay submission. The website does not introduce a second preference store, route new work through Datamailer or Amazon SES, or add a Slack preference toggle.

Normative authority

  • _docs/specs/01-platform-architecture.md: account identity, after-commit durable work, and Relay ownership.
  • _docs/specs/05-events-registration-email.md: event preference, newsletter audience/import, unsubscribe, Slack, EmailDelivery, and Relay worker contracts.
  • _docs/specs/07-security-privacy-operations.md: minimization, redaction, consent evidence, retention, and protected-data handling.
  • _docs/specs/09-migration-rollout-roadmap.md: send-disabled imports, exact Relay deployment gates, one active sender per purpose, and rollback holds.
  • _docs/specs/10-verification-strategy.md: token, preference, delivery, migration, browser, and network-negative evidence.
  • _docs/PROCESS.md: dependency ordering and engineer -> independent tester -> PM -> merge/on-call lifecycle.

Product contract

Preference categories
  • Event email is optional and applies only to a recipient registered for the relevant event. Turning it off suppresses verification, confirmation, cancellation, reschedule, reminder, calendar invitation, and every other event-lifecycle message. Event preference is separate from newsletter/marketing preference.
  • Marketing/newsletter is a separate optional category. New member/account signup defaults it on, but an unverified new account is not a newsletter recipient. The recipient can unsubscribe later.
  • Slack access is a one-time requested-service message, not a recurring preference category. It has no preference toggle and is not sent again automatically. Account verification and password recovery are also non-optional security messages.
Audiences and onboarding
  • A new account receives email verification first. After successful verification, the Slack-owned onboarding flow may create one Slack join-link delivery; the current link is also available in the authenticated member area.
  • Legacy/imported contacts bypass the new-account verification-to-Slack sequence and do not receive an automatic Slack message merely because they were imported.
  • Newsletter delivery targets verified contacts plus the explicit legacy/imported audience, which is treated as assumed verified for newsletter delivery. A new account must verify before entering the verified audience.
  • An identifiable logged-in interaction or a recipient-specific newsletter-link interaction may move a legacy contact from legacy to verified. This is not a newsletter delivery prerequisite and does not trigger another email. A bare anonymous visit does not identify a contact and does not change status.
Mailchimp reconciliation

Mailchimp is authoritative for legacy marketing subscription state. Provide two separate, idempotent, send-disabled import operations:

  • subscribed list: turn the matched legacy marketing preference on;
  • unsubscribed list: turn the matched marketing preference off.

The operations affect only the marketing/newsletter preference. A contact absent from both lists keeps the current platform preference. The supported input lists are assumed clean and mutually exclusive; overlap precedence is out of scope. Import or replay never queues or sends email.

Unsubscribe and link safety
  • A visible body/footer unsubscribe link opens the website preference page. The recipient unticks unwanted categories and submits; the initial GET is non-mutating.
  • A supported email-header one-click action may immediately and idempotently disable only the optional category represented by that message, without changing unrelated categories.
  • Preference and newsletter-attribution links use opaque, recipient- and purpose-scoped, expiring, replay-safe tokens. Raw email addresses never appear in URLs or logs. A newsletter token is for recipient attribution and legacy-status promotion, not an additional verification gate.

Scope

  • Replace the legacy Datamailer-backed account preference adapter with a website-owned application service and canonical persisted preference state.
  • Expose event and marketing/newsletter controls through the existing authenticated account-settings experience; preserve existing course categories during migration.
  • Apply the same service from HTML, API, imports, business triggers, and Relay submission checks.
  • Implement safe preference-center body links, category-specific email-header one-click handling, and recipient-attribution tokens.
  • Add deterministic, idempotent, send-disabled subscribed and unsubscribed Mailchimp import commands with counts/checksums/conflict reporting and no protected values in routine output.
  • Integrate with the accepted event-registration and ordinary EmailDelivery interfaces; do not invent either contract here.

Acceptance criteria

  • Existing account settings exposes event and marketing/newsletter preferences from one website-owned service/store; existing course categories retain their values and behavior, and no preference read/write calls Datamailer.
  • Event mail is eligible only for an active registrant for the relevant event and only while event preference is enabled. Opt-out suppresses every event-lifecycle delivery intent, including verification and calendar messages.
  • Marketing/newsletter delivery is eligible only for verified contacts and the explicit legacy audience; unverified new accounts are excluded.
  • Preference state is checked atomically before the Relay submission path, with idempotent suppression and no mutation of unrelated categories.
  • A body/footer link opens the preference page without mutating on GET; an authenticated or valid token-authorized submit changes only selected optional categories.
  • The supported email-header one-click action disables only the represented optional category immediately and idempotently; invalid, expired, wrong-purpose, replayed, and near-match requests fail safely without enumeration.
  • No Slack preference exists. The accepted onboarding path remains verification first, then at most one automatic Slack delivery plus authenticated reveal; retries and legacy imports create no automatic duplicate.
  • Identifiable account/newsletter interactions may promote legacy to verified; anonymous, shared, malformed, and un-attributed visits never do.
  • Subscribed and unsubscribed Mailchimp imports independently turn the legacy marketing preference on/off; absence preserves current state; apply-twice is stable and import never creates delivery intent, job, callback, or provider request.
  • Tokens are opaque, recipient- and purpose-scoped, expiring, replay-safe, revocable with the owning identity/preference revision, redacted, and absent from URLs in logs, analytics, audits, screenshots, and errors.
  • Email mutations use owning application services and the accepted single EmailDelivery plus after-commit durable-job/Relay model; no new Datamailer, direct SES, local renderer, or provider-event path exists.
  • Focused Django, migration, security/redaction, API parity, and graph-selected browser tests pass through the current versioned verification plan and independent tester gate.

Browser and integration scenarios

At desktop about 1440x900 and mobile about 390x844:

  1. An authenticated member reads and changes event and marketing preferences; focus, errors, stale submission, refresh, and back navigation preserve a coherent accessible state and never change unrelated course preferences.
  2. A valid body-link token opens the preference center without mutation; submit changes only the chosen category. Invalid, expired, revoked, wrong-purpose, replayed, and another-recipient tokens produce uniform safe outcomes without leaking identity.
  3. The email-header one-click contract disables exactly one optional category without a page visit; repeated requests are idempotent and non-optional messages expose no such action.
  4. Event registration with event mail enabled/disabled exercises the accepted event and delivery fakes and proves intent creation/suppression. No test performs a live Relay, Datamailer, SES, Mailchimp, or provider call.
  5. Verified, unverified-new, and explicit legacy newsletter audiences are distinguished; an attributable legacy interaction may promote status while an anonymous/shared link cannot.
  6. Authorized import preview/apply and denial/error states expose bounded counts and safe reason codes only, never raw addresses, tokens, provider payloads, or message bodies.

Dependencies and sequencing

  • Product decisions are complete in #22 and the authoritative specifications; no new preference or Slack decision is requested here.
  • Website implementation that creates or suppresses delivery work depends on accepted #49 ordinary EmailDelivery/after-commit Relay interfaces. Event-specific enforcement additionally depends on the accepted native registration interface owned by #46. Slack sequencing is owned by #108's bounded Slack child (#249); #227 only preserves the no-toggle/no-duplicate boundary.
  • Any real Relay consumer/send path remains disabled until Relay #1, #2, and #3 are accepted and deployed at an exact pinned commit/OpenAPI version, with website consumer evidence. Relay main 69189fb2911c4b808dd7d4e5913da91d7ded6fa1 adds only a CLAUDE.md pointer over the audited baseline; all three Relay contract issues remain open.
  • #50 consumes this recipient/preference contract when wiring purposes and retiring Datamailer. It is coordinated downstream work, not a prerequisite for defining preferences; do not create a #227 <-> #50 dependency cycle.
  • #225 is a separate anonymous browser analytics-consent surface and is not the account email-preference foundation.
  • Current website origin/main face8e4808d65afbf0374d1ced7a88079950d663 still has a deferred email_app, no native EventRegistration, and an account preference endpoint backed by Datamailer. Therefore this full issue is dependency-blocked before engineering. A narrower source-only foundation would require its own raw intake and PM-groomed child with an interface that cannot pre-empt #49/#46/#249; none exists today.

Non-goals

  • No Slack preference toggle, Slack API/SCIM work, join-secret storage, or independent Slack onboarding implementation.
  • No production Relay, Mailchimp, Datamailer, SES, credential, sender/domain, callback, deployment, protected-data import, or broad-recipient action.
  • No second preference store/UI, local email renderer/template catalog, website provider-attempt/event stack, or synchronous network side effect.
  • No marketing campaign composer, engagement/open/click tracking, analytics consent changes, or overlapping-Mailchimp-list precedence rule.
  • No event-registration model or generic delivery lifecycle invented inside this issue.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the listed _docs/specs files and _docs/PROCESS.md, then inspect the deferred email_app and the current account preference endpoint on origin/main. Confirm the accepted #49 and #46 interfaces and the Relay deployment gates before implementation. Done means the acceptance criteria and focused Django, migration, security/redaction, API, and browser tests pass without provider calls.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend, databases, frontend, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.