Mail Sender API

Last modified by Admin on 2024/06/15 01:19

cogAPI to send emails
Developed by

XWiki Development Team

0 Votes
LicenseGNU Lesser General Public License 2.1
Bundled With

XWiki Standard


First introduced in XWiki 6.1M2 and heavily modified in versions 6.4M3, 6.4.2, 7.0M2, 6.4.4, 7.1M1, 6.4.5, 7.1M2 and 7.1RC1.

Installable with the Extension Manager


This API is replacing the older Mail Sender Plugin.

This API allows to:

  • Ability to send Multipart emails with any type of mime type content (html, text, vcalendar, etc)
  • Ability to embed images in HTML emails
  • Ability to send mail templates
  • Ability to send mails to list of users, a list of groups and a list of emails (with ability to exclude users, groups and emails), ensuring that recipients don't get duplicate emails
  • Scripting API to make it easy to send mails from wiki pages
  • Asynchronous email sending
  • Support for sending large volume of emails

There's also a Mail Application that provides an Administration UI for configuring parameters and to see the statuses of sent mails (when using the database Mail Listener - see below).

See also the Mail Sender Storage module which extends this API.

RFC Compliance

The XWiki Mail API uses the Jakarta Mail API as its implementation, and thus supports the following RFCs:


Most API examples below are written in Velocity. If you're using this module from Java make sure to check the Java API Example below.

The way to access the mail sender API from script has changed in XWiki 12.4RC1. Before that version you had to use $services.mailsender, where the new way is $services.mail.sender.

Create a Message

// Message creation, internally creates a JavaMail MimeMessage
// Note that "from" is optional and if not specified, taken from Mail configuration, in the following order:
// - first a ##from## xproperty is looked for in a ##XWiki.SendMailConfigClass## xobject in the ##XWiki.MailConfig## page in the current wiki
// - if not found, an ##admin_email## xproperty is looked for in the ##WebPreferences## page for the current space
// - if not found, an ##admin_email## xproperty is looked for in the ##XWiki.XWikiPreferences## page
// - if not found, a ##mail.sender.from## configuration property is looked for in
// - if not found, no from will be set and the sending will fail
$message = $services.mail.sender.createMessage()
$message = $services.mail.sender.createMessage(to, subject)
$message = $services.mail.sender.createMessage(from, to, subject)

Add Content

The first parameter is a Mime Type and it corresponds to a Component Hint for a MimeBodyPartFactory

  • Add Simple text:
    $message.addPart("text/plain", "text message")
  • Add simple text to message with a mail header:
    $message.addPart("text", "text message", {"headers" : { "Content-Transfer-Encoding" : "quoted-printable"}})
  • Add simple HTML to message:
    $message.addPart("text/html", "html message")
  • Add HTML + alternate text to message:
    $message.addPart("text/html", "html message", {"alternate" : "text message"})
  • Add HTML + alternate text + embedded images + some attachments to message (Note: $attachments is of type List<Attachment> here).
    $message.addPart("text/html", "html message", {"alternate" : "text message", "attachments" : $attachments})

    Where $attachments is of type List<com.xpn.xwiki.api.Attachment>. Example to get an attachment:

    #set ($attachment = $xwiki.getDocument('reference here').getAttachment('attachment name'))
    #set ($attachments = [$attachment])
  • Add HTML + alternate text from a Template Document containing a XWiki.Mail object. Any $var1 Velocity variable is replaced with value1.
    $message.addPart("xwiki/template", $documentReference, {"velocityVariables" : { "var1" : "value1" }})
  • Same as previous addPart() example but also add internationalization support by retrieving the XWiki.Mail Object which has a language property equals to fr.
    $message.addPart("xwiki/template", $documentReference, {"language" : "fr", "velocityVariables" : { "var1" : "value1" }})
  • Add HTML + alternate text + embedded images + some attachments, from a Template Document containing a XWiki.Mail object. Any $var1 Velocity variable is replaced with value1. (Note: $attachments is of type List<Attachment> here).
    $message.addPart("xwiki/template", $documentReference, {"velocityVariables" : { "var1" : "value1" }, "attachments" : $attachments})
  • Same as previous addPart() example but includes all attachments found in the Mail Template document instead of controlling precisely which attachments to send. Note that if you also pass "attachments" : $attachments it'll not include by default the attachments found in the template.
    $message.addPart("xwiki/template", $documentReference, {"includeTemplateAttachments" : true, "velocityVariables" : { "var1" : "value1" })

Note: addPart() returns a BodyPart object which can be used to set/get headers.

Send the Messages

  • Send a single Message, synchronously (it'll block till the mail is sent or it fails), storing the result in memory:
    #set ($mailResult = $services.mail.sender.send($message))
  • Send N messages, synchronously, storing the result in memory:
    #set ($mailResult = $services.mail.sender.send([$message1, $message2, ...]))

    // Equivalent to:
    #set ($mailResult = $services.mail.sender.send([$message1, $message2, ...], 'memory'))
  • Send N message, asynchronously, storing the result in memory:
    #set ($mailResult = $services.mail.sender.sendAsynchronously([$message1, $message2, ...], 'memory'))
  • Send N messages, asynchronously, storing the results in the database. It can then be retrieved later on. The Mail Application has an Admin screen which lists the statuses of all mails sent like this. Note that this very useful especially when sending large volume of emails, to see which mails have succeeded and which mails have failed to be sent.
    #set ($mailResult = $services.mail.sender.sendAsynchronously([$message1, $message2, ...], 'database'))

Set the Type

Sets the type of email that is being sent. This allows (for example) to filter these emails in the Mail Sender Status Admin UI (when using a Database Mail Listener). Example of types: "Watchlist", "Reset Password", "Send Page by Mail", etc.

$message.setType("Some type")

Check Message Statuses

When mails are sent asynchronously, it's possible to check the status of the sending process by calling:

// Returns true when the process is over for the batch (when all mails have been sent or have failed to be sent)

// Wait 10 seconds till the mails are sent (the passed timeout is expressed in milliseconds)

Check for Errors

  • Checking for errors that can occur before the mails have been processed:
    #if ($services.mail.sender.lastError)

    This can happen for example under the following conditions:

    • If an error happened when creating the message(s) (when using the $services.mail.sender.createMessage(...) APIs)
    • If there isn't enough permission to send mail (for example if the page containing the sending script doesn't have Programming Rights)
    • If the Mail Listener referenced by the second parameter of $services.mail.sender.send(messages, mailListenerHint) doesn't exist
  • Checking for mails sent successfully:
    #set ($mailStatuses = $mailResult.statusResult.getByState('SENT'))
    #foreach ($mailStatus in $mailStatuses)
      * Mail ($mailStatus.messageId) - Date Sent: $
  • Checking for all mail statuses:
    #set ($mailStatuses = $mailResult.statusResult.getAll())
    #foreach ($mailStatus in $mailStatuses)
      * Mail ($mailStatus.messageId) - Date Sent: $ State: $mailStatus.state - Error: $mailStatus.errorSummary
    • Checking for mails that have failed to be sent:
      #set ($mailStatuses = $mailResult.statusResult.getByState('SEND_ERROR'))
      #foreach ($mailStatus in $mailStatuses)


    • Checking for mails that have failed to be prepared:
      #set ($mailStatuses = $mailResult.statusResult.getByState('PREPARE_ERROR'))
      #foreach ($mailStatus in $mailStatuses)


    • Checking for all mail statuses in error:
      #set ($mailStatuses = $mailResult.statusResult.getAllErrors())
      #foreach ($mailStatus in $mailStatuses)
        * Mail ($mailStatus.messageId) - Date Sent: $ State: $mailStatus.state - Error: $mailStatus.errorSummary

    Checking statuses using getAll(), getAllErrors() or getByState() is not scalable and should be used only when a small number of mails are sent at once. If you're sending a large number of mails, you should always use the database Mail Listener and you should retrieve results using the Storage Script Service (see below).

    Accessing Configuration

    Access the Mail Sending configuration. In this example we define a default from email address if no from is defined on the configuration:

    #set ($from = $services.mail.sender.configuration.fromAddress)
    #if ("$!from" == '')
     #set ($from = "no-reply@${request.serverName}")

    MimeBodyPartFactory Implementations

    When adding a body part using the addPart(mimeType, source, ...) script API, the following logic is used:

    • Look for a Component implementing MimeBodyPartFactory and using the passed mimeType as a Hint.
    • If no Component is found and the source is a String then defaults to using the default MimeBodyPartFactory implementation
    • Otherwise throws an exception.

    The following implementations are available:

    • default: Creates a text Message Body Part.
    • text/html: Creates an HTML BodyPart that supports a text alternative and a list of attachments that will be added to the mail as standard attachments and also as embedded images if they are referenced in the passed HTML using the format <img src="cid:(attachment name)"/>.
    • xwiki/attachment: Creates an attachment Body Part from an Attachment object.
    • xwiki/template: Creates an Body Part from a Document Reference pointing to a Document containing an XWiki.Mail XObject (the first one found is used). Note that when evaluating Velocity in Mail Templates, the Execution Context used is a clone of the one that was available when the send*(...) method was called. Thus all Velocity bindings that were present are available from your Mail Template (request, xwiki, services, Velocity Tools, etc).

    Specialized Message Factories

    There are also specialized Message factories that can be used to create pre-filled Message objects. For example it's possible to create a message having its subject automatically computed from a template (i.e. from a wiki page having a XWiki.Mail object), by evaluating its subject xproperty with Velocity (see the example further below for more details) + having a template body part added too. Generic API:

    #set ($message = $services.mail.sender.createMessage(hint, source, parameters))
    #set ($messages = $services.mail.sender.createMessages(hint, source, parameters))


    • hint is the Component hint of the component implementing the org.xwiki.mail.MimeMessageFactory role.
    • source depends on the hint used. For example when the hint is template, the source represents the Document Reference to a page containing an XWiki.Mail object.
    • parameters depends on the hint used too. For example when the hint is template, it's used to pass Velocity variables and values to use when evaluating the subject xproperty of the XWiki.Mail object.

    Check the examples below to learn more.

    Using from Java

    Spirit: The JavaMail API should be used and XWiki only provides some helper components to make it simpler to use.


    @Inject MailSenderConfiguration configuration;
    @Inject @Named("text/html") MimeBodyPartFactory htmlPartFactory;
    @Inject MailSender mailSender;
    @Inject @Named("database") MailListener databaseMailListener;

    // Step 1: Create a JavaMail Session
    //... with authentication:
    Session session = Session.getInstance(configuration.getAllProperties(), new XWikiAuthenticator(configuration));
    //... without authentication:
    Session session = Session.getInstance(configuration.getAllProperties());

    // Step 2: Create the Message to send
    MimeMessage message = new MimeMessage(session);
    message.addRecipient(MimeMessage.RecipientType.TO, new InternetAddress("[email protected]"));

    // Step 3: Add the Message Body
    Multipart multipart = new MimeMultipart("mixed");
    // Add HTML in the body, with a text alternative and attachments
    Map<String, Object> parameters = new HashMap<>();
    parameters.put("alternative", "text");
    parameters.put("attachments", attachments);
    multipart.addBodyPart(htmlPartFactory.create("some html here", parameters));

    // Step 4: Send the mail

    // Synchronously
    MailResult result = mailSender.send(Arrays.asList(message), session);

    // Asynchronously with a Database Mail Listener:
    MailResult result = mailSender.sendAsynchronously(Arrays.asList(message), session, databaseMailListener);

    // Optional: Block till there are no more messages on the sending queue.
    // Parameter passed is the max time to wait in milliseconds.

    Get standard Session

    It's also possible to directly create a pre-configured mail Session.

    @Inject SessionFactory sessionFactory;
    @Inject @Named("text/html") MimeBodyPartFactory htmlPartFactory;
    @Inject MailSender mailSender;
    @Inject @Named("database") MailListener databaseMailListener;

    // Step 1: Create a JavaMail Session
    Map<String, String> customConfiguration = new HashMap()
    Session session = sessionFactory.create(customConfiguration);

    Using from Groovy

    Example of sending an HTML email with an attachment, using Groovy (can be useful for example if you need to write Groovy for a scheduler job):

    def from = '[email protected]'
    def to = '[email protected]'
    def message = services.mail.sender.createMessage(from, to, 'test')
    def attachment = xwiki.getDocument('Sandbox.WebHome').getAttachment('XWikiLogo.png')
    def attachments = [attachment]
    def parameters = ['alternative': 'text', 'attachments': attachments]
    message.addPart('text/html', '<h1>html title</h1>', parameters)
    def result = services.mail.sender.send(message)


    In addition to the examples below you can also watch a contributed video showing how to configure mail sending and testing it.

    Example 1: Send a simple text email

    This example uses a memory Mail Listener.

    #set ($message = $services.mail.sender.createMessage("[email protected]", "[email protected]", "subject"))
    #set ($discard = $message.addPart("text/plain", "text content"))
    #set ($mailResult = $services.mail.sender.send($message))
    ## Check if the message was created properly and if we have permissions to send emails
    #if ($services.mail.sender.lastError)
    ## Check if the mail we tried to send has failed to be sent
    #set ($statuses = $mailResult.statusResult.getAllErrors())
    #if ($statuses.hasNext())
     #set ($status = $
        Error: $status.errorSummary ($status.state)


    The same example using a database Mail Listener.

    #set ($message = $services.mail.sender.createMessage("[email protected]", "[email protected]", "subject"))
    #set ($discard = $message.addPart("text/plain", "text content"))
    #set ($mailResult = $services.mail.sender.send([$message], 'database'))
    ## Check if the message was created properly and if we have permissions to send emails
    #if ($services.mail.sender.lastError)
    ## Check if the mail we tried to send has failed to be sent
    #set ($statuses = $mailResult.statusResult.getAllErrors())
    #if ($statuses.hasNext())
     #set ($status = $
        Error: $status.errorSummary ($status.state)


    Example 2: Send a text + calendar event email

    This example uses a memory Mail Listener.

    #set ($message = $services.mail.sender.createMessage("[email protected]", "[email protected]", "subject"))
    #set ($discard = $message.addPart("text/plain", "text content"))
    #set ($discard = $message.addPart("text/calendar;method=CANCEL", "
    PRODID: Meeting
    SUMMARY:test request
    ORGANIZER:MAILTO:[email protected]
    LOCATION:on the net
    DESCRIPTION:learn some stuff
    , {"headers" : {"Content-Class" : "urn:content-classes:calendarmessage"}}))
    #set ($mailResult = $services.mail.sender.send($message))

    Example 3: Send a Template email

    This example uses a memory Mail Listener.

    Add HTML + alternate text from a Template Document containing a XWiki.Mail object. Use that template to generate both the mail subject and the mail content. Also pass the current language to support internationalization (the right XWiki.Mail object will be used). Any $var1 Velocity variable is replaced with value1.

    #set ($templateReference = $services.model.createDocumentReference('', 'Space', 'MailTemplatePage'))
    #set ($mailParameters = {'language' : $xcontext.language, 'velocityVariables' : { 'var1' : 'value1' }})
    #set ($message = $services.mail.sender.createMessage('template', $templateReference, $mailParameters))
    #set ($discard = $message.setFrom('[email protected]'))
    #set ($discard = $message.addRecipient('to', '[email protected]'))
    #set ($mailResult = $services.mail.sender.send($message))

    It's also possible to pass "to", "from", "cc" and "bcc" in the parameter list:

    #set ($templateReference = $services.model.createDocumentReference('', 'Space', 'MailTemplatePage'))
    #set ($mailParameters = {'from' : '[email protected]', 'to' : '[email protected]', 'language' : $xcontext.language, 'velocityVariables' : { 'var1' : 'value1' }})
    #set ($message = $services.mail.sender.createMessage('template', $templateReference, $mailParameters))
    #set ($mailResult = $services.mail.sender.send($message))

    Example 4: Send a Template email to a list of Users and Groups

    The following example will send a template email to all the users in the XWiki.MyGroup group + to the XWiki.User1 and XWiki.User2 users + to the [email protected] email address.

    Note that nested groups are handled (i.e. if the XWiki.MyGroup group contains other groups, all users of those other groups will also receive the template email)!

    ## Parameters for the 'template' MimeMessageFactory
    #set ($templateParameters = {'type' : 'Some type', 'language' : $xcontext.language, 'velocityVariables' : { 'var1' : 'value1' }})

    #set ($templateReference = $services.model.createDocumentReference('', 'Space', 'MailTemplatePage'))
    #set ($parameters = {'hint' : 'template', 'parameters' : $templateParameters, 'source' : $templateReference})

    #set ($groupReference = $services.model.createDocumentReference('', 'XWiki', 'MyGroup'))
    #set ($user1Reference = $services.model.createDocumentReference('', 'XWiki', 'User1'))
    #set ($user2Reference = $services.model.createDocumentReference('', 'XWiki', 'User2'))

    #set ($source = {'groups' : [$groupReference], 'users' : [$user1Reference, $user2Reference], 'emails' : ['[email protected]']})

    #set ($messages = $services.mail.sender.createMessages('usersandgroups', $source, $parameters))
    #set ($mailResult = $services.mail.sender.send($messages, 'database'))

    It's also possible to exclude groups, users and email addresses:

    #set ($source = {'groups' : [$groupReference], 'users' : [$user1Reference, $user2Reference], 'emails' : ['[email protected]'], 'excludedUsers' : [], 'excludedEmails' : [], 'excludedGroups' : []})

    To try this out, here's a script that sends some mail to all registered users of a wiki. To use it, create a Admin.MailTemplate terminal page and add a XWiki.Mail xobject to it and put the following content in any page:

    #if ("$!request.confirm" == '1')
     #set ($templateParameters = {'type' : 'SendAll', 'language' : $xcontext.language })
     #set ($templateReference = $services.model.createDocumentReference('', 'Admin', 'MailTemplate'))
     #set ($parameters = {'hint' : 'template', 'parameters' : $templateParameters, 'source' : $templateReference })

     #set ($groupReference = $services.model.createDocumentReference('', 'XWiki', 'XWikiAllGroup'))
     #set ($source = {'groups' : [$groupReference]})

     #set ($messages = $services.mail.sender.createMessages('usersandgroups', $source, $parameters))

     #set ($mailResult = $services.mail.sender.send($messages, 'database'))

      Mails are being sent. Check the status in the [[Admin>>path:$xwiki.getURL('XWiki.XWikiPreferences', 'admin', 'editor=globaladmin&section=emailStatus')]].
      To send email to all users, [[click here>>||queryString='confirm=1']]

    Example 5: Send a prepared Mime Message to a list of Users and Groups

    The following example is similar to the previous one, except that it use a fixed prepared mime message to send it to multiple users as separate independent message. (ie: all the users in the XWiki.MyGroup group).

    ## Create a mime message, the way you like it, adding any part you like, without recipient.
    #set ($message = $services.mail.sender.createMessage('[email protected]', null, 'SendMimeMessageToGroup'))
    #set ($discard = $message.addPart('text/plain', 'text content'))

    ## Use the mime message cloning factory as message factory to duplicate the created message
    #set ($parameters = {'hint' : 'message', 'source' : $message})

    #set ($source = {'groups' : [$services.model.createDocumentReference('', 'XWiki', 'XWikiAllGroup')]})

    #set ($messages = $services.mail.sender.createMessages('usersandgroups', $source, $parameters))
    #set ($result = $services.mail.sender.send($messages, 'database'))


    Mail Sender configuration properties are searched in various places, using the following order:

    • Look for a non-empty value in Mail.MailConfig in the current wiki
    • Look for a non-empty value in Mail.MailConfig in the main wiki
    • Look for a non-empty value in the xwiki properties file

    XWiki 15.4+ If the SMTP host config property is overridden in a subwiki, then the credentials (username and password) must also be overridden (if not, they'll be considered empty). This is to allow anonymous credentials in subwikis when overriding the SMTP host.

    To see all the possible configuration properties, check the file (check the "Mail" section in that file).

    When the Mail.MailConfig page doesn't exist or doesn't contain a Mail.SendMailConfigClass or a Mail.GeneralMailConfigClass, they'll be created, and filled with non-empty values from XWikiPreferences xobjects (for backward-compatibility). More precisely the values from (current space).XWikiPreferences will be checked first and then XWiki.XWikiPreferences from the current wiki.


    There are 2 ways to extend the API:

    • By implementing components that implement the MimeBodyPartFactory role.
    • By implementing components that implement the MimeMessageFactory role.


    Any document using the Scripting API needs to have Programming Rights by default to be allowed to send emails. This is configurable through the file:

    #-# Defines which authorization checks are done when sending mails using the Mail Sender Script Service.
    #-# Example of valid values:
    #-# - "programmingrights": the current document must have Programming Rights
    #-# - "alwaysallow": no check is performed. This is useful when running XWiki in a secure environment where we
    #-#   want to allow all users to be able to send emails through the Script Service.
    #-# The default is:
    # mail.sender.scriptServiceCheckerHint = programmingrights

    There's a pluggable permission checker used for checking if a mail should be sent, when using the Mail Sender Script Service. Two implementations are provided (see above) but you can also provide your own implementation by implementing the org.xwiki.mail.script.ScriptServicePermissionChecker component role:

    public interface ScriptServicePermissionChecker
         * @param message the message to check for authorization
         * @exception MessagingException if the message is not allowed to be sent

       void check(MimeMessage message) throws MessagingException;

    For example you could imagine implementing checks on the size of the email or who the recipients are, run the content of the mail through some filter, etc.


    How to create a Multipart alternative with both HTML and text?

    You might be tempted to write:

    $message.addPart("text/plain", "text message")
    $message.addPart("text/html", "html message")"

    However this is not correct. You need to use the HTML Body Part Factory and pass the alternative in the parameters list as in:

    $message.addPart("text/html", "html message", {"alternate" : "text message"})

    Said differently each call to addPart() adds a body part in a top level "mixed" Multipart.

    Compatibility with the Mail Sender Plugin

    Since this API replaces the old Mail Sender Plugin, users should be aware of the following difference in the mimetypes used for attachments added to emails (note that the Mime Types used can be configured by providing a Custom Tika Mime type configuration file):

    File ExtensionOld Mail Sender PluginMail Sender API

Prerequisites & Installation Instructions

We recommend using the Extension Manager to install this extension (Make sure that the text "Installable with the Extension Manager" is displayed at the top right location on this page to know if this extension can be installed with the Extension Manager).

You can also use the manual method which involves dropping the JAR file and all its dependencies into the WEB-INF/lib folder and restarting XWiki.


Dependencies for this extension (org.xwiki.platform:xwiki-platform-mail-send-api 16.4.0):

Get Connected