Mail Sender API

Last modified by Vincent Massol on 2017/10/06 13:49

cogAPI to send emails
Developed by

XWiki Development Team

Active Installs0
1 Votes
LicenseGNU Lesser General Public License 2.1
Bundled With

XWiki Enterprise


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.


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.

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.mailsender.createMessage()
$message = $services.mailsender.createMessage(to, subject)
$message = $services.mailsender.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})
  • 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.mailsender.send($message))
  • Send N messages, synchronously, storing the result in memory:
    #set ($mailResult = $services.mailsender.send([$message1, $message2, ...]))

    // Equivalent to:
    #set ($mailResult = $services.mailsender.send([$message1, $message2, ...], 'memory'))
  • Send N message, asynchronously, storing the result in memory:
    #set ($mailResult = $services.mailsender.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.mailsender.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:

XWiki 7.1+:

// 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)
// Note: Use "$mailResult.waitTillProcessed(10000L)" with XWiki < 7.1

XWiki < 7.1:

// 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)
// Note: Use "$mailResult.waitTillProcessed(10000L)" with XWiki < 7.1

Check for Errors

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

    This can happen for example under the following conditions:

    • If an error happened when creating the message(s) (when using the $services.mailsender.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.mailsender.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

    XWiki <7.1:

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



    XWiki 7.1+:

    • 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.mailsender.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.mailsender.createMessage(hint, source, parameters))
    #set ($messages = $services.mailsender.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(""));

    // 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 = sender.send(Arrays.asList(message), session);

    // Asynchronously with a Database Mail Listener:
    MailResult result = sender.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.
    // Note: Use "result.waitTillProcessed(10000L);" with XWiki < 7.1

    Get standard Session

    Since 8.3 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);


    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.mailsender.createMessage("", "", "subject"))
    #set ($discard = $message.addPart("text/plain", "text content"))
    #set ($mailResult = $services.mailsender.send($message))
    ## Check if the message was created properly and if we have permissions to send emails
    #if ($services.mailsender.lastError)
    ## Check if the mail we tried to send has failed to be sent
    ## XWiki < 7.1 use #set ($statuses = $mailResult.statusResult.getByState('FAILED'))
    #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.mailsender.createMessage("", "", "subject"))
    #set ($discard = $message.addPart("text/plain", "text content"))
    #set ($mailResult = $services.mailsender.send([$message], 'database'))
    ## Check if the message was created properly and if we have permissions to send emails
    #if ($services.mailsender.lastError)
    ## Check if the mail we tried to send has failed to be sent
    ## XWiki < 7.1 use #set ($statuses = $mailResult.statusResult.getByState('FAILED'))
    #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.mailsender.createMessage("", "", "subject"))
    #set ($discard = $message.addPart("text/plain", "text content"))
    #set ($discard = $message.addPart("text/calendar;method=CANCEL", "
    PRODID: Meeting
    SUMMARY:test request
    LOCATION:on the net
    DESCRIPTION:learn some stuff
    ", {"headers" : {"Content-Class" : "urn:content-classes:calendarmessage"}}))
    #set ($mailResult = $services.mailsender.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.mailsender.createMessage('template', $templateReference, $mailParameters))
    #set ($discard = $message.setFrom(''))
    #set ($discard = $message.addRecipient('to', ''))
    #set ($mailResult = $services.mailsender.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' : '', 'to' : '', 'language' : $xcontext.language, 'velocityVariables' : { 'var1' : 'value1' }})
    #set ($message = $services.mailsender.createMessage('template', $templateReference, $mailParameters))
    #set ($mailResult = $services.mailsender.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 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' : ['']})

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

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

    #set ($source = {'groups' : [$groupReference], 'users' : [$user1Reference, $user2Reference], 'emails' : [''], '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.mailsender.createMessages('usersandgroups', $source, $parameters))

     #set ($mailResult = $services.mailsender.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

    XWiki 7.1 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.mailsender.createMessage('', 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.mailsender.createMessages('usersandgroups', $source, $parameters))
    #set ($result = $services.mailsender.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
    • [Backward compatibility] Look for a non-empty value in (current space).XWikiPreferences in the current wiki
    • [Backward compatibility] Look for a non-empty value in in XWiki.XWikiPreferences in the current wiki
    • Look for a non-empty value in the xwiki properties file
    In the future, we'd like to change this to also look for a non-empty value in Mail.MailConfig in the main wiki (see XWIKI-12435).

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


    There are 2 ways to extend the API:

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


    Starting with XWiki 6.4M2 any document using the Scripting API will need to have Programming Rights by default to be allowed to send emails. This is configurable through the file:

    #-# [Since 6.4M2]
    #-# 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


    ImprovementXWIKI-14727OpenAdd name of last author + link to modified page to the email templateGuillaume Delhumeau
    ImprovementXWIKI-14525OpenUnable to specify multiple values for a filter when querying the mail message storeUnassigned
    BugXWIKI-13720OpenImpossible to inject database MailListener in an event listenerUnassigned
    ImprovementXWIKI-13294OpenEnable smtp extra property from Mail sending should be valid also for Invitation appUnassigned
    ImprovementXWIKI-12830OpenAllow deleting mail status for a whole batch using a single requestUnassigned
    New FeatureXWIKI-12826OpenMail sending should have a priority queue for urgent mailsUnassigned
    ImprovementXWIKI-12500OpenDisable the mail API when it is not yet configuredUnassigned
    ImprovementXWIKI-12435OpenUse the Main Wiki Mail Configuration if not overridden at the subwiki levelUnassigned
    BugXWIKI-12207OpenNo information about the recipient user or its group from a mail template when using a UserAndGroupMimeMessage iteratorUnassigned
    ImprovementXWIKI-12180OpenScriptMimeMessage#addPart should support BodyPartUnassigned
    BugXWIKI-12179OpenMailSenderScriptService will throw a nullpointerexception if there is any issue instead of returning nullUnassigned
    BugXWIKI-12178OpenNullPointerException when trying to create a mail from a not existing mail template documentUnassigned
    ImprovementXWIKI-12110OpenOnly discard success mail statuses when the preparation is overUnassigned
    ImprovementXWIKI-12109OpenHandle Mail duplicates in the Prepare threadUnassigned
    ImprovementXWIKI-12108OpenMake mail threads use less CPUUnassigned
    ImprovementXWIKI-12107OpenAdd Mail context information when listing Mail statusesUnassigned
    ImprovementXWIKI-12106OpenAdd the sender's user name to the Admin Mail Status UIUnassigned
    ImprovementXWIKI-12105OpenDisplay Mail statuses in user's profiles for mails they sentUnassigned
    ImprovementXWIKI-12081OpenDisplay batch statuses in Mail Status Admin UIUnassigned
    New FeatureXWIKI-12015OpenAdd "send test message" functionality to admin Mail UIUnassigned
    ImprovementXWIKI-11654OpenAdd the ability to delete a single messageUnassigned
    ImprovementXWIKI-11653OpenAdd the ability to delete all messages (from a batch or overall) from the UIUnassigned
    ImprovementXWIKI-11652OpenAdd the ability to resend all failed messages from a batchUnassigned
    ImprovementXWIKI-10638OpenAdd support for adding other body part types in XWiki.Mail templates and moreUnassigned
    New FeatureXWIKI-10489OpenAdd ability to send a full wiki page by emailVincent Massol

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). Note that installing Extensions when being offline is currently not supported and you'd need to use some complex manual method.

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 9.8.1):

Tags: mail
Created by Vincent Massol on 2014/06/13 15:32

Get Connected