# Mail Templates: subject, body and whitelabeling

<div style="text-align: justify;">

A mail needs something to say. **Mail Templates** are the subject and the body, written once and reused by every render that mails a document. They are edited in a WYSIWYG editor, so nobody has to write HTML, and everything variable is a placeholder that VeloxFactory fills in at send time: the trace id, a parameter of the request, the name of the report, a column of the data that was rendered.

A template decides the content. The surrounding mail, a plain card in your application's colours, is built by VeloxFactory, and a template can override parts of it when a customer needs their own look.

<div style="border: 2px dashed #b8bfcc; background: #f7f8fa; padding: 18px 16px; margin: 16px 0; text-align: center; color: #525E5A;">
<strong>Screenshot placeholder:</strong> <code>mail-template.edit.png</code><br>Mail Template edit page with the rich text editor, the toolbar and the placeholder and preview buttons.
</div>

---

<h3 id="where" style="color: #203671; margin-top: 2.2em;">Where to find it</h3>

Mail Templates live in the main menu under **Configuration → Mail Templates**. The overview is a regular VeloxFactory lookup with search over name, subject and description, a filter for the active state and the usual audit filters.

A template has a unique name, which is what a render request may use instead of the ID, a subject, a body and an active flag. An inactive template stays in the master data but is no longer offered and no longer resolves.

---

<h3 id="editor" style="color: #203671; margin-top: 2.2em;">The editor</h3>

The body is written in a rich text editor built into VeloxFactory. No add-on, no external service, and nothing to learn: the toolbar does what a toolbar does.

<table style="width: 100%; border-collapse: collapse;">
  <thead>
    <tr style="border-top: 1px solid #e6e8ef; border-bottom: 1px solid #e6e8ef;">
      <th style="text-align: left; padding: 6px 10px; white-space: nowrap;">Group</th>
      <th style="text-align: left; padding: 6px 10px;">Tools</th>
    </tr>
  </thead>
  <tbody>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Text</td>
      <td style="padding: 6px 10px;">Bold, italic, underline, headings, paragraph, bullet and numbered lists, text colour and remove colour, clear formatting.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Links</td>
      <td style="padding: 6px 10px;">Insert and remove links. Only <code>http</code>, <code>https</code> and <code>mailto</code> are accepted, and every link is marked so it opens safely.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Tables</td>
      <td style="padding: 6px 10px;">Insert a table with a chosen number of rows and columns and an optional header row, then insert or delete rows and columns from wherever the caret sits.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Images</td>
      <td style="padding: 6px 10px;">Insert a graphic from the Common Report Resources, with an optional width.</td>
    </tr>
    <tr>
      <td style="padding: 6px 10px; white-space: nowrap;">Placeholders</td>
      <td style="padding: 6px 10px;">Insert a placeholder at the caret, chosen from the catalog.</td>
    </tr>
  </tbody>
</table>

Pasting is deliberately plain text. Formatted content dragged in from Word or a browser is the single most common reason a mail looks broken in one client and fine in another, so the editor strips it and keeps the words.

Whatever the editor produces is cleaned on the server against an allowlist before it is stored. That is not a formality: it is what guarantees that a template cannot carry a script, a tracking pixel or an arbitrary external image, no matter what was pasted into it.

---

<h3 id="placeholders" style="color: #203671; margin-top: 2.2em;">Placeholders</h3>

Anything in square brackets is replaced when the mail is built. Both the subject and the body support them.

> Hello [contactName], attached is your report [report.name] with the trace ID [traceId], rendered on [now.dateLocale].

The **Placeholders** button opens a dialog listing every token that is available, grouped, with a description and an example, and each one copyable with a click. Opened from a template that is tied to a report, the parameter and field tokens are expanded to the real names of that report.

<table style="width: 100%; border-collapse: collapse;">
  <thead>
    <tr style="border-top: 1px solid #e6e8ef; border-bottom: 1px solid #e6e8ef;">
      <th style="text-align: left; padding: 6px 10px; white-space: nowrap;">Group</th>
      <th style="text-align: left; padding: 6px 10px;">Examples</th>
    </tr>
  </thead>
  <tbody>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">General</td>
      <td style="padding: 6px 10px;"><code>[traceId]</code>, <code>[reportFileName]</code> (the attachment name), <code>[reportUrl]</code>, <code>[outputType]</code>, <code>[appName]</code>, <code>[contactName]</code>, <code>[broadcastId]</code></td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Generic</td>
      <td style="padding: 6px 10px;"><code>[now.date]</code>, <code>[now.dateLocale]</code>, <code>[now.dateTime]</code>, <code>[now.weekday]</code>, <code>[now.monthName]</code>, <code>[now.weekNumber]</code>, <code>[now.quarter]</code></td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Report</td>
      <td style="padding: 6px 10px;"><code>[report.name]</code>, <code>[report.description]</code>, <code>[report.context]</code>, <code>[report.connection]</code></td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">History Record</td>
      <td style="padding: 6px 10px;"><code>[historyRecord.id]</code>, <code>[historyRecord.status]</code>, <code>[historyRecord.url]</code> as a direct link into VeloxFactory</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">User, API Token</td>
      <td style="padding: 6px 10px;"><code>[user.name]</code>, <code>[user.email]</code>, <code>[token.name]</code> - who or what triggered the render</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Printer</td>
      <td style="padding: 6px 10px;"><code>[printer.name]</code>, <code>[printer.copies]</code> when a print task was created alongside</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Parameters, Data</td>
      <td style="padding: 6px 10px;"><code>[parameters.P_ORDER_NO]</code>, <code>[data.count]</code>, <code>[data.first.CUSTOMER]</code>, <code>[data.last.ARTICLE]</code></td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Recipients</td>
      <td style="padding: 6px 10px;"><code>[recipients.toCount]</code>, <code>[recipients.ccCount]</code>, <code>[recipients.bccCount]</code></td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Environment</td>
      <td style="padding: 6px 10px;"><code>[env.appEnv]</code>, <code>[env.hostname]</code>, <code>[env.timezone]</code> - to mark a mail from a test system as such</td>
    </tr>
    <tr>
      <td style="padding: 6px 10px; white-space: nowrap;">Images</td>
      <td style="padding: 6px 10px;"><code>[image.logo_dark]</code> - one entry per usable Common Report Resource</td>
    </tr>
  </tbody>
</table>

<div style="border-left: 4px solid #5fc75d; background: #f6fdf6; padding: 10px 16px; margin: 16px 0; border-radius: 0 4px 4px 0;">
ℹ️ <strong>An unknown placeholder never breaks a mail.</strong> It resolves to an empty string and is reported back to the caller, so a typo shows up as a gap and a note, not as a failed send.
</div>

The same catalog is available beyond subject and body: recipients, contact name and the names the attachments carry in the mail accept placeholders too. See the Report Mailing page for how that behaves.

---

<h3 id="images" style="color: #203671; margin-top: 2.2em;">Graphics</h3>

Mail graphics are the **Common Report Resources** you already maintain, the same PNG and JPEG files the reports use. A logo is uploaded once and serves both a printed label and a mail.

Insert one with the image button, or type its placeholder, for example `[image.logo_dark]`. Both produce the same thing, and the editor shows the graphic while you write.

At send time the graphic is embedded **into** the mail as an inline attachment, not linked from a server. That matters for two reasons: the mail is self-contained, so it also works for a recipient outside your network, and no client shows a *load external images* bar or treats it as a tracking pixel.

<div style="border-left: 4px solid #5fc75d; background: #f6fdf6; padding: 10px 16px; margin: 16px 0; border-radius: 0 4px 4px 0;">
ℹ️ <strong>Only graphics from the master data.</strong> An image pasted in from a website is removed when the template is saved. If a graphic should be in a mail, upload it under Configuration → Common Report Resources and reference it by name.
</div>

A Common Report Resource that a template embeds cannot be deleted or renamed while the template uses it. The refusal names the template, so it is clear what would break.

---

<h3 id="whitelabel" style="color: #203671; margin-top: 2.2em;">Whitelabeling</h3>

By default every mail looks the same: a card in the colours of your VeloxFactory installation, read straight from the application's stylesheet, with a coloured header bar and a footer. Change a colour in the frontend and the mails follow, without touching a template.

When one customer or one process needs something else, a template can override parts of that frame. Each override has its own switch, and switching it off returns that part to the global default:

<table style="width: 100%; border-collapse: collapse;">
  <thead>
    <tr style="border-top: 1px solid #e6e8ef; border-bottom: 1px solid #e6e8ef;">
      <th style="text-align: left; padding: 6px 10px; white-space: nowrap;">Override</th>
      <th style="text-align: left; padding: 6px 10px;">Effect</th>
    </tr>
  </thead>
  <tbody>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Header colour</td>
      <td style="padding: 6px 10px;">The colour of the bar at the top of the card, instead of the theme colour.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Body colour</td>
      <td style="padding: 6px 10px;">The background of the content area, instead of white.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Logo</td>
      <td style="padding: 6px 10px;">A different Common Report Resource in the header, instead of the configured logo.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;">Hide logo</td>
      <td style="padding: 6px 10px;">No logo at all, for a plain header.</td>
    </tr>
    <tr>
      <td style="padding: 6px 10px; white-space: nowrap;">Footer text</td>
      <td style="padding: 6px 10px;">A different footer line. Supports the full placeholder catalog, so it can carry a trace id, a contact or a legal note.</td>
    </tr>
  </tbody>
</table>

<div style="border: 2px dashed #b8bfcc; background: #f7f8fa; padding: 18px 16px; margin: 16px 0; text-align: center; color: #525E5A;">
<strong>Screenshot placeholder:</strong> <code>mail-template.whitelabel.png</code><br>The whitelabeling block with the per-field switches for header colour, body colour, logo and footer text.
</div>

---

<h3 id="preview" style="color: #203671; margin-top: 2.2em;">Preview</h3>

The **Preview** button renders the template inside the real mail frame, with example values or against an existing history record, and shows it in a dialog. It is the same rendering path the actual mail uses and the same one the Mail Queue uses to show a sent mail, so the three cannot drift apart: what the preview shows is what is sent and what is archived.

Use it after every change. It is the fastest way to catch a placeholder that resolves to nothing, a table that lost its borders or a whitelabel colour that makes the text unreadable.

---

<h3 id="api" style="color: #203671; margin-top: 2.2em;">The API</h3>

Mail Templates are a regular API resource under `/api/v1/mail-template`.

<table style="width: 100%; border-collapse: collapse;">
  <thead>
    <tr style="border-top: 1px solid #e6e8ef; border-bottom: 1px solid #e6e8ef;">
      <th style="text-align: left; padding: 6px 10px; white-space: nowrap;">Endpoint</th>
      <th style="text-align: left; padding: 6px 10px;">Description</th>
    </tr>
  </thead>
  <tbody>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;"><code>GET /mail-template/placeholders</code></td>
      <td style="padding: 6px 10px;">The placeholder catalog, optionally expanded for a given <code>reportConfig</code>.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;"><code>GET /mail-template</code></td>
      <td style="padding: 6px 10px;">List templates.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;"><code>GET /mail-template/{id}</code></td>
      <td style="padding: 6px 10px;">A single template, with its audit segment on request.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;"><code>POST /mail-template</code></td>
      <td style="padding: 6px 10px;">Create a template. The body is sent Base64-encoded and is cleaned against the allowlist before it is stored.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;"><code>POST /mail-template/{id}/preview</code></td>
      <td style="padding: 6px 10px;">Render subject and body, with example values or against a history record, and report which placeholders stayed unresolved.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;"><code>PATCH /mail-template/{id}</code></td>
      <td style="padding: 6px 10px;">Partial update.</td>
    </tr>
    <tr>
      <td style="padding: 6px 10px; white-space: nowrap;"><code>DELETE /mail-template/{id}</code></td>
      <td style="padding: 6px 10px;">Delete a template, refused while mail tasks still reference it.</td>
    </tr>
  </tbody>
</table>

Unknown placeholders in a stored template are not an error. They are returned as messages, so an integration can warn without being blocked.

---

<h3 id="mcp" style="color: #203671; margin-top: 2.2em;">AI assistants</h3>

The MCP server exposes the same operations, including the placeholder catalog. An assistant can therefore write a template that uses the real parameter names of a specific report, and preview it before anyone sends it.

---

<h3 id="permissions" style="color: #203671; margin-top: 2.2em;">Permissions</h3>

Mail Templates use the standard permission scheme and have their own **Mail Templates** section in the user editor.

<table style="width: 100%; border-collapse: collapse;">
  <thead>
    <tr style="border-top: 1px solid #e6e8ef; border-bottom: 1px solid #e6e8ef;">
      <th style="text-align: left; padding: 6px 10px; white-space: nowrap;">Permission</th>
      <th style="text-align: left; padding: 6px 10px;">Grants</th>
    </tr>
  </thead>
  <tbody>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;"><code>mail-template:read</code></td>
      <td style="padding: 6px 10px;">See the menu entry, the overview and a template. Also required to pick a template when sending.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;"><code>mail-template:create</code></td>
      <td style="padding: 6px 10px;">Create templates.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;"><code>mail-template:update</code></td>
      <td style="padding: 6px 10px;">Change templates, including the whitelabeling and the active state.</td>
    </tr>
    <tr style="border-bottom: 1px solid #e6e8ef;">
      <td style="padding: 6px 10px; white-space: nowrap;"><code>mail-template:delete</code></td>
      <td style="padding: 6px 10px;">Delete templates.</td>
    </tr>
    <tr>
      <td style="padding: 6px 10px; white-space: nowrap;"><code>mail-template:full</code></td>
      <td style="padding: 6px 10px;">All of the above, like <code>global:admin</code>.</td>
    </tr>
  </tbody>
</table>

Uploading the graphics a template uses is a separate permission, `common-report-resource:create` and `:update`, because those files are shared with the reports.

</div>