Create nice HTML emails in AL

In this video, I show how to send a nicely formatted email from AL in just 10 lines of codes. And even giving the customer a way to customize the email afterwards:

https://youtu.be/zrJXze8zwT0


In this video, Erik demonstrates how to create nicely formatted HTML emails directly from AL code in Business Central. The key technique involves using a Word layout report, saving it as HTML, and then feeding that HTML into the SMTP Mail codeunit. This approach gives end users the ability to customize email templates through the standard Custom Report Layouts feature — no developer intervention needed for layout changes.

The Core Idea: Word Layout → HTML → Email

Instead of manually building HTML strings in AL code (which is tedious and hard to maintain), Erik’s approach leverages something Business Central already does well: Word report layouts with merge fields. The trick is that Report.SaveAs supports saving a report as HTML format. So you design your email in Word, let the report engine merge the data, export it as HTML, and pass that HTML as the email body.

Setting Up the Report

First, you need a report with a Word layout that will serve as your email template. Erik creates a simple report (50147) running on the Customer table with a Word default layout:

report 50147 "Customer Email"
{
    DefaultLayout = Word;
    WordLayout = 'CustomerEmail.docx';
    dataset
    {
        dataitem(Customer; Customer)
        {
            column(Name; Name)
            {
            }
            column(City; City)
            {
            }
            column(HomePage; "Home Page")
            {
            }
            // ... additional columns as needed
        }
    }
}

The report was initially generated using the AL File Wizard in Visual Studio Code. After creating the report, a quick compile generates the CustomerEmail.docx template file which you can then edit in Word.

Designing the Word Template

To edit the Word layout and insert merge fields:

  1. Open the generated .docx file in Microsoft Word
  2. Enable the Developer tab (right-click the ribbon → Customize the Ribbon → check Developer)
  3. Open the XML Mapping Pane from the Developer tab
  4. Find your report’s data source in the XML mapping pane
  5. Insert fields as plain text content controls by right-clicking the field names

Erik creates a simple template with text like “Hello [Name], you live in [City]” and applies some bold formatting to demonstrate that Word formatting carries through to the HTML output.

The Page Extension: Sending the Email

The email sending logic lives in a page extension on the Customer List with a single action button:

pageextension 50147 "Customer List Email" extends "Customer List"
{
    actions
    {
        addfirst(processing)
        {
            action(SendMail)
            {
                Caption = 'Send Mail';
                ApplicationArea = all;
                trigger OnAction()
                var
                    OutS: OutStream;
                    InS: InStream;
                    B: Codeunit "Temp Blob";
                    Ref: RecordRef;
                    FRef: FieldRef;
                    Mail: Codeunit "SMTP Mail";
                    Recipients: List of [Text];
                    Body: Text;
                begin
                    B.CreateOutStream(OutS);
                    Ref.OPEN(DATABASE::Customer);
                    Fref := Ref.Field(1);
                    Fref.Setrange(Rec."No.");
                    Report.SaveAs(50147, '', ReportFormat::Html, OutS, Ref);
                    B.CreateInStream(InS);
                    InS.ReadText(Body);
                    Body := Body.Replace('%%%%HOMEPAGE%%%%',
                        '<a href="https://' + rec."Home Page" + '">Homepage</a>');
                    Recipients.add('erik@hougaard.com');
                    Mail.CreateMessage('Erik Hougaard', 'erik@hougaard.com',
                        Recipients, 'Mail from BC', Body, true);
                    Mail.Send();
                end;
            }
        }
    }
}

Step-by-Step Breakdown

1. Prepare the Output Stream

The Temp Blob codeunit acts as an in-memory buffer. You create an OutStream from it, which is where the report’s HTML output will be written:

B.CreateOutStream(OutS);

2. Set Up the Record Filter

Reports need filters, not a single record passed in. You use a RecordRef and FieldRef to dynamically set the filter to the current customer:

Ref.OPEN(DATABASE::Customer);
Fref := Ref.Field(1);
Fref.Setrange(Rec."No.");

Note the use of DATABASE::Customer instead of the raw table number 18 — much more readable and maintainable.

3. Save the Report as HTML

This is the key line. Report.SaveAs renders the Word layout report and converts it to HTML, writing the result into the OutStream (and thus into the Temp Blob):

Report.SaveAs(50147, '', ReportFormat::Html, OutS, Ref);

4. Read the HTML into a Text Variable

Now switch directions — create an InStream from the same Temp Blob and read the HTML content into a text variable:

B.CreateInStream(InS);
InS.ReadText(Body);

If the stream direction terminology is confusing (OutStream to write into the blob, InStream to read from the blob), Erik has a separate video covering the quirks of in/out/read/write naming conventions.

5. Send the Email

Finally, use the SMTP Mail codeunit to compose and send the message. The last parameter (true) tells it the body is HTML-formatted:

Recipients.add('erik@hougaard.com');
Mail.CreateMessage('Erik Hougaard', 'erik@hougaard.com',
    Recipients, 'Mail from BC', Body, true);
Mail.Send();

Bonus: Dynamic Hyperlinks

One limitation Erik discovered is that you can’t insert dynamic hyperlinks through Word merge fields — Word content controls don’t support building links from data. The workaround is simple: use a placeholder string in your Word template and replace it with actual HTML after the report renders:

Body := Body.Replace('%%%%HOMEPAGE%%%%',
    '<a href="https://' + rec."Home Page" + '">Homepage</a>');

In the Word template, you simply type the placeholder text %%%%HOMEPAGE%%%% where you want the link to appear. Since the body is already HTML at this point, you can inject any valid HTML markup through string replacement.

End User Customization

One of the biggest advantages of this approach is that end users can customize the email template without any code changes. They simply go to Custom Report Layouts in Business Central, find the report, and edit the Word layout. They can add their own logos, change formatting, rearrange content — all through the familiar Word interface.

Important: Error Handling

Erik points out that in the demo, error handling is intentionally omitted for simplicity, but in production code you should handle it properly:

  • Report.SaveAs returns a Boolean — if the layout needs updating, it may prompt the user, and without an if check it can fail silently
  • Mail.CreateMessage and Mail.Send can also fail and should be wrapped in proper error handling
  • This is especially important in batch scenarios where you don’t want one failed email to stop the entire batch from processing

Summary

In roughly 10 lines of actual logic code, you can send beautifully formatted HTML emails from Business Central. The technique chains together three things the platform already provides: Word report layouts for design, Report.SaveAs with ReportFormat::Html for conversion, and the SMTP Mail codeunit for delivery. The result is a maintainable, customizable email solution where developers define the data and end users control the presentation — all without hand-crafting HTML strings in AL code.