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});
}