Your D365 Emails Deserve Better Than Plain Text | Build Professional HTML Email Templates in Dynamics 365 Finance & Operations | D365 FO X++
Your D365 Emails Deserve Better Than Plain Text | Build Professional HTML Email Templates in Dynamics 365 Finance & Operations
ERP notification emails have a reputation problem. Approval alerts, violation notices, document-ready pings — most of them look like they were built in 2004: plain text, no branding, maybe a logo if you're lucky. It doesn't have to be that way.
If you've built a notification email in Dynamics 365 Finance & Operations, you've likely faced this question: where does the header banner, the logo, and the footer icons actually come from — and how do you make the email actually look good without turning it into a maintenance nightmare?
A common first instinct is to hardcode the whole email as an HTML string in X++, with the images either linked to a random hosted path or (worse) embedded as long Base64 strings baked directly into the code. It works, but it's fragile: every rebrand or logo tweak becomes a code change and a deployment, the class balloons with a wall of markup, and nobody outside the dev team can touch it.
There's a much cleaner way — and it comes down to one core idea: host your images externally and reference them by URL inside the HTML template, rather than embedding or hardcoding them.
Why Online-Hosted Images Are the Right Call
When you're building the HTML body for an email template (whether that lives in D365's SysEmailTable/SysEmailMessageTable or anywhere else), you have two real options for getting an image into the email:
1. Online-hosted images (recommended)
<img src="https://XYZYourCompanyWebsite.com/EmailHeader" width="650" border="0">
The image lives on a public (or externally reachable) web server or CDN, and the <img> tag just points to it by URL.
- The template body stays small and readable.
- Updating a logo or banner is a one-file swap on the server — no template edit, no code change, no deployment.
- It's what every commercial email platform does, and it's the most broadly compatible option across email clients (Outlook, Gmail, mobile mail apps).
- Multiple templates and multiple languages can all point to the same hosted asset, so you're not duplicating image data anywhere.
2. Base64-embedded images (fallback only)
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...">
The image is inlined directly into the HTML as an encoded string.
- Useful only if you have no hosting available or need the email to render with zero external calls.
- It significantly bloats the size of the message body — and therefore every single email sent, and the template record itself.
- Some corporate mail clients strip or block inline/Base64 images by default, which means the "offline-safe" benefit isn't even guaranteed.
- It makes the template painful to read or maintain in the editor — you're scrolling past a wall of encoded text to find the actual copy.
Bottom line: use online-hosted images unless you have a specific constraint (no hosting available, strict offline requirement, a security policy against external calls in email). Host them somewhere stable and versioned — a CDN, blob storage, or your public website — so the URL doesn't change or get reorganized later and silently break every template that references it.
Why Configuration-Driven Header & Footer Beats a One-Off Design
Here's the mindset shift that actually makes ERP emails look good long-term: stop treating the header and footer as part of "the email." Treat them as a reusable brand component, configured once and reused everywhere.
In practice, that means every notification template you build shares the same structure:
- A header block — banner image, logo, maybe a tagline — hosted and referenced by URL.
- A body block — the actual message, with
%Token%placeholders for whatever's dynamic (worker name, request type, dates). - A footer block — company name, contact email, address, social icons, and the legal/confidentiality notice.
The header and footer are identical across every template in your system — approval emails, violation notices, document-ready pings, onboarding notices — because they're pulling from the same hosted images and the same static markup. The only thing that changes template to template is the body block in the middle.
That gives you a few very practical wins:
- Rebrand once, update everywhere. Change the logo or banner image at its hosted URL, and every template — across every language — picks it up instantly. You're not hunting through a dozen
SysEmailMessageTablerecords to fix one outdated logo. - Design consistency without a designer on every ticket. Once the header/footer "shell" looks good, every new notification type automatically looks good too — a developer just adds the body copy for the new scenario, using the exact same shell.
- Governance is easier. A confidentiality notice, a P.O. Box, a compliance line — these usually shouldn't vary by template. Configuration-driven footers mean legal/compliance only has to sign off on that block once.
- New languages don't mean rebuilding the design. You're translating body copy and maybe the footer text — the visual shell (banner, icon layout, colors) doesn't need to be recreated per language.
Practically, this is exactly what SysEmailTable + SysEmailMessageTable gives you for free: a header (sender name/address, template metadata) plus a message body per language — and nothing stops you from keeping that body's header/footer markup identical across every template you create, changing only the middle section.
What This Looks Like in a Real Template
Here's a template structure showing hosted images used for a banner, a footer logo, and social icons — all just <img src="URL"> references, nothing embedded:
<body bgcolor="#ffffff">
<table width="650" cellpadding="0" cellspacing="0" border="0" align="center"><tr><td><img src="https://XYZYourCompanyWebsite.com/EmailHeader" width="650" border="0"></td></tr></table><table width="650" cellpadding="8" cellspacing="0" border="0" align="center"><tr><td><font face="Arial Unicode MS,Helvetica" size="3" color="#000000">Dear %WorkerName%,<br /><br />Your requested document is ready. Please collect it via the ERP system.<br /><br /><a href="https://XYZYourCompanyERP.com/?cmp=xyz&mi=NW_LeaveRequestWorkSpace">Track My Requests</a></font></td></tr></table><table width="650" cellpadding="0" cellspacing="0" border="0" align="center" bgcolor="#2f58a5"><tr><td align="center" style="padding-top:15px;"><font face="Arial Unicode MS,Helvetica" size="2" color="#ffffff"><b>XYZ Your Company Name</b><br><br><a href="mailto:notifications@xyzyourcompany.com"><font color="#ffffff">notifications@xyzyourcompany.com</font></a><br>P.O. Box 000, Your City, Your Country<br><br></font></tr><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0"><tr><td><a href="https://www.linkedin.com/company/xyzyourcompany/"><img src="https://XYZYourCompanyWebsite.com/LinkedInIcon" width="28" height="28" border="0"></a></td><td><a href="https://x.com/XYZYourCompany"><img src="https://XYZYourCompanyWebsite.com/TwitterIcon" width="28" height="28" border="0"></a></td><td><a href="https://www.facebook.com/XYZYourCompany/"><img src="https://XYZYourCompanyWebsite.com/FacebookIcon" width="28" height="28" border="0"></a></td><td><a href="https://www.instagram.com/xyz_your_company/"><img src="https://XYZYourCompanyWebsite.com/InstagramIcon" width="28" height="28" border="0"></a></td><td><a href="https://www.youtube.com/channel/XYZYourCompanyChannel"><img src="https://XYZYourCompanyWebsite.com/YouTubeIcon" width="28" height="28" border="0"></a></td></tr></table></td></tr><tr><td align="center"><img src="https://XYZYourCompanyWebsite.com/MyCompanyLogo" height="100" border="0"></td></tr></table><table width="650" cellpadding="8" cellspacing="0" border="0" align="center" bgcolor="#f2f2f2"><tr><td width="12"></td><td><font face="Arial Unicode MS,Helvetica" size="2" color="#666666"><b>XYZ YOUR COMPANY NAME</b><br><br>This electronic message contains information from XYZ Your Company Name. The contents may be privileged and confidential and are intended for the use of the intended addressee(s) only. If you are not an intended addressee, any disclosure, copying, distribution, or use of the contents of this message is prohibited. If you have received this e-mail in error, please notify the sender and delete the original message and any copies thereof.</font></td></tr></table>
</body>
Every <img src="..."> above points to a hosted URL — the header banner, all five social icons, and the footer logo. None of it is embedded. If marketing hands you a new logo tomorrow, you replace the file at that URL and every template using it updates automatically, in every language, with zero template edits.
⚠️ A Real Limitation: Keep the HTML Structure Exactly As-Is
This is the part people get burned on. When you preview the template in a regular browser or in the D365 email template editor, malformed or restructured HTML will often still render fine — browsers are forgiving, they'll silently fix unclosed tags, reflow nested tables, and normalize whitespace. That gives a false sense of confidence.
The problem is that the email is not sent through a browser — it's assembled by the D365 mailer/SMTP pipeline and then rendered by whatever email client the recipient uses (Outlook, Gmail, mobile mail apps, etc.). These renderers are far less forgiving than a browser preview:
- Nested
<table>layout is deliberate, not legacy sloppiness. Email clients (Outlook especially, since it uses the Word rendering engine) don't reliably support CSS flexbox/grid or even basic block-level layout. Table-based layout is still the only way to guarantee consistent image and text alignment across clients. - Don't "clean up" the markup. Reformatting the HTML — closing tags that were left open, converting
<font>tags to CSS classes, collapsing nested tables into<div>s — can visually match in a browser preview but break alignment once it's actually delivered and rendered by the mail client. - Keep header, body, and footer as separate sibling
<table>blocks, exactly as structured, rather than merging them into one table. Each block (banner image, message body, colored footer, confidentiality notice) is intentionally its own table so a quirk in one section doesn't cascade into the others. - Test in an actual mail client, not just the D365 preview or a browser tab, before publishing a template.
In short: the D365 email template editor and any browser preview will look correct even when the structure is technically broken — the framework and the eventual email client are what actually expose the problem. Preserve the structure exactly, and only swap out image URLs and copy.
Setting Up the Template in Organization Administration
D365 F&O already ships a form for this — no custom setup required:
- Go to Organization administration → Email → Email templates.
- Click New to create a template header record:
- Template Id — a unique identifier.
- Sender name / Sender email address — this is what recipients see as the "From."
- From the same form, open the Message action for a specific language and paste in the HTML body — this is where your hosted
<img src="...">references and%Token%placeholders go. - Repeat per language if you need multi-language support — each language gets its own body/subject row against the same Template Id.
- Save. That's it — the images render from their hosted URLs the moment the email is sent, and swapping any of them later never touches this form again.
Here's what the form looks like in practice
[reference screenshot: Organization email templates form]
A Better Email Architecture
A professional D365 F&O email can be thought of as four main components:
Dynamics 365 Finance & Operations
│
▼
Email Template
│
├── Header / Banner
│
├── Dynamic Email Body
│ └── %Token% placeholders
│
└── Footer / Branding
│
▼
Hosted Image URLs
│
▼
Email Client
Outlook / Gmail / MobileA Few Practical Tips for Hosted Images
- Use a stable, versioned path for hosted assets (CDN, blob storage, your public site) — don't point at something that could be reorganized or renamed later, since every template referencing it would break silently.
- Keep image dimensions explicit in the
<img>tag (width/height) — some clients won't reserve layout space for an image until it loads, and explicit dimensions prevent the rest of the email from jumping around. - Use HTTPS URLs — some clients block or flag mixed-content (HTTP) images in an otherwise secure email.
- If you're supporting multiple brands or languages from the same X++ send method, keep the image hosting consistent and let the template (not the code) decide which images/branding apply — that's a content change, not a logic change.
Getting the image strategy right early saves you from the two most common traps in D365 email templates: bloated Base64 bodies that break in half the mail clients you test, and hardcoded image paths buried in X++ that nobody remembers to update. Host the images, reference them by URL, keep the surrounding HTML structure untouched — and your ERP emails go from looking like a system alert to looking like something your brand actually put thought into.
Final Sample Output:
#Dynamics365 #D365FO #MicrosoftDynamics #ERPDevelopment #EmailTemplates #EmailDesign
.png)

Comments
Post a Comment