Dynamically populating custom HTML email template content in force.com with custom dynamic data using Apex

The Force.com platform provides rich set of features to send emails through the apex code. It provides different templates like visualforce templates, HTML email templates and text templates so that the developers can create rich email experience. The beauty of these templates is that as a developer you can include the fields from an object, so that the emails will be completely customized for the given scenario. For e.g. if you want to send a nicely formatted HTML emails with the opportunity information to your sales group, then you can include the opportunity related fields such as the opportunity name, whether the opportunity is won or lost, etc. There are several articles that covers the basics of email services like this and this. By default, as a developer, you can only send the values of the object fields in the email and cannot include anything that is dynamically computed. This article will show you how to dynamically populate the content of a custom HTML email template with custom data. For e.g. adding dynamically calculated data such as number of days it took to won the opportunity, total number of sales by a sales person in a given month, etc. I’m also going to show you how you can write some generic code that can take varying dynamic parameters, populate the custom HTML email template with those dynamic parameters along with the standard/custom fields from standard/custom objects and send an email to the recipients. What is unique in this approach is that it is not only possible to populate the email body text with custom created dynamic data, but also possible to populate the email subject with custom created dynamic data.

For this demonstration, let us take the following use case. The requirement is to send an email notification on the Case object whenever a case is not closed and the case is still pending even after the set SLA period. For e.g. if a case is pending for 5 days since it was opened and if the SLA was set for 5 days, then on the 6th day, an email notification needs to be sent. Here is the screenshot of how the email will look alike when it is sent. The values highlighted in the red come from the standard fields, while the values highlighted in blue are calculated dynamically.


Here is the high level approach to accomplish this.

  • Create the custom HTML email template
  • Create the logic to populate the subject and the body text and send the email
  • Create a apex scheduler to send the email

1. Create the custom HTML email template

A. The following screenshots show the steps in creating the custom HTML email template.


B. In order to include the dynamically calculated data, follow the same convention of using the merge fields. For e.g to include the case status, include ‘{!Case.Status}. Please note that unlike in the standard merge field feature in the HTML templates, here you can use any format for the merge field syntax. For the custom data, I suggest to use the variable name that you used to calculate the dynamic data. In the following example, ‘{!numDays} indicate the number of days the case is open since it was created.


C. Specify the text for the plain text email. You can click the ‘Copy text from HTML version’ button to copy the content in the HTML template, unless you want to specify a different text for plain text email.


Click ‘Save’.

2. Create the logic to populate the subject and the body text and send the email

In the above example, the subject has two dynamic parameters and the body text has 6 dynamic parameters. Each requirement is different and we need a generic method to accommodate different number of parameters for both subject and the body text. To accomplish that, I have used two ‘maps’, one for subject and another for body text. This way, we can add any number of dynamic parameters and able to customize based on the requirements. I have created two apex classes to accommodate this logic:

  • EmailMessageWrapper – Wraps email message. Provides different constructors to capture email information.
  • UtilityClass – Provides method(s) to send emails. One of the method supports sending email using email templates and this is the method that we will use for this demo.

The following code snippet from EmailMessageWrapper shows the constructor that we will use in this demo.

   1: public EmailMessageWrapper(String fromAddr, Id toAddrId, String sub, Map<String, String> mapSubjectParams, Map<String, String> mapBodyParams) {

   2:     this(fromAddr, null, toAddrId, null, sub, mapSubjectParams, null, mapBodyParams );

   3: }


   5: public EmailMessageWrapper(String fromAddr, String toAddr, Id toAddrId, String bccAddr, String sub, Map<String, String>  mapSubjectParams, String body, Map<String, String> mapBodyParams) {

   6:     this.FromAddress = fromAddr;

   7:     this.ToAddress = toAddr;

   8:     this.ToAddressId = toAddrId;

   9:     this.BccAddress = bccAddr;

  10:     this.Subject = sub;

  11:     this.ParameterSubjectMap = mapSubjectParams;

  12:     this.Body = body;

  13:     this.ParameterBodyMap = mapBodyParams;

  14: }

The mapSubjectParams and the mapBodyParams parameters holds the dynamic data as a key/value pair. The key corresponds to the merge field that is defined in the custom HTML email template, while the ‘value’ is the data that will be merged into the merge field.

The following code snippet from UtilityClass shows the sendEmail method that takes two parameters. The first parameter is a list of EmailMessageWrapper and the second parameter is the email template name.

   1: public static void sendEmail(List<EmailMessageWrapper> listEmailMessageWrapper, String emailTemplateName) {

   2:     List<Messaging.SendEmailResult> listEmailResult = null;

   3:     List<Messaging.Singleemailmessage> listSingleEmailMessages = new List<Messaging.Singleemailmessage>();

   4:     EmailTemplate emailTemplate = [SELECT Id, Subject, HtmlValue, Body FROM EmailTemplate WHERE Name = :emailTemplateName];

   5:     for (EmailMessageWrapper emailMessageWrapper : listEmailMessageWrapper) {

   6:         Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

   7:         mail.setSenderDisplayName('Case Age Notification');

   8:         if(emailMessageWrapper.FromAddress != null && emailMessageWrapper.FromAddress.length() > 0)

   9:             mail.setReplyTo(emailMessageWrapper.FromAddress);

  10:         if(emailMessageWrapper.ToAddress != null && emailMessageWrapper.ToAddress.length() > 0)

  11:             mail.setToAddresses(new String[] { emailMessageWrapper.ToAddress });

  12:         else

  13:             mail.setTargetObjectId(emailMessageWrapper.ToAddressId);

  14:         if(emailMessageWrapper.BccAddress != null && emailMessageWrapper.BccAddress.length() > 0)

  15:             mail.setBccAddresses(new String[] {emailMessageWrapper.BccAddress });

  16:         String subject = null;

  17:         if(emailMessageWrapper.Subject != null && emailMessageWrapper.Subject.length() > 0) {

  18:             mail.setSubject(emailMessageWrapper.Subject);

  19:             subject = emailMessageWrapper.Subject;

  20:         }

  21:         else

  22:             subject = emailTemplate.Subject;


  24:         for(String key: emailMessageWrapper.ParameterSubjectMap.keySet())

  25:             subject = subject.replace(key, (emailMessageWrapper.ParameterSubjectMap.get(key) == null ? '' : emailMessageWrapper.ParameterSubjectMap.get(key)));


  27:         mail.setSubject(subject);

  28:         String htmlBody = emailTemplate.HtmlValue;

  29:         String plainBody = emailTemplate.Body;

  30:         for (String key : emailMessageWrapper.ParameterBodyMap.keySet()) {

  31:             htmlBody = htmlBody.replace(key, (emailMessageWrapper.ParameterBodyMap.get(key) == null) ? '' : emailMessageWrapper.ParameterBodyMap.get(key));

  32:             plainBody = plainBody.replace(key, (emailMessageWrapper.ParameterBodyMap.get(key) == null) ? '' : emailMessageWrapper.ParameterBodyMap.get(key));

  33:         }


  35:         mail.setHtmlBody(htmlBody);

  36:         mail.setSaveAsActivity(false);

  37:         mail.setPlainTextBody(plainBody);

  38:         listSingleEmailMessages.add(mail);

  39:     }

  40:     if(!Test.isRunningTest())

  41:         listEmailResult = Messaging.sendEmail(listSingleEmailMessages);

  42: }

What you see in the above code is that we iterate through the maps to populate the dynamic data in the HTML email template content by replacing the merge field with the dynamic data from the map variable. Since it is a map, we can add any number of dynamic parameters as key/value pairs, where ‘key’ represents the merge field and the ‘value’ represents the data for the merge field.

3. Create a apex scheduler to send the email

The final piece of this demo is to create the apex scheduler to send the email. The following code snippet is from the CaseAgeNotificationExecute class which retrieves the eligible cases and sends an email notification.

   1: public static void processSchedules() {

   2:     Integer slaTime = 3;

   3:     List<EmailMessageWrapper> listEmailMessageWrapper = new List<EmailMessageWrapper>();


   5:     String slaTimeLabel = 'Days';

   6:     List<Case> listCases = [SELECT Id, CaseNumber, CreatedDate, OwnerId, Status FROM Case WHERE Status <> 'Closed'];

   7:     for(Case c : listCases) {

   8:         if(DateTime.now() > c.CreatedDate.addDays(slaTime)) {

   9:             long numDays = (DateTime.now().getTime() / 1000 / 60 / 1440) - (c.CreatedDate.getTime() / 1000 / 60 / 1440);

  10:             Map<String, String> mapSubjectParams = new Map<String, String> {

  11:                 '{!Case.Status}' => c.Status,

  12:                 '{!numDays}' => String.valueOf(numDays)

  13:             };


  15:             Map<String, String> mapBodyParams = new Map<String, String> {

  16:                 '{!Case.CaseNumber}' => c.CaseNumber,

  17:                 '{!Case.Status}' => c.Status,

  18:                 '{!numDays}' => String.valueOf(numDays),

  19:                 '{!Case.CreatedDate}' => c.CreatedDate.date().format(),

  20:                 '{!slaTime}' => String.valueOf(slaTime),

  21:                 '{!slaTimeLabel}' => slaTimeLabel

  22:             };

  23:             listEmailMessageWrapper.add(new EmailMessageWrapper('admin@salesforce.com', c.OwnerId, null, mapSubjectParams, mapBodyParams));

  24:         }

  25:     }

  26:     if(listEmailMessageWrapper.size() > 0)

  27:         UtilityClass.sendEmail(listEmailMessageWrapper, 'Case Aging Notification Email Template');

  28: }

To test this, you can call the CaseAgeNotificationExecute.processSchedules() method from either the developer console or using anonymous block from your eclipse IDE.

The code for entire set of classes referred in this article can be downloaded from here.


Tagged: , , ,

21 thoughts on “Dynamically populating custom HTML email template content in force.com with custom dynamic data using Apex

  1. SutoCom September 4, 2013 at 10:59 am Reply
  2. Swell Designs (@SwellDesigns) September 12, 2013 at 10:07 pm Reply

    Hari, Thanks for posting this. I’ve been searching all day for this exact thing, and the source files are the icing on the cake. I’m glad i found your blog, it has a lot of great information. Definitely on the top of my Salesforce bookmarks. Cheers!

    • Hari Krishnan September 12, 2013 at 10:41 pm Reply

      Thanks Swell.

  3. ☁ Vamsi Krishna ☁ (@krish_vamsi) December 4, 2013 at 5:06 am Reply

    Nice post Hari.
    Thanks for the detailed steps in dynamically populating the email contents with data from apex. and I like the way you structured the code. very useful one.

    • Hari Krishnan December 4, 2013 at 5:36 pm Reply

      Thanks Vamsi.

  4. Chris Gibson December 4, 2013 at 7:59 pm Reply

    Is it possible for the parameters to be something other than strings? For example if I want to put a pageblocktable that iterates over a list stored in a variable in my apex class – will I be able to pass this list to the e-mail template’s pageblocktable?

    • Hari Krishnan December 5, 2013 at 11:22 pm Reply

      Hi Chris,
      The email template that I referenced in the article is a pure HTML email template – so adding pageblocktable will not work. However, if you need to display a dynamic table like dataset, then you can update the apex code that I provided to iterate through a list or map and generate HTML data as a string. You can then insert this string into the HTML template.

      Hari K.

  5. Jim Myers January 21, 2014 at 9:26 pm Reply

    Great work! Found a small bug today. If the plain body is defaulted, it fails. Need to use htmlbody in that case.

    • Hari Krishnan January 22, 2014 at 3:53 am Reply

      Jim – Thanks for pointing out – will fix it when I get a chance.

  6. surgaya December 16, 2014 at 10:54 am Reply

    Hi Hari,

    Your code works perfectly well for those templates without using a letterhead. If the template uses a letterhead then the letterhead doesn’t show up in the emails recieved.

    I have marked the “externally available” checkboxes in the letterheads images and made available for use.

    But somehow the brandTemplateId is not merging with the template.

    Please give me any sugggestions on this

    • Hari Krishnan December 16, 2014 at 11:04 pm Reply

      Hello Surgaya,
      I initially tried with HTML template using letterhead, but I had a problem when I was trying to replace the variables with the values. If I remember correctly, the closing CDATA tags ‘]]’ was appearing in my output and I couldn’t find a reliable way to remove it; hence I chose to use the custom HTML template. I’m not sure if there is a way to do this with HTML templates using letterhead.

  7. surgaya December 17, 2014 at 5:43 am Reply

    Yes Exactly I used get the same CDATA tags ‘]]’. But i got rid of that using replace().

  8. Seb January 23, 2015 at 11:09 am Reply

    Hi, Thanks for posting this. Can this code be used as is in our project? Cheers.

    • Hari Krishnan January 24, 2015 at 7:59 pm Reply

      Hello Seb,
      Yes, you can absolutely use it the way you want.

  9. twench January 28, 2015 at 9:15 pm Reply

    was this ever updated to allow the HTML template with letterhead? i keep getting a blank email when trying to use our letterhead. i used a text body email and it worked fine. thank you for posting this. i am a junior developer on my first job and this has helped me with one of my assignments and should be useful in the future.

  10. Janardhan Muddana February 12, 2015 at 4:25 pm Reply

    Hi Hari Krishnan,

    I understand some of the code.Can you please send me full code how to pass html body and subject to controller and all other things.I am new to coding part.Any reference links.

    I am very grateful to you.

  11. Subhojit Bhattacharjee August 17, 2015 at 2:59 pm Reply

    Can we send a list of selected data dynamically into email template? I am using a wrapper class to select some records from a list and want to populate it into a email template dynamically

  12. Shashi Kumar September 10, 2015 at 11:06 am Reply

    Hello Hari,

    I am looking at integrating a dynamic facebook post into a HTML Email. Can you throw some light on this?

  13. Neymar March 11, 2016 at 9:53 am Reply


    I am facing this error. Can you please make me understand this regarding this post?

  14. Kent Roessler May 10, 2016 at 2:35 pm Reply

    Hello Hari,

    Thanks for the posting. One question though.
    I downloaded the files and for the CaseAgeNotificationExecute.cls class, the line,
    public with sharing class CaseAgeNotificationExecute implements CaseAgeNotificationSchedule.ICaseAgeNotificationSchedule

    Where is the interface class? It doesn’t appear in the zip file.


  15. Geetha August 8, 2016 at 6:23 am Reply

    Nice post. gives information about custom html email template content which is very new to learn. Lot of concepts are provided in this post. keep on giving this type of posts

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: