Skip to main content

acs.email: Templated Email Service for AEM

Module README · Repository · Original ACS Commons Email API

acs.email is a slim, template-based email sender for AEM as a Cloud Service. It replaces ACS AEM Commons' EmailService with a focused OSGi service built directly on AEM's native MessageGatewayService and MailTemplate APIs -- no ACS Commons dependency required.

What it does

A mail template is a JCR file (typically under /apps/.../emailTemplates/) whose first line is a Subject: ... header, followed by a blank line and a plain-text or HTML body. The body may contain ${placeholder} tokens.

Calling sendEmail(templatePath, emailParams, recipients...):

  1. Resolves templatePath via a service resource resolver and parses it with MailTemplate.
  2. Merges emailParams into the template, substituting ${key} tokens in the body.
  3. Applies reserved keys (sender, subject, bounce address) as headers instead of body substitutions.
  4. Sends one email per recipient through MessageGatewayService, which uses the SMTP settings from AEM's com.day.cq.mailer.DefaultMailService OSGi config.

HTML vs plain text is inferred from the template file extension (.html → HTML), unless attachments are supplied, which force a multipart HTML message.

When to use it

Use acs.email when you need:

  • Order confirmations, password resets, workflow notifications, or any other templated transactional email from AEM.
  • A central OSGi service so every servlet, workflow step, and event listener sends mail the same way.
  • A drop-in replacement for ACS Commons EmailService without installing the rest of ACS Commons.

Prefer AEM's raw MessageGatewayService only for one-off, non-templated messages. Prefer a full ESP (SendGrid, Adobe Campaign, etc.) when you need marketing journeys, deliverability analytics, or non-AEM senders.

For SMTP and local testing background, see E-Mail Service in AEM.

Example usage

@Reference
private EmailService emailService;

Map<String, String> params = new HashMap<>();
params.put(EmailConstants.SUBJECT, "Your order has shipped");
params.put("orderNumber", "12345"); // substituted into the template body as ${orderNumber}

List<String> failures = emailService.sendEmail(
"/apps/myapp/emailTemplates/orderShipped.html",
params,
"customer@example.com");

Reserved keys in emailParams (EmailConstants.SENDER_EMAIL_ADDRESS, SENDER_NAME, SUBJECT, BOUNCE_ADDRESS) override headers. Everything else is merged into the template as a ${key} placeholder.

Attachments (optional -- forces HTML multipart):

MailAttachment attachment = new MailAttachment("invoice.pdf", myDataSource);
emailService.sendEmail(templatePath, params, List.of(attachment), "customer@example.com");

Example template

/apps/myapp/emailTemplates/orderShipped.html
Subject: Your order has shipped

<html>
<body>
<p>Hello,</p>
<p>Order <strong>${orderNumber}</strong> is on its way.</p>
</body>
</html>

Required setup: service user mapping

EmailServiceImpl resolves templates via a service resource resolver, not the calling user's session. Map a system user for it:

acsemail.core:emailService=[<your-system-user>]

via the Apache Sling Service User Mapper OSGi config, and grant that system user jcr:read on your template paths. Without this mapping, template resolution fails and every recipient is returned as a failure.

Building and deploying

mvn clean install # build
mvn clean install -PautoInstallBundle # build + deploy the bundle (default: localhost:4502)

Local testing with Mailpit

Point AEM's DefaultMailService at Mailpit so messages land in a local UI instead of a real SMTP server:

docker run -d --name mailpit -p 1025:1025 -p 8025:8025 axllent/mailpit

Then configure com.day.cq.mailer.DefaultMailService with smtp.host=localhost and smtp.port=1025. Open http://localhost:8025 to inspect sent mail.