{"id":"69b918b05015a698c66d1f90","title":"2026 03 16 Issue 177","path":"how-to/committee/release-notes/2026-03-16-issue-177","contentMarkdown":"# 16-Mar-2026 — Customisable email templates, placeholder toolbar, and email routing diagnostics\n\n## [build 540](https://github.com/nbarrett/ngx-ramblers/actions/runs/23185962824) — [commit cc7411f](https://github.com/nbarrett/ngx-ramblers/commit/cc7411f4dbe023d78a3d3b7bf5e0d51d02ee5e1c)\n\n_____\n\n### Covers: follow-up notifications, mailing lists, and member priority ([#177](https://github.com/nbarrett/ngx-ramblers/issues/177)),  improve transactional email deliverability across all environments[#186](https://github.com/nbarrett/ngx-ramblers/issues/186))\n\n- Add admin-editable markdown email templates with section toggle, edit/preview mode, insertable placeholder fields via toolbar dropdown and per-event overrides for all five email types. \n- Introduce BookingEmailType enum replacing string literals across frontend and backend. \n- Add data migration to backfill default templates and help content. \n- Show SPF, DKIM, and DMARC authentication results with colour-coded badges and plain-English summaries in the email routing log. \n- Fix worker invocation log timing and row striping.\n\n## User Guide — Booking Email Templates & Reminder Notifications\n- Admins can now customise the content of every booking-related email and schedule automatic reminder emails to be sent before events.\n- All configuration is managed from the **Bookings → Configuration** tab in the admin area.\n## Email Types\nFive email types are supported, each sent automatically at the\nappropriate point in the booking lifecycle:\n| Email            | Triggered when                                          |\n|------------------|---------------------------------------------------------|\n| **Confirmation** | A booking is created                                    |\n| **Cancellation** | A booking is cancelled by the attendee or an admin      |\n| **Waitlisted**   | A non-member booking is displaced during member priority |\n| **Restored**     | A waitlisted booking is restored after a cancellation   |\n| **Reminder**     | The event is within the configured reminder window      |\n## Customising Email Templates\n### Global Templates\nNavigate to **Bookings → Configuration → Email Templates**. Each email type has a markdown editor where you can write the email body. A live preview is shown alongside the editor.\n### Per-Event Overrides\n- For **confirmation** and **reminder** emails, you can override the global template on a per-event basis. Navigate to **Bookings → Per-Event Detail**, select an event, and edit the override fields.\n- If an override is set, it takes priority over the global template for that event.\n### Template Resolution Order\nWhen sending an email the system resolves the template as follows:\n1. **Per-event override** (confirmation and reminder only)\n2. **Global template** from booking configuration\n3. **Built-in default** (hardcoded fallback)\nThe first non-empty value wins.\n## Placeholders\nTemplates support the following placeholders, which are replaced with\nreal values at send time:\n| Placeholder          | Replaced with                                       |\n|----------------------|-----------------------------------------------------|\n| `{{EVENT_TITLE}}`    | The event title                                     |\n| `{{EVENT_DATE}}`     | Formatted date and time (e.g. Wednesday, 15 March…) |\n| `{{EVENT_LINK}}`     | URL to the event page                               |\n| `{{ATTENDEE_NAME}}`  | First name of the primary attendee                  |\n| `{{ATTENDEE_LIST}}`  | Markdown list of all attendees with email addresses  |\n| `{{PLACES_COUNT}}`   | Number of places in the booking                     |\nInclude them in your template text exactly as shown (with double\nbraces). Unknown placeholders are left as-is.\n## Reminder Emails\n### Configuration\n- In **Bookings → Configuration**, set the **Reminder days before event** field to the number of days before an event that reminders should be sent.\n- Set to **0** to disable reminders.\n### How It Works\nA daily cron job runs at **08:00** each morning. It:\n1. Reads the configured `reminderDaysBefore` value.\n1. Finds all events whose start date falls within the reminder window\n(today → today + reminderDaysBefore).\n1. For each event, finds active bookings that have not yet received a\nreminder (no `reminderSentAt` timestamp).\n1. Sends the reminder email and stamps `reminderSentAt` on the booking.\nEach booking receives at most one reminder — the process is idempotent.\n## Brevo Integration\n- Emails are sent via the existing Brevo transactional email pipeline.\n- The `bookingNotificationConfigId` must be set in the mail configuration to point to a valid Brevo notification config. If it is not configured, booking emails are silently skipped.\n- The resolved markdown template is rendered to HTML and injected into the Brevo template as the `BODY_CONTENT` merge field alongside the standard site merge fields (banner, signoff, app name, etc.).","contentHtml":"<h1>16-Mar-2026 — Customisable email templates, placeholder toolbar, and email routing diagnostics</h1>\n<h2><a href=\"https://github.com/nbarrett/ngx-ramblers/actions/runs/23185962824\">build 540</a> — <a href=\"https://github.com/nbarrett/ngx-ramblers/commit/cc7411f4dbe023d78a3d3b7bf5e0d51d02ee5e1c\">commit cc7411f</a></h2>\n<hr>\n<h3>Covers: follow-up notifications, mailing lists, and member priority (<a href=\"https://github.com/nbarrett/ngx-ramblers/issues/177\">#177</a>),  improve transactional email deliverability across all environments<a href=\"https://github.com/nbarrett/ngx-ramblers/issues/186\">#186</a>)</h3>\n<ul>\n<li>Add admin-editable markdown email templates with section toggle, edit/preview mode, insertable placeholder fields via toolbar dropdown and per-event overrides for all five email types. </li>\n<li>Introduce BookingEmailType enum replacing string literals across frontend and backend. </li>\n<li>Add data migration to backfill default templates and help content. </li>\n<li>Show SPF, DKIM, and DMARC authentication results with colour-coded badges and plain-English summaries in the email routing log. </li>\n<li>Fix worker invocation log timing and row striping.</li>\n</ul>\n<h2>User Guide — Booking Email Templates &amp; Reminder Notifications</h2>\n<ul>\n<li>Admins can now customise the content of every booking-related email and schedule automatic reminder emails to be sent before events.</li>\n<li>All configuration is managed from the <strong>Bookings → Configuration</strong> tab in the admin area.</li>\n</ul>\n<h2>Email Types</h2>\n<p>Five email types are supported, each sent automatically at the\nappropriate point in the booking lifecycle:</p>\n<table>\n<thead>\n<tr>\n<th>Email</th>\n<th>Triggered when</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><strong>Confirmation</strong></td>\n<td>A booking is created</td>\n</tr>\n<tr>\n<td><strong>Cancellation</strong></td>\n<td>A booking is cancelled by the attendee or an admin</td>\n</tr>\n<tr>\n<td><strong>Waitlisted</strong></td>\n<td>A non-member booking is displaced during member priority</td>\n</tr>\n<tr>\n<td><strong>Restored</strong></td>\n<td>A waitlisted booking is restored after a cancellation</td>\n</tr>\n<tr>\n<td><strong>Reminder</strong></td>\n<td>The event is within the configured reminder window</td>\n</tr>\n</tbody></table>\n<h2>Customising Email Templates</h2>\n<h3>Global Templates</h3>\n<p>Navigate to <strong>Bookings → Configuration → Email Templates</strong>. Each email type has a markdown editor where you can write the email body. A live preview is shown alongside the editor.</p>\n<h3>Per-Event Overrides</h3>\n<ul>\n<li>For <strong>confirmation</strong> and <strong>reminder</strong> emails, you can override the global template on a per-event basis. Navigate to <strong>Bookings → Per-Event Detail</strong>, select an event, and edit the override fields.</li>\n<li>If an override is set, it takes priority over the global template for that event.</li>\n</ul>\n<h3>Template Resolution Order</h3>\n<p>When sending an email the system resolves the template as follows:</p>\n<ol>\n<li><strong>Per-event override</strong> (confirmation and reminder only)</li>\n<li><strong>Global template</strong> from booking configuration</li>\n<li><strong>Built-in default</strong> (hardcoded fallback)\nThe first non-empty value wins.</li>\n</ol>\n<h2>Placeholders</h2>\n<p>Templates support the following placeholders, which are replaced with\nreal values at send time:</p>\n<table>\n<thead>\n<tr>\n<th>Placeholder</th>\n<th>Replaced with</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>{{EVENT_TITLE}}</code></td>\n<td>The event title</td>\n</tr>\n<tr>\n<td><code>{{EVENT_DATE}}</code></td>\n<td>Formatted date and time (e.g. Wednesday, 15 March…)</td>\n</tr>\n<tr>\n<td><code>{{EVENT_LINK}}</code></td>\n<td>URL to the event page</td>\n</tr>\n<tr>\n<td><code>{{ATTENDEE_NAME}}</code></td>\n<td>First name of the primary attendee</td>\n</tr>\n<tr>\n<td><code>{{ATTENDEE_LIST}}</code></td>\n<td>Markdown list of all attendees with email addresses</td>\n</tr>\n<tr>\n<td><code>{{PLACES_COUNT}}</code></td>\n<td>Number of places in the booking</td>\n</tr>\n<tr>\n<td>Include them in your template text exactly as shown (with double</td>\n<td></td>\n</tr>\n<tr>\n<td>braces). Unknown placeholders are left as-is.</td>\n<td></td>\n</tr>\n</tbody></table>\n<h2>Reminder Emails</h2>\n<h3>Configuration</h3>\n<ul>\n<li>In <strong>Bookings → Configuration</strong>, set the <strong>Reminder days before event</strong> field to the number of days before an event that reminders should be sent.</li>\n<li>Set to <strong>0</strong> to disable reminders.</li>\n</ul>\n<h3>How It Works</h3>\n<p>A daily cron job runs at <strong>08:00</strong> each morning. It:</p>\n<ol>\n<li>Reads the configured <code>reminderDaysBefore</code> value.</li>\n<li>Finds all events whose start date falls within the reminder window\n(today → today + reminderDaysBefore).</li>\n<li>For each event, finds active bookings that have not yet received a\nreminder (no <code>reminderSentAt</code> timestamp).</li>\n<li>Sends the reminder email and stamps <code>reminderSentAt</code> on the booking.\nEach booking receives at most one reminder — the process is idempotent.</li>\n</ol>\n<h2>Brevo Integration</h2>\n<ul>\n<li>Emails are sent via the existing Brevo transactional email pipeline.</li>\n<li>The <code>bookingNotificationConfigId</code> must be set in the mail configuration to point to a valid Brevo notification config. If it is not configured, booking emails are silently skipped.</li>\n<li>The resolved markdown template is rendered to HTML and injected into the Brevo template as the <code>BODY_CONTENT</code> merge field alongside the standard site merge fields (banner, signoff, app name, etc.).</li>\n</ul>\n"}