In-Depth Guide on Sending Emails With Apex

On August 26, 2026
10min read
Dzenana Kajtaz Technical Content Writer @Mailtrap
This is a featured image for an article explaining how to send emails in Apex

Apex, the object-oriented programming language, helps users access the Salesforce platform back-end database and client-server interfaces. With Apex, a multitude of tasks can be performed, including sending emails/email notifications.

And while in this article, we won’t be touching upon each and every use case of this programming language, we will go in-depth on how to send emails with Apex.

So, if this is something you have been eager to learn, then get your Salesforce Developer Console ready, and make sure to read the rest of this article!

Ready to deliver your emails?
Try Mailtrap for Free

The limit on sending emails in Apex

Before getting into any technical steps or code snippets, we have to address one important thing – the limits Salesforce places on resource usage. Why? Because these limits will also affect the number of emails you can send with Apex, be it single or mass emails.

That being said, please use the following information as a guide when implementing your email-sending functionality:

  • Max total of 5000 single emails to external addresses per day
  • Max total of 5000 mass emails to external addresses per day
  • Max total of 150 email recipients specified in the to, cc, and bcc fields
  • Max size of 4,000 bytes for the to, cc, and bcc fields

Note: Each email sent to a duplicate email address counts as an individual email.

In case you exceed any of the mentioned limits, Salesforce will stop your code from running and provide you with either of the following errors: SINGLE_EMAIL_LIMIT_EXCEEDED or MASS_MAIL_LIMIT_EXCEEDED.

To prevent your code from sending any emails past a certain limit and thus avoid facing errors, the following method can be used Messaging.reserveMassEmailCapacity(50); with the number in the brackets indicating the max number of emails.

How to send a single email in Apex

Now that you know your limits, it’s time to learn how to write the Apex email-sending code.

As an example, below, you can find the code snippet we used to send a simple single email. The snippet covers how to send email to multiple recipients, set an email-sending limit, create an instance of the SingleEmailMessage object, specify the replyto address, and define other aspects of the email message, such as the sender name, subject, plain text, and HTML body. 

At the bottom of the snippet, you will also see the sendEmail method being called, which is responsible for the actual sending.

public class sendSingleEmail {
  Public void sendSingleEmailMethod(){

Messaging.reserveSingleEmailCapacity(2);
Messaging.SingleEmailMessage mymail = new Messaging.SingleEmailMessage();
String[] toaddresses = new String[] {'user1@example.com'}; 
String[] ccaddresses = new String[] {'user2@example.com'};
String[] bccaddresses = new String[] {'user3@example.com'};

mymail.setToAddresses(toAddresses);
mymail.setCcAddresses(ccAddresses);
mymail.setBccAddresses(bccAddresses);
mymail.setReplyTo('support@example.com');
mymail.setSenderDisplayName('Salesforce Support');
mymail.setSubject('An email from Salesforce');
mymail.setPlainTextBody('This email has been sent through Apex');
mymail.setHtmlBody('<p> This email has been sent through Apex</p>');

Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mymail });
  }
}

How to send mass emails in Apex

In our first code snippet, we demonstrated how to send an email to only a few email addresses at a time – the to, cc, and bcc addresses. But if we wanted to do the same for, let’s say, 200 addresses, things would get pretty messy.

To tackle this challenge, you can use the MassEmailMessage object intended to send bulk emails with the IDs from your contact list.

In case you don’t have a contact list already, you can create one in Salesforce by clicking on the settings icon in the top right corner and then navigating to Service Console -> Contacts.

An important thing to note when sending mass emails is that we will no longer be using the setToAddresses method. Instead, we will use setTargetObjectIds through which we can specify a list of contacts that will receive the email.

public class SendMassEmail {
  public static void SendEmail()
  {
     EmailTemplate et = [SELECT Id,Subject, Body FROM EmailTemplate WHERE Name ='ApexEmailTemplateMessage'];
        List<Contact> contacts=[Select Id,Email From Contact Where LastName='Eze'];
        List<Id> conIds=new List<Id>();
        List<Id> whatIds=new List<Id>();
        for(Contact c: contacts) {
            conIds.add(c.Id);
            whatIds.add(et.Id);
        }
		system.debug(conIds);
        Messaging.MassEmailMessage email = new Messaging.MassEmailMessage(); 
        email.setTargetObjectIds(conIds);
        email.setTemplateId(et.id);
        Messaging.SendEmail(New Messaging.MassEmailMessage[] {email});

  }
}

How to send emails with a template in Apex

To help you save time and also have consistent messaging across your emails, Salesforce allows you to use templates for both single and mass emails. What’s more, if you use Salesforce templates with merge fields, your emails can include field data from records such as contacts, leads, or opportunities.

When looking at the code we used to send an email with a template, most of your attention should be directed to the line where we create an instance of the EmailTemplate object. This object is assigned an ID value and later passed to the setTemplateId method. 

Do keep in mind that before writing your own version of the code, you will need to go into your Salesforce account, create a template and retrieve its name/ID.

public class sendEmailTemplate {     
    public static void SendEmailTemplateWithTemplate() {
        EmailTemplate et = [SELECT Id,Subject, Body FROM EmailTemplate WHERE Name ='ApexEmailTemplateMessage'];
        List<string> toAddress = new List<string>();
        toAddress.add('ezenagusomtobechukwu@gmail.com');
        Contact con = [Select Id, Email from Contact where LastName ='Eze'];
            system.debug(et);
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            mail.setTemplateId(et.Id);
            mail.setToAddresses(toAddress);
            mail.setSubject(et.subject);
            mail.setHTMLBody(et.Body);
            mail.setTargetObjectId(con.Id);
            mail.setWhatId(et.Id);
            mail.setSaveAsActivity(false);
            mail.setUseSignature(false);
        List<Messaging.SingleEmailMessage> allmsg = new List<Messaging.SingleEmailMessage>();
        allmsg.add(mail);

        try {
            Messaging.sendEmail(allmsg,false);
            return;
        } catch (Exception e) {
            System.debug(e.getMessage());
        }        
    }
};

How to send triggered emails in Apex

In case you are unfamiliar, triggered emails are used to respond to actions performed in Salesforce. And to create them, you need to create a trigger class that will be responsible for shooting out an email before or after a specific action such as Insert, Update, Delete, Merge, and Upsert.

For demonstration purposes, we will now go over how to use a Salesforce trigger to send emails every time a new contact is created.

First, you start off by creating a new Apex trigger file in your Salesforce account. You can name this file whatever you want. In our case, we went for “ContactEmailTrigger”.

Once the file is named, you need to then select “Contact” for the sObject using the dropdown menu. 

Finally, to implement the trigger class, you can follow this code example:

trigger ContactEmailTrigger on Contact (before insert) {
    // Access all emails in the list 
    List<Messaging.SingleEmailMessage> mailList =  new List<Messaging.SingleEmailMessage>();

    for (Contact myContact : Trigger.new) {
    if (myContact.Email != null && myContact.FirstName != null) {

      // initialize the single send email id object
      Messaging.SingleEmailMessage newMail = new Messaging.SingleEmailMessage();

      // Specify who receives the email
      List<String> sendToAddressesList = new List<String>();

      sendToAddressesList.add(myContact.Email);

      newMail.setToAddresses(sendToAddressesList);

       // Set the attributes of the email.
      newMail.setSubject('Your contact detail have been added');

      String body = 'Hello ' + myContact.FirstName + ', '; 
      body += 'Your details have been added and your contact has been created.';     
      newMail.setHtmlBody(body);

      // Add the email to the list
     mailList.add(newMail);
    }
  }
  // Send all emails in the list
  Messaging.sendEmail(mailList);
}

Note: This trigger fires on before insert for every Contact, including the ones your colleagues create while you are still testing it. In a real org that means real people receive whatever the trigger builds. If you want to exercise it without that happening, route the org’s outbound email to a sandbox first. The testing section below covers two ways to do it.

How to send emails with attachments in Apex

If you are looking to send email with attachments in Apex, the good news is that you can do so with a range of file types, including binary (blob) files. Plus, the process is pretty simple! 

To begin, in your Salesforce account, create a new document, specify its name and upload the document file. Also, be sure to remember the filename, as we will need it in the code.

Then, simply use your version of the following snippet to first fetch the document from your Salesforce account and then add it to your email with setEntityAttachments – a method that accepts IDs of Document, ContentVersion, and Attachment items.

public class EmailsAttachment {
   public static void emailerAttachment(){
        // Email sending with attachment
        Document doc = [SELECT Id,Name FROM Document WHERE Name = 'attachmentDocument'];
        list<ID> docid=new list<ID>();
        docid.add(doc.id);
        Messaging.SingleEmailMessage semail = new Messaging.SingleEmailMessage();
        semail.setEntityAttachments(docid);
    	semail.setUseSignature(false);
        semail.setSubject('Sending document as attachment');
        String[] sendTo = new String[]{'somtobeeze@gmail.com'};
        semail.setToAddresses(sendTo);
        semail.setPlainTextBody('Please inspect the document attached');
        Messaging.sendEmail(new Messaging.SingleEmailMessage[]{semail});

    }
};

How to send emails with Mailtrap in Apex 

Sending emails is one thing, but ensuring high deliverability and monitoring your email performance falls into a totally different category. That is why many devs resort to using solutions such as Mailtrap Email API/SMTP

With Mailtrap Email API/SMTP, your emails can reach customer inboxes in seconds. And thanks to its dashboard with major email stats, weekly health checks, critical deliverability alerts, and up to 60 days of email logs, you get to troubleshoot any unexpected sending issues quickly and easily.

To learn more about this reliable sending solution, check out the video below:

Now, let’s see just how straightforward it is to use Mailtrap Email API/SMTP in practice.

First, you create a Mailtrap account

Then, you proceed to add and verify your domain.

Upon domain verification, navigate to the SMTP/API Settings page, where you can find the API example code in different programming languages as well as SMTP credentials for sending emails from your domain.

Just make sure to pick the right email-sending stream based on your needs!

This image is showing the SMTP/API settings page in Mailtrap Email API/SMTP

For Apex, the API integration code should look something like this:

public class MailTrapEmailAPI {
   public static void sendMail(){
      String body = '{"to": [{"email": "johndoe@example.com", "name": "John Doe"}],  "cc": [    {      "email": "johnny@example.com",      "name": "Jane Doe"}],  "bcc": [{"email": "emmanuelezenagu5@gmail.com", "name": "Jim Doe"}],  "from": {"email": "emailer@mailtrap.io", "name": "Mailtrap sent this message"},  "subject": "Your Example Order Confirmation",  "html": "<p>Congratulations on your order no. <strong>1234</strong>.</p>",  "text": "Congratulations on your order no. 1234",  "category": "API Test",  "custom_variables": {"user_id": "45982", "batch_id": "PSJ-12"}, "headers": {"X-Message-Source": "example.yourdomain.com"}}';
      
       Http http = new Http();
       HttpRequest request = new HttpRequest();
       request.setEndpoint('https://send.api.mailtrap.io/api/send');
       request.setMethod('POST');
       request.setHeader('Api-Token', 'Your_MailrapSending_APIToken');
       request.setHeader('Content-Type', 'application/json;charset=UTF-8');
       request.setBody(body);
       System.debug('1 ' + request);
       HttpResponse response = http.send(request);
       System.debug('2 ' + response);
   }
}

Run the code, and you should see the email sent with Mailtrap Email API/SMTP in your account under Email Sending-> Email Logs, where you can also check out its status, preview, events history, HTML source, and statistics. 

Note: Before running the code, make sure to add Mailtrap Email API/SMTP as a remote site under Setup -> Security -> Remote Site Settings. Otherwise, you might receive the systemCalloutException: Unauthorized Endpoint error (more on that in the next section of this article).

How to test emails in Apex

While trying to send emails in Apex for the first time, it is normal to encounter a couple of hiccups. Luckily, Apex provides debugging support through the developer console and debug logs, which will alert you about any errors, with the most common ones being:

No_mass_mail_permission – An error often encountered by people using trial Salesforce accounts as these accounts can’t send mass emails. The error can also stem from not having the appropriate permissions in non-trial accounts, which can be modified under Setup -> Administration -> Email Administration -> Deliverability by setting the access level to “All emails”.

systemCalloutException: Unauthorized Endpoint – An error caused by your application attempting to access an external endpoint that has not been listed in your Salesforce account. To solve it, navigate to Setup -> Security -> Remote Site Settings and add a new remote site.

MALFORMED_ID – An error caused by specifying the wrong ID.

LIMIT_EXCEEDED – An error caused by exceeding the email sending limit.

INVALID_EMAIL_ADDRESS – An error caused by passing an invalid email.

REQUIRED_FIELD_MISSING – An error caused by not entering a value for a mandatory field. 

For more information about these and other errors, please refer to the Apex documentation.

Besides the developer console and debugs logs, you can also test your email-sending code with the Apex test class as well as third-party solutions. So let’s explore those, shall we?

Apex test class

The Apex test class is used to test the functionality of your Apex code, and a common practice is to write one for each of your standard Apex classes. This is done by placing the @isTest keyword before a specific class or method, such as:

@isTest
public class ApexEmailSampleTestClass {
    @testsetup static void data_create(){
        Contact cont1 = new Contact();
        cont1.FirstName = 'John';
        cont1.LastName = 'Doe';
        cont1.email = 'tochieze669@gmail.com';
        
        insert cont1;
    }

    @istest static void test_method(){
        Contact cont1 = [Select id,name,email from Contact WHERE name = 'John Doe'];
        System.debug(cont1);
        ApexEmailSample.sendEmail(cont1.email);
    }
}

In the above class code, two methods are marked as test methods – data_create and  test_method. These methods will not produce standard execution results as they are, you guessed it, just for testing.

So, upon error-free execution of ApexEmailSampleTestClass and the methods within it, you should see a contact email logged in your debug console. But, if you head over to your Salesforce account, you will not find a new contact or a new entry in your email logs.

Third-party testing solution: Mailtrap Email Sandbox

While the Apex test class and debugging support will allow you to test the functionality of your code, you are sort of left on your own if you want to see what your emails look like in different email clients, analyze their HTML/CSS code, check their spam score, and gain insight into detailed tech info.

For this, you will need the help of tools intended specifically for advanced email testing.

One such tool is Mailtrap Email Sandbox, which catches all SMTP traffic from staging and dev environments so you can inspect and debug the emails making up that traffic. Thanks to this way of functioning, Mailtrap Email Sandbox creates a safe environment for email testing and puts you at no risk of spamming users with testing emails.

With Mailtrap Email Sandbox, you also get to switch from manual testing to automated test flows and scenarios and preserve your domain reputation, as your personal inbox is no longer being used to send out testing emails.

Since this article was first written, connecting the two has been packaged up. Mailtrap Email Sandbox for Salesforce installs from the AppExchange, and once it is connected, turning Testing Mode on routes outbound email from the org into a Mailtrap sandbox. No SMTP credentials to copy, no email relay, no domain filter.

The point worth taking away for an Apex developer: nothing in your code changes. Messaging.sendEmail still does exactly what it did above. What changes is where the SMTP conversation ends up.

Setup is a one-time admin job: install the package, assign the permission set, configure a Named Credential, then connect your Mailtrap API token and choose which Mailtrap sandbox to route into. Two things to plan for. Testing Mode is org wide, so while it is on, every outbound email from that org goes to Mailtrap, including email your colleagues send. And turning it off does not release what was captured while it was on.

If you already send through Mailtrap: Salesforce allows one active email relay per org, and the managed package manages its own. Install the package into a Salesforce sandbox org first, and sequence the two rather than running them side by side.

Mailtrap Email Sandbox for Salesforce is free to use with the Mailtrap Free plan, which covers 50 test emails a month, one sandbox, and one email every 10 seconds, and does not support multiple recipients on a single email. Connecting Salesforce to a paid Mailtrap plan requires the paid add-on. The full walkthrough is in our guide to testing emails in Salesforce.

With Testing Mode on, this code will send your first test email into your Mailtrap sandbox.

public class ApexEmailSample {
    public static void sendEmail(String useremail){       
    Messaging.reserveSingleEmailCapacity(20);
        Messaging.SingleEmailMessage mymail = new Messaging.SingleEmailMessage();
        String[] toaddresses = new String[] {useremail}; 
        String[] toccaddresses = new String[] {'som****@gmail.com'};
        String[] tobccaddresses = new String[] {'ez*****@gmail.com'};
                    
        mymail.setToAddresses(toaddresses);
        mymail.setCcAddresses(toccaddresses);
        mymail.setBccAddresses(tobccaddresses);
        mymail.setReplyTo('support@example.com');
        mymail.setSenderDisplayName('Salesforce Support');
        mymail.setSubject('An email from Salesforce');
        mymail.setPlainTextBody('This email has been sent through Apex');
        mymail.setHtmlBody('<p>This email has been sent through Apex.</p>');
        
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mymail });
    	system.debug(useremail);
    }
}

Wrapping things up

In this article, we touched upon all the most important aspects of sending emails in Apex. So now, whenever you want to send single or mass emails, use templates/attachments/triggers, or simply put your email-sending code through some testing, you can always refer to this article.

For other topics related to emails in Salesforce that we didn’t cover in this article, such as “send email to public groups”, “send visualforce email template”, “send email alert”, “send email to queue members”, and so on, please consult Salesforce’s documentation.

Article by Dzenana Kajtaz Technical Content Writer @Mailtrap

I’m a Technical Content Writer with an educational background in software engineering and 4 years of experience as a writer, editor, and content marketer. Currently, I mainly focus on writing about email infrastructure, sending, testing, and so on, but I do love creating fun and engaging content on email marketing topics as well.