- Shipped
- October 15, 2026 at 5:06 AM UTC
- Author
- Kamo
- Commit
- 31ddbfe
Owner 2026-10-15: one company-wide do-not-contact list per organization, scoped per channel; an email unsubscribe blocks email only. - Writer: MirroringEmailOptOutLedger (the EmailOptOutLedger bean) hands every applied change to EmailDoNotContactMirror. A whole unsubscribe (preference page, one-click, List-Unsubscribe mailto, relay) lists the address for EMAIL (source EMAIL_UNSUBSCRIBE, recorded by "Recipient (<source>)" or "Mail system (<source>)"); the person allowing mail again takes it off (EMAIL_RESUBSCRIBE), a member lifting it with the person's consent takes it off with their note (MANUAL). Bounces, complaints, topics and pauses stay the email list's own. Written after the opt-out commits, REQUIRES_NEW, never thrown: an unsubscribe never fails because of the mirror. - Readers: SuppressionService (campaigns, article shares) and SuppressionLookup (eligibility) also count the EMAIL entries, so an address added or imported in Settings -> Phone -> Do not contact is withheld from marketing mail. POST /api/email/send refuses an SI (mail_send, mail_reply, mail_forward) 409 {reason: DO_NOT_CONTACT, error, addresses} on the final recipients, 503 when the list cannot be read; a person's own correspondence keeps the existing rules. Tests: EmailDoNotContactMirrorTest, DoNotContactSuppressionTest, SiMailDoNotContactGuardTest, EmailControllerSendContractTest (RED on origin/main, GREEN here), the other EmailController tests, DeliverabilityWiringTest, SuppressionLookupTest, BulkEmailWiringTest, the arch ratchets.
