5

I need to send a (potentially large) number of emails to (community) users via code using an Email Template, with a custom object as the whatId.

  • Because my whatId is a custom object, I am using SingleEmailMessage, not MassEmailMessage.
  • Because I am using an Email template, I am using setTargetObjectId(), not setToAddresses(), so I must construct one SingleEmailMessage per recipient.
  • Because I am emailing to internal users, I am setting my target object id to user id values to avoid limits (see next section).

I have found the following relevant passages in the Limits Quick Reference (spring 15):

Total number of sendEmail methods allowed: 10 (p. 30)

If you use SingleEmailMessage to email your organization’s internal users, specifying the user’s ID in setTargetObjectId means the email doesn’t count toward the daily limit. However, specifying internal users’ email addresses in setToAddresses means the email does count toward the limit. (p. 41)

You can send an unlimited amount of email to your organization’s internal users, which includes portal users (p. 42)

The other email limits I have found are related to Email Services (inbound email), or to emails to external email address, neither of which apply.

Messaging.sendEmail accepts a list<Messaging.Email> of messages to send - so is there a limit on how many SingleEmailMessage objects I can pass in that list? Is there a limit on number of emails per transaction? Some other limit I'm missing?

Let's say I need to send emails to 25,000 Community Users (extreme case, may be a possibility). Can I do this in a single call to Messaging.sendEmail()? I'm guessing I'd hit a heap limit building my list. Any way to tell how many I can send at once? I am planning to use a Batchable class, and was prepared to work in batches of 10, sending one email per sendEmail invocation (first limit above) before I realized that sendEmail accepts a list. What is a sensible batch size? If the list size is limited, is the per-transaction total limited? Batchable batches are limited to 2000 records, could I send one list of 2000? or 10 lists of 200?

1
  • Hi Clark,I'm facing the same issue.how did you resolve this issue,please let us know.
    – User1511
    Oct 8, 2015 at 20:45

2 Answers 2

7

Edit: Having been questioned about the behavior, I just tested this and found that you can send as many emails as you can handle within governor limits. As a proof of concept, I wrote the following code:

Messaging.SingleEmailMessage[] messages = new Messaging.SingleEmailMessage[0];
for(integer i = 0; i < 10000; i++) {
    messaging.SingleEmailMessage m = new messaging.SingleEmailMessage();
    m.settargetobjectid(userinfo.getUserId());
    m.setsubject('hello'+i);
    m.setplaintextbody('world'+i);
    m.setsaveasactivity(false);
    messages.add(m);

}
messaging.sendemail(messages);
system.assert(false);

This code did not fail with any errors about too many emails in the list, heap limits, CPU limits, etc. So, as long as you don't break any other governor limits, you can theoretically send 25,000 in a single call (but, you'd likely run in to governor limits without going asynchronous).


You can send 100 SingleEmailMessage per sendEmail call, and 10 sendEmail calls per transaction. So, you can send 1000 per transaction. There's no limit for sending to portal and internal users using setTargetObjectId.

All of this information is found in the Governor Limits document in the Email Limits section (the link goes there directly).

8
  • 1
    The document says "You can send 100 emails per SingleEmailMessage," which I take to mean that you can specify up to 100 recipients with setToAddresses(). No where does it say 100 SingleEmailMessages per sendEmail call that I can find. Apr 10, 2015 at 12:06
  • @JasonClark Doh. You're right. I misread. That does make me wonder what the maximum value would be, then. I'm assuming there's got to be at least some limit based on DML rows or something, though.
    – sfdcfox
    Apr 10, 2015 at 13:31
  • @sfdcfox do you know if Contact Ids in setTargetObjectId bypass the limits as well? Or is it only User Ids that work?
    – Mossi
    Nov 18, 2016 at 19:46
  • @Mossi No, leads and contacts count towards the daily limits described in the Governor Limits documentation.
    – sfdcfox
    Nov 18, 2016 at 19:52
  • 3
    @Programmatic The docs and this answer are both wrong compared to current behavior. You are allowed 10x Messaging.sendEmail calls, 100x emails per email message, and unlimited (?!) emails in a single call to Messaging.sendEmail (subject to governor limits). I just tested this and sent myself 10,000 emails in a single Messaging.sendEmail call (then canceled it to avoid spamming myself).
    – sfdcfox
    Apr 3, 2019 at 19:30
0

I have a solution for you that gets around some of these problems. Given that you are sending up to 25000 emails, you could still run into limits, but this should be helpful.

Your comment here:

Because I am using an Email template, I am using setTargetObjectId(), not setToAddresses(), so I must construct one SingleEmailMessage per recipient.

Can be avoided by a cunning use of Database.setSavePoint() and Database.rollback()

We can both use an Email template AND set a whole bunch of email addresses at once in the recipients part of the email.

I'll paste the code in and then explain:

public void sendEmails(Opportunity[] opps, Map<Id,String[]> toAddressesByOpp) {

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

    EmailTemplate[] templates = [SELECT HtmlValue, Body 
                                FROM EmailTemplate WHERE DeveloperName LIKE '%Your Template Name%' ];

    //get a contact to add to the email - don't worry, we are not sending anything to this person
    Contact[] contacts = [SELECT Id, Name, Email FROM Contact WHERE Email LIKE '%[email protected]%' LIMIT 1];

    if (templates.size()==0 || contacts.size()==0){
      return;
    }

    Messaging.SingleEmailMessage[] dummyEmails = new Messaging.SingleEmailMessage[]{};
    Messaging.SingleEmailMessage[] emails = new Messaging.SingleEmailMessage[]{};

    for (Opportunity opp : opps){
      email = new Messaging.SingleEmailMessage();
      //set up email for a fake send.
      email.setWhatId(opp.Id);
      email.setTemplateId(templates[0].Id);
      email.setTargetObjectId(contacts[0].Id);
      email.setSaveAsActivity(false);
      dummyEmails.add(email);
    }

    try {
      Savepoint sp = Database.setSavepoint();
      //this causes the email to be parsed.
      Messaging.sendEmail(dummyEmails);
      //we don't actually want to send the email to the owner, so rollback.
      Database.rollback(sp);
    }
    catch (Exception ex) {
    }

    //now extract our compiled/parsed messages
    for (Messaging.SingleEmailMessage e : dummyEmails){

      String[] toAddresses = toAddressesByOpp.get(e.getWhatId());
      if (toAddresses.size()<1){
        continue;
      }
      //extract the parsed details and resend!!
      String mailTextBody = e.getPlainTextBody();
      String mailHtmlBody = e.getHTMLBody();
      String mailSubject = e.getSubject();

      //we need a new email to attach all this data to
      email = new Messaging.SingleEmailMessage();

      email.setToAddresses(toAddresses);
      email.setHtmlBody(mailHtmlBody);
      email.setPlainTextBody(mailTextBody);
      email.setSubject(mailSubject);

      emails.add(email);
    }

    try {
      Messaging.sendEmail(emails);
    } 
    catch (Exception ex) {
    }
  }

This code uses an an Opportunity as a WhatId, but you can use what you like. Also, the list of email addresses is indexed via an oppid based Map().

You could just pass in a list of email addresses and not worry about any sort of mapping to Opportunity.

Anyway, to send a SingleEmailMessage, you need to call setTargetObjectId() with a contact or user id. This is why we get a random Contact in the system and use them to create the email.

At this point, we set a savepoint and send the Email. This forces Salesforce to parse the message and evaluate the Template. We immediately rollback the transaction, but as we have a reference to the parsed Email, we can now treat it as a regular email message - except that we were able to use a template!

From here, we add our real recipients and send.

This is from code that has been working in production for nearly 2 years.

(Minus a few error checks, exception handling and debug statements for brevity)

2
  • I don't think this will work for me, specifically due to the limits; see my second quote from p41. The limits document states that you must assign user ids via setTargetId() to avoid the 1000-per-day send limit; if you use setToAddresses() you are subject to the limit. Apr 10, 2015 at 12:14
  • Ah yes, you are right. However, I can't see any problem with calling email.setTargetObjectId again on the new email and setting the saved html body on the email. I had a look at the spec and I couldn't see any restrictions on this. Apr 11, 2015 at 2:51

You must log in to answer this question.

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