In version 17, the old and much beloved SMTP Mail codeunit is being marked obsolete. In this video, I’ll show how to replace the obsoleted code with fresh new functionality.

In this video, Erik walks through how to update your AL email code in Business Central when the familiar SMTP Mail codeunit becomes obsolete. Microsoft is retiring the old SMTP-based email functionality and replacing it with a new, more capable email framework. Erik demonstrates the migration step by step, showing how to replace the old code with the new Email Message and Email codeunits.
The Problem: Obsolete SMTP Mail Code
If you’ve been working with AL extensions in Business Central, you may have noticed Visual Studio Code suddenly showing warnings about obsolete code. Specifically, starting with platform version 17.0, Microsoft has marked the SMTP mail functionality for removal. When you compile your project, you’ll see warnings like:
- Table “SMTP Mail Setup” is marked for removal — “Move to Email SMTP app”
- Codeunit “SMTP Mail” is marked for removal — “Replaced with Email Message codeunit from System Application”
The obsolete messages themselves have some quirks (including what Erik noted as a typo or two), but the intent is clear: stop using the old SMTP codeunit and switch to the new email framework.
The Old Way: Using SMTP Mail
Here’s the legacy approach that many extensions use. It relies on the SMTP Mail Setup record and the SMTP Mail codeunit to compose and send emails:
procedure sendemail()
var
SMTPSetup: Record "SMTP Mail Setup";
mail: Codeunit "SMTP Mail";
TempBlob: Codeunit "Temp Blob";
attach1: InStream;
addressTo: List of [Text];
subjectTxt: Text;
begin
SMTPSetup.get();
SMTPSetup.TestField("User ID");
addressTo.Add('homey@home.com');
addressTo.Add('demo@example.com');
subjectTxt := 'Important message inside, please open!';
mail.CreateMessage('CEO of Bank of Spain', SMTPSetup."User ID", addressTo, subjectTxt, '');
mailBody(mail);
Clear(TempBlob);
SaveDocumentAsPDFToStream(TempBlob, 50140);
TempBlob.CreateInStream(attach1);
mail.AddAttachmentStream(attach1, 'a cheque for you.pdf');
mail.Send();
end;
This pattern is straightforward: create a message with recipients, subject, and body; optionally add attachments from streams; then call Send(). The SMTP Mail Setup record provides the sender’s user ID and SMTP server configuration.
The New Way: Email Message + Email Codeunits
The key insight is that the old single SMTP Mail codeunit has been split into two separate codeunits:
Email Message— responsible for composing the email (recipients, subject, body, attachments)Email— responsible for sending the email (and other actions like saving as draft, opening in editor, enqueueing)
Here’s the updated code:
procedure NewSendMail()
var
mail: Codeunit "Email Message";
email: Codeunit Email;
InS: InStream;
base64: Codeunit "Base64 Convert";
begin
mail.Create('hello@example.com', 'The is the subject', 'here''s the body');
mail.AddAttachment('open this.pdf', 'application/pdf', base64.ToBase64(InS));
email.Send(mail);
end;
Step-by-Step Breakdown
1. Create the Message
The Email Message codeunit has a Create method that takes three parameters: recipients, subject, and body. This replaces the old CreateMessage call:
mail.Create('hello@example.com', 'The is the subject', 'here''s the body');
2. Add Attachments
The AddAttachment method on the new codeunit takes a filename, a content type, and a Base64-encoded string (not a stream directly). If you have your report saved to an InStream, you need to convert it using the Base64 Convert codeunit:
mail.AddAttachment('open this.pdf', 'application/pdf', base64.ToBase64(InS));
This is a notable difference from the old approach where you could pass a stream directly via AddAttachmentStream. Now you need the extra Base64 encoding step.
3. Send the Email
The Email codeunit handles the actual sending. You simply pass your composed Email Message to the Send method:
email.Send(mail);
The Email codeunit also exposes other useful methods that go beyond what SMTP could offer:
- Enqueue — queue the email for later sending
- SaveAsDraft — save the email as a draft
- OpenInEditor / OpenInEditorModally — open the email in an editor for the user to review before sending
Why the Change? SMTP vs. Higher-Level Email
Erik explains the architectural reasoning behind this change well: SMTP is purely a transport protocol. It moves the body of an email from point A to point B — that’s it. SMTP has no concept of sent folders, drafts, or any of the features we associate with modern email clients.
With Business Central’s deeper integration into Office 365, Microsoft wants to work at a higher abstraction level. The new email framework is aware of concepts like drafts, sent folders, and email accounts configured through different connectors (SMTP being just one option). This means you can configure email accounts from the “Email Accounts” page in Business Central, and your code doesn’t need to care whether the underlying transport is SMTP, Microsoft Graph API, or something else entirely.
Codeunits as Objects
Erik also points out an interesting pattern that’s becoming more common in Business Central AL development: using codeunits as variables that carry state. In the old NAV world, codeunits were rarely used this way. But in the new email framework, the Email Message codeunit is essentially used as an object — you create it, populate it with data (recipients, body, attachments), and then pass it to the Email codeunit for sending. This is the closest thing AL has to object-oriented patterns, and it’s a paradigm worth getting comfortable with.
The Complete Source File
Here’s the full source code showing both the old (obsolete) approach and the new approach side by side:
pageextension 50100 CustomerListExt extends "Customer List"
{
procedure CalcAvgUnitCost(Item: Record Item; Location: Record Location): decimal
var
ILE: Record "Item Ledger Entry";
Q: Decimal;
Total: Decimal;
begin
ILE.setrange("Item No.", Item."No.");
ILE.setrange("Location Code", Location.Code);
ILE.setrange(Open, true);
ILE.SetCurrentKey("Item No.", "Location Code", Open);
if ILE.findset() then
repeat
ILE.CalcFields("Cost Amount (Actual)", "Cost Amount (Expected)");
if ILE."Cost Amount (Actual)" <> 0 then begin
Total += ILE."Cost Amount (Actual)" / ILE.Quantity * ILE."Remaining Quantity";
Q += ILE."Remaining Quantity";
end else
if ILE."Cost Amount (Expected)" <> 0 then begin
Total += ILE."Cost Amount (Expected)" / ILE.Quantity * ILE."Remaining Quantity";
Q += ILE."Remaining Quantity";
end;
until ILE.Next() = 0;
if Q <> 0 then
exit(Total / Q);
end;
// NEW approach - no obsolete warnings
procedure NewSendMail()
var
mail: Codeunit "Email Message";
email: Codeunit Email;
InS: InStream;
base64: Codeunit "Base64 Convert";
begin
mail.Create('hello@example.com', 'The is the subject', 'here''s the body');
mail.AddAttachment('open this.pdf', 'application/pdf', base64.ToBase64(InS));
email.Send(mail);
end;
// OLD approach - generates obsolete warnings
procedure sendemail()
var
SMTPSetup: Record "SMTP Mail Setup";
mail: Codeunit "SMTP Mail";
TempBlob: Codeunit "Temp Blob";
attach1: InStream;
addressTo: List of [Text];
subjectTxt: Text;
begin
SMTPSetup.get();
SMTPSetup.TestField("User ID");
addressTo.Add('homey@home.com');
addressTo.Add('demo@example.com');
subjectTxt := 'Important message inside, please open!';
mail.CreateMessage('CEO of Bank of Spain', SMTPSetup."User ID", addressTo, subjectTxt, '');
mailBody(mail);
Clear(TempBlob);
SaveDocumentAsPDFToStream(TempBlob, 50140);
TempBlob.CreateInStream(attach1);
mail.AddAttachmentStream(attach1, 'a cheque for you.pdf');
mail.Send();
end;
procedure mailBody(var mail: Codeunit "SMTP Mail"): Text
begin
mail.AppendBody('I am Santiago Sebastian, a Spanish');
mail.AppendBody('politician/investor and former Senior');
mail.AppendBody('Director with Bank of Spain, National Central');
mail.AppendBody('Bank of Spain. I am interested in investing the');
mail.AppendBody('sum of US$12.7Million in your Country or any');
mail.AppendBody('other place in the world under your custody.');
mail.AppendBody('Please if you are interested in this project,');
mail.AppendBody('kindly reply me and lets move forward.');
end;
procedure SaveDocumentAsPDFToStream(var TempBlob: Codeunit "Temp Blob"; ReportID: integer): Boolean;
var
DocumentRef: RecordRef;
VarOutStream: OutStream;
begin
DocumentRef.Open(18);
TempBlob.CreateOutStream(VarOutStream);
if Report.SaveAs(ReportID, '', ReportFormat::Pdf, VarOutStream, DocumentRef) then
exit(true)
else
Error('Could not save Report: %1 as PDF attachment', ReportID);
end;
}
Summary
Migrating from the obsolete SMTP Mail codeunit to the new email framework in Business Central is straightforward once you understand the pattern:
- Replace
Codeunit "SMTP Mail"with two codeunits:Codeunit "Email Message"for composing andCodeunit Emailfor sending. - Use
Email Message.Create()instead ofSMTP Mail.CreateMessage(). - Use
Email Message.AddAttachment()with Base64-encoded content instead ofSMTP Mail.AddAttachmentStream(). - Use
Email.Send()and pass the message codeunit as a parameter. - You no longer need the
SMTP Mail Setuprecord — email accounts are now configured through the Email Accounts page in Business Central.
The SMTP functionality was obsoleted in version 17, so it will be removed in a future release. The sooner you migrate your email code, the better prepared you’ll be. The new framework also opens up capabilities beyond basic SMTP — including draft management, editor integration, and support for multiple email connectors.