DevLearningTools

MODULE 13 · LESSON 07

<cfmail>

cfmail in depth: its full attribute set, cfmailparam for attachments and custom headers, cfmailpart for multipart messages, a real query-driven mail merge, and real engine differences from Lucee's mail tag.

New lessons are added one at a time as the course gets built out — a graded quiz for each lesson is still on the way.

The previous lesson covered how a message actually gets delivered. This lesson covers cfmail itself: its full attribute set, cfmailparam and cfmailpart for attachments and multipart messages, and a real query-driven mail merge.

Learning Objectives

After completing this lesson, you'll be able to:

  • Use cfmail's core attributes for recipients, formatting, and delivery.
  • Attach a file and set a custom header with cfmailparam, and send a multipart message with cfmailpart.
  • Send a personalized message per row of a query with cfmail's query/group attributes.
  • Recognize where Lucee's mail tag genuinely differs from Adobe's cfmail.

cfmail's Core Attributes

AttributeMeaning
from / to / cc / bcc / replyToSender and recipient addresses (comma-delimited for multiple)
subjectThe message subject line
typetext/plain (default) or text/html
charsetCharacter encoding, defaults to utf-8
server / port / username / passwordOverrides the Administrator's SMTP configuration for this message
useSSL / useTLSSecure connection options
priority1–5, or highest/high/normal/low/lowest
failToAddress notified on a delivery failure
spoolEnableWhether to spool (queue) the message rather than send it immediately
mimeAttachA file path to attach, for a single simple attachment

A Real Example: HTML Email With an Attachment and a Custom Header

Tag Syntax
<cfmail to="#customer.email#" from="orders@myshop.com" subject="Your Invoice" type="html">
    <cfmailparam name="X-Order-Id" value="#order.id#">
    <cfmailparam file="#invoicePath#" type="application/pdf">

    <p>Hi #customer.firstName#, your invoice is attached.</p>
</cfmail>
NOTE

cfmailparam does two different jobs depending on its attributes: name/value sets a custom header, file (with an optional type) attaches a file.

A Real Example: cfmailpart for a Plain-Text Fallback

Tag Syntax
<cfmail to="#customer.email#" from="orders@myshop.com" subject="Your Invoice">
    <cfmailpart type="text">
        Hi #customer.firstName#, your invoice is attached. View it online at #invoiceUrl#.
    </cfmailpart>
    <cfmailpart type="html">
        <p>Hi #customer.firstName#, your invoice is attached. <a href="#invoiceUrl#">View it online</a>.</p>
    </cfmailpart>
</cfmail>
NOTE

When cfmailpart is used, all of the message's content must be inside one cfmailpart or another, a mail client that can't render HTML falls back to the text part automatically.

A Real Example: A Query-Driven Mail Merge

cfmail's query attribute sends one personalized message per row, looping the tag body the way cfoutput would loop over the same query.

Tag Syntax
<cfmail to="#email#" from="billing@myshop.com" subject="Payment Reminder" query="overdueAccounts">
    Dear #firstName#,

    Your account balance of #dollarFormat(balanceDue)# is overdue.
</cfmail>
NOTE

group (with groupCaseSensitive) sends one message per group of rows sharing a column value, instead of one per row, useful when a single customer has several line items that all belong in the same email.

Digital Signing and Encryption

cfmail also supports S/MIME digital signing (sign, keystore, keyAlias, keyPassword) and encryption (encrypt, recipientCert, encryptionAlgorithm, with DES_EDE3_CBC, RC2_CBC, and AES128/192/256_CBC available). These are real attributes for a genuine compliance or security requirement, not something most applications need to reach for by default.

Real Engine Differences: Lucee's mail Tag

AspectAdobe cfmailLucee mail
Query-driven loopingquery, group, groupCaseSensitive, startRow, maxRows all supportedNot implemented at all, query, group, groupCaseSensitive, startRow, and maxRows have no effect
Async/spoolingspoolEnableasync (spoolEnable is deprecated), sent via Lucee's Task Manager with automatic retry logic
Scheduled deliveryNot availablesendTime schedules a message for future delivery through the spooler
Proxy supportNot availableproxyServer/proxyPort/proxyUser/proxyPassword for sending through a proxy
PackagingBuilt inSince Lucee 7.1, mail functionality moved to a separate Mail extension (included in the full JAR distribution)
NOTE

The missing query/group support is a real, practical migration gotcha, a cfmail-based mail merge built on Adobe ColdFusion needs to be rewritten as an explicit loop on Lucee.

A Real Lucee Gotcha: TLS Version Restrictions

NOTE

Lucee respects the JDK's own TLS restrictions. On Java 11.0.11+, 17+, and 21+, that means only TLSv1.2 and TLSv1.3 are available by default, an SMTP server that only offers an older protocol needs the mail.smtp.ssl.protocols system property overridden explicitly.

Common Beginner Mistakes

Confusing cfmailparam and cfmailpart

cfmailparam attaches a file or sets a custom header. cfmailpart provides an alternate format (plain text vs HTML) of the same message. They solve different problems.

Mixing content outside of cfmailpart when cfmailpart is used at all

Once any cfmailpart is present, all of the message's content needs to be inside one part or another, content outside them isn't included correctly.

Porting a query/group-based mail merge to Lucee unchanged

Lucee doesn't implement query, group, groupCaseSensitive, startRow, or maxRows on its mail tag at all, that logic needs to become an explicit loop instead.

Assuming an older SMTP server's TLS version will just work on newer Lucee/Java versions

Java 11.0.11+, 17+, and 21+ restrict TLS to 1.2/1.3 by default, an older protocol needs the mail.smtp.ssl.protocols system property overridden explicitly.

Best Practices

  • Use cfmailpart for a real HTML/plain-text fallback rather than relying on every recipient's client rendering HTML.
  • Reach for cfmailparam's file attribute for attachments rather than mimeAttach when more than one attachment or a custom header is needed.
  • Check for Lucee's unimplemented query/group support before porting an Adobe-based mail merge.
  • Reserve digital signing/encryption attributes for an actual compliance requirement, they add real operational complexity (keystores, certificates).

Interview Questions

What's the difference between cfmailparam and cfmailpart?

cfmailparam attaches a file or adds a custom header. cfmailpart provides an alternate content format (like plain text alongside HTML) of the same message body.

How would you send one personalized email per row of a query on Adobe ColdFusion, and why wouldn't the same approach work unchanged on Lucee?

cfmail's query attribute (with group for grouping related rows) loops the tag body once per row or group. Lucee doesn't implement query, group, groupCaseSensitive, startRow, or maxRows at all, that logic has to become an explicit loop instead.

What replaced spoolEnable on Lucee, and what does it add?

async, which sends through Lucee's Task Manager with automatic retry logic, spoolEnable is deprecated on Lucee in favor of it.

Why might an email to an older SMTP server suddenly fail after a Java upgrade?

Java 11.0.11+, 17+, and 21+ restrict TLS to versions 1.2/1.3 by default, an SMTP server offering only an older protocol needs mail.smtp.ssl.protocols overridden explicitly to keep working.

Summary

In this lesson, you used cfmail's core attributes, attached a file and set a custom header with cfmailparam, sent a multipart message with cfmailpart, built a real query-driven mail merge, and covered where Lucee's mail tag genuinely differs, its missing query/group support, async replacing spoolEnable, sendTime scheduling, and a real TLS version gotcha on newer Java.

What's Next?

The next lesson covers cfimap, reading and managing mail on an IMAP server directly from CFML.