Table of Contents

Mail — CL.Mail

CL.Mail provides SMTP email sending and IMAP reading with a Handlebars-style template engine, HTML and plain-text support, and file attachments.


Registration

await Libraries.LoadAsync<CL.Mail.MailLibrary>();

Configuration (config.mail.json)

{
  "Smtp": {
    "Host": "smtp.example.com",
    "Port": 587,
    "UseSsl": true,
    "Username": "noreply@example.com",
    "Password": "secret",
    "FromAddress": "noreply@example.com",
    "FromName": "My Application"
  },
  "Imap": {
    "Host": "imap.example.com",
    "Port": 993,
    "UseSsl": true,
    "Username": "inbox@example.com",
    "Password": "secret"
  },
  "TemplatesPath": "templates/"
}

Sending Email

Simple HTML Email

var mail = context.GetLibrary<CL.Mail.MailLibrary>();

await mail.SendAsync(new EmailMessage
{
    To      = ["alice@example.com"],
    Subject = "Welcome to MyApp",
    HtmlBody = "<h1>Welcome!</h1><p>Your account is ready.</p>",
    TextBody = "Welcome! Your account is ready."
});

With CC, BCC, and Reply-To

await mail.SendAsync(new EmailMessage
{
    To       = ["alice@example.com"],
    Cc       = ["manager@example.com"],
    Bcc      = ["audit@example.com"],
    ReplyTo  = "support@example.com",
    Subject  = "Order Confirmation #12345",
    HtmlBody = "<p>Your order has been confirmed.</p>"
});

With Attachments

await mail.SendAsync(new EmailMessage
{
    To      = ["alice@example.com"],
    Subject = "Your Invoice",
    HtmlBody = "<p>Please find your invoice attached.</p>",
    Attachments =
    [
        new EmailAttachment
        {
            FileName    = "invoice-2026-04.pdf",
            ContentType = "application/pdf",
            Data        = await File.ReadAllBytesAsync("invoices/inv-2026-04.pdf")
        }
    ]
});

Template Engine

CL.Mail includes a Handlebars-style template engine. Templates are HTML files with {{variable}} placeholders.

Template File

Place templates in the library's templates/ directory:

CodeLogic/Libraries/CL.Mail/templates/
  welcome.html
  order-confirmation.html
  password-reset.html
<!-- templates/welcome.html -->
<!DOCTYPE html>
<html>
<body>
  <h1>Welcome, {{name}}!</h1>
  <p>Your account was created on {{date}}.</p>
  <p>Click <a href="{{activationUrl}}">here</a> to activate your account.</p>
  {{#if isPremium}}
  <p>You have <strong>premium</strong> access.</p>
  {{/if}}
</body>
</html>

Using Templates

var body = await mail.RenderTemplateAsync("welcome", new
{
    name          = "Alice",
    date          = DateTime.UtcNow.ToString("MMMM d, yyyy"),
    activationUrl = "https://myapp.com/activate?token=abc123",
    isPremium     = true
});

await mail.SendAsync(new EmailMessage
{
    To      = ["alice@example.com"],
    Subject = "Welcome to MyApp",
    HtmlBody = body
});

Convenience Method

await mail.SendTemplateAsync(
    to:       ["alice@example.com"],
    subject:  "Welcome to MyApp",
    template: "welcome",
    model:    new { name = "Alice", activationUrl = "https://..." }
);

Reading Email (IMAP)

var messages = await mail.ReadInboxAsync(maxMessages: 20);

foreach (var msg in messages)
{
    Console.WriteLine($"From: {msg.From}");
    Console.WriteLine($"Subject: {msg.Subject}");
    Console.WriteLine($"Body: {msg.TextBody}");

    if (msg.Subject.StartsWith("ALERT:"))
    {
        await ProcessAlertAsync(msg);
        await mail.MarkAsReadAsync(msg.Id);
    }
}

Filtering

// Unread messages from a specific sender
var support = await mail.ReadInboxAsync(new ImapFilter
{
    Unread     = true,
    From       = "client@bigcorp.com",
    MaxResults = 50
});

Health Check

// Returns Healthy if SMTP connection can be established
// Returns Degraded if SMTP is reachable but latency is high
// Returns Unhealthy if SMTP connection fails
var status = await mail.HealthCheckAsync();