{"id":"6a77bfc977822257266a7ced","title":"2026 08 08 Issue 354","path":"how-to/committee/release-notes/2026-08-08-issue-354","contentMarkdown":"# 08-Aug-2026 — persist Brevo deny events as local email blocks [ref #354](https://github.com/nbarrett/ngx-ramblers/issues/354)\n\n## [build 818](https://github.com/nbarrett/ngx-ramblers/actions/runs/31284079316) — [commit b05a667](https://github.com/nbarrett/ngx-ramblers/commit/b05a667254a5676a8e348d17a06eb5982cd95b3a)\n\n_____\n\n## What's new\n\nWhen Brevo reports spam, a complaint, or an unsubscribe for a member, NGX now records that as a local email block and clears their mailing-list ticks, so the member admin view and outbound suppression match what Brevo will actually deliver. The first time that happens, consent writeback uses the same shared helper as the Team Emails work. Domain models and admin UI also use deny/denied language for channel suppression, with Brevo SDK field names mapped only at the API boundary.\n\nMail Settings → Blocks now lists every contact Brevo has globally denied for email, not only the transactional blocklist. Rows are enriched from campaign and transactional history so admins see the real reason, campaign subject, sender role, and event time - the same story as the member Brevo activity timeline. Existing denied contacts are backfilled via migration where audit rows exist, and on the next Blocks sync or unsubscribes cron for the rest.\n\nOn a campaign report, drilling into a stat (for example hard bounces) updates the URL so that view can be shared or bookmarked, and matched problem subscribers link through to their member admin record.\n\n## At a glance\n\n- Spam, complaint and unsubscribe webhooks create a local email block with a clear reason\n- All local mailing-list subscriptions are switched off for that member, not only lists still returned by Brevo\n- A durable audit row is written for the block\n- First transition into a block can attempt Head Office consent writeback when configured\n- List-sync block handling uses the same \"clear every subscribed list\" rule\n- Member Brevo channel badges show Email denied / SMS denied\n- Mail Settings → Blocks includes global email-denied contacts as well as transactional blocks\n- Block rows show event reason (spam, hard bounce, unsubscribe), campaign subject, sender (committee role name when known), and the real event date\n- Related near-simultaneous events appear as a secondary note (for example also unsubscribed)\n- Blocks table layout keeps rows compact with fixed columns and truncated list names\n- Migration backfills local emailBlock from existing Brevo block audit rows; remaining denied contacts are filled on Blocks sync\n- Campaign report drill-downs (opens, clicks, bounces, and so on) write `eventType` into the URL for sharing\n- Matched recipients on the campaign detail list open Member Admin via their membership number","contentHtml":"<h1>08-Aug-2026 — persist Brevo deny events as local email blocks <a href=\"https://github.com/nbarrett/ngx-ramblers/issues/354\">ref #354</a></h1>\n<h2><a href=\"https://github.com/nbarrett/ngx-ramblers/actions/runs/31284079316\">build 818</a> — <a href=\"https://github.com/nbarrett/ngx-ramblers/commit/b05a667254a5676a8e348d17a06eb5982cd95b3a\">commit b05a667</a></h2>\n<hr>\n<h2>What&#39;s new</h2>\n<p>When Brevo reports spam, a complaint, or an unsubscribe for a member, NGX now records that as a local email block and clears their mailing-list ticks, so the member admin view and outbound suppression match what Brevo will actually deliver. The first time that happens, consent writeback uses the same shared helper as the Team Emails work. Domain models and admin UI also use deny/denied language for channel suppression, with Brevo SDK field names mapped only at the API boundary.</p>\n<p>Mail Settings → Blocks now lists every contact Brevo has globally denied for email, not only the transactional blocklist. Rows are enriched from campaign and transactional history so admins see the real reason, campaign subject, sender role, and event time - the same story as the member Brevo activity timeline. Existing denied contacts are backfilled via migration where audit rows exist, and on the next Blocks sync or unsubscribes cron for the rest.</p>\n<p>On a campaign report, drilling into a stat (for example hard bounces) updates the URL so that view can be shared or bookmarked, and matched problem subscribers link through to their member admin record.</p>\n<h2>At a glance</h2>\n<ul>\n<li>Spam, complaint and unsubscribe webhooks create a local email block with a clear reason</li>\n<li>All local mailing-list subscriptions are switched off for that member, not only lists still returned by Brevo</li>\n<li>A durable audit row is written for the block</li>\n<li>First transition into a block can attempt Head Office consent writeback when configured</li>\n<li>List-sync block handling uses the same &quot;clear every subscribed list&quot; rule</li>\n<li>Member Brevo channel badges show Email denied / SMS denied</li>\n<li>Mail Settings → Blocks includes global email-denied contacts as well as transactional blocks</li>\n<li>Block rows show event reason (spam, hard bounce, unsubscribe), campaign subject, sender (committee role name when known), and the real event date</li>\n<li>Related near-simultaneous events appear as a secondary note (for example also unsubscribed)</li>\n<li>Blocks table layout keeps rows compact with fixed columns and truncated list names</li>\n<li>Migration backfills local emailBlock from existing Brevo block audit rows; remaining denied contacts are filled on Blocks sync</li>\n<li>Campaign report drill-downs (opens, clicks, bounces, and so on) write <code>eventType</code> into the URL for sharing</li>\n<li>Matched recipients on the campaign detail list open Member Admin via their membership number</li>\n</ul>\n"}