24

I am having an html email template which uses certain merge fields from a custom object. Now I am using following apex code to send certain emails to users.

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

        mail.setTargetObjectId(t.OwnerId); 
        mail.setTemplateId(et.Id);
        mail.setWhatId(t.Id); 
        mail.SaveAsActivity = false;
        Messaging.sendEmail(new Messaging.singleemailMessage[] {mail});

Code is saved without error but when I run it it says "can't use whatId when sending email to users". Can anyone explain me how to overcome this error? When I use a visualforce template then same code works fine.

4 Answers 4

43

Unfortunately, the above solutions, to send to a contact record or to setToAddresses, will count towards Salesforce limit as explained here: https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_email.htm

You can send an unlimited amount of email to your org’s internal users, which includes portal users.

Internal user means the proper user record, not a random email address or Contact.

A solution which allows to send email to internal user, and yet allows the use of the whatId is by leveraging Messaging.renderStoredEmailTemplate(templateId, whoId, whatId). Click here for more details on this method: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_email_outbound_messaging.htm. This method allows to merge the fields without sending an email. Once fields are merged, it's possible to retrieve the subject and body text of the merged template and send an email to internal users without specifying a whatId.

Ultimately, this is what needs to be done

String templateId = ....;
String userId = ....;
String whatId = ....;

Messaging.SingleEmailMessage email = Messaging.renderStoredEmailTemplate(templateId, userId, whatId);

String emailSubject = email.getSubject();
String emailTextBody = email.getPlainTextBody();

email.setTargetObjectId(userId);
email.setSubject(emailSubject);
email.setPlainTextBody(emailTextBody);
email.saveAsActivity = false;

if(!Test.isRunningTest()) {
  Messaging.SendEmailResult[] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});
}
5
  • This is a very elegant solution to this problem. Thanks Sir. Jul 8, 2017 at 20:55
  • This is perfect solution I was always looking for. Why haven't I found this ever before!
    – Patlatus
    Feb 6, 2018 at 16:32
  • 1
    This is the perfect solution to this problem. It should be marked as best answer in my opinion. Oct 18, 2018 at 15:48
  • 2
    You should be able to get rid of the Subject/Body stuff. I was able to make this work with just these lines: ` Messaging.SingleEmailMessage message = Messaging.renderStoredEmailTemplate(templateId, userId, recordId); message.setTargetObjectId(userId); message.setSaveAsActivity(false); Messaging.SingleEmailMessage[] messages = new List<Messaging.SingleEmailMessage> {message}; Messaging.SendEmailResult[] results = Messaging.sendEmail(messages);` Aug 30, 2019 at 23:06
  • You saved my life! I had to send an email to several email addresses but not to the user, and renderStoredEmailTemplate worked! I wasted a day on it..... Dec 9, 2022 at 15:35
24

This is a glaring hole that I'm sad to say has been here since the beginning. Particular frustrating since workflow alerts seem to have no problems merging a custom object template when sending to a user. You can work around it by creating a contact with the users email and then deleting it after the email has been sent.

private static void sendMyTemplate(Id userId, Id mergedObjectId) {
  User user = [select email, firstName, lastName from User where id = :userId];
  Contact tempContact = new Contact(email = user.email, firstName = user.firstName, lastName = user.lastName);
  insert tempContact;
  Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
  mail.setTargetObjectId(tempContact.id); 
  mail.setTemplateId(template.Id);
  mail.setWhatId(mergedObjectId); 
  Messaging.sendEmail(new Messaging.singleemailMessage[] {mail});
  delete tempContact;
}
4
  • Ralph, plus 1 for a creative idea.
    – zachelrath
    Oct 2, 2012 at 13:17
  • 2
    The one glaring whole is validation rules. If you're org creates a new contact validation rule it'll break this code ... in which case @LaceySnr suggestion will be a better route Oct 8, 2012 at 20:10
  • +1. Though not recommended approach, but one of way out solution as of now. Thanks Ralph! May 22, 2015 at 6:45
  • What about if there are some business process - email notification, external system integration when contact is created :)
    – Ayub
    Jan 29, 2020 at 12:43
14

You can get around this by grabbing the user's email address from their user record (you've already got their ID so that's a trivial query) and then using that with the setToAddresses method of SingleEmailMessage, then you shouldn't need to set the target object ID and as such will bypass this restriction.

7
  • 2
    The downside to this approach is you can't use any merge fields dependent on the target object id. Feb 25, 2013 at 17:38
  • Did this ever work for you? Was just trying this and I get a dml error. Posted a question about it: salesforce.stackexchange.com/q/23273/594 Dec 31, 2013 at 18:41
  • 1
    It did for me... will see if I can check it out tomorrow, pushed for time right now!
    – Matt Lacey
    Jan 2, 2014 at 18:24
  • Thanks @laceysnr, really hoping I don't have to always waste two DML statements creating and deleting a contact. Jan 2, 2014 at 23:31
  • 1
    I tried this, but I get this error: First exception on row 0; first error: REQUIRED_FIELD_MISSING, Missing targetObjectId with template: []
    – Oxidator
    May 25, 2015 at 4:52
10

It would seem that you can't set the WhatId (which you have) when the target object Id is a UserId. You can set it when the target is a contact or such. This post i found seems to confirm that hunch https://stackoverflow.com/questions/11105086/setwhatid-in-salesforce-using-email-template

1
  • 4
    Note that it does work in visualforce email templates as @doga mentioned. It's a rather arbitrary restriction on non-VF templating. Oct 1, 2012 at 20:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .