Email & Messaging

Limits: 10 sendEmail calls per transaction; daily limits for emails to external addresses (5,000 per org per day for single emails in most editions); emails are sent only if the transaction commits.

34.1 Sending email

Limits: 10 sendEmail calls per transaction; daily limits for emails to external addresses (5,000 per org per day for single emails in most editions); emails are sent only if the transaction commits. Attach files with Messaging.EmailFileAttachment or setEntityAttachments(contentVersionIds).

Messaging.reserveSingleEmailCapacity(2);       // fail fast if daily limit would be exceeded

Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setToAddresses(new List<String>{ '[email protected]' });
mail.setCcAddresses(new List<String>{ '[email protected]' });
mail.setSubject('Your order has shipped');
mail.setHtmlBody('<p>Hello! Your order <b>A-1</b> is on its way.</p>');
mail.setPlainTextBody('Hello! Your order A-1 is on its way.');
mail.setOrgWideEmailAddressId(owaId);          // send from a verified org-wide address
mail.setSaveAsActivity(false);

// template-based email (merge fields from a Contact/Lead/User + related record)
Messaging.SingleEmailMessage tmpl = new Messaging.SingleEmailMessage();
tmpl.setTemplateId(templateId);
tmpl.setTargetObjectId(contactId);              // recipient + merge source
tmpl.setWhatId(orderId);                        // related record for merge fields

List<Messaging.SendEmailResult> results = Messaging.sendEmail(
    new List<Messaging.SingleEmailMessage>{ mail, tmpl }, false);   // allOrNone = false

34.2 Inbound email services

Configure under *Setup → Email Services*: assign the class, accepted senders, and the context user; Salesforce generates an email address. (Email-to-Case is the no-code alternative for cases.)

global class SupportEmailHandler implements Messaging.InboundEmailHandler {
    global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email,
                                                           Messaging.InboundEnvelope envelope) {
        Messaging.InboundEmailResult result = new Messaging.InboundEmailResult();
        try {
            insert new Case(Subject = email.subject, Description = email.plainTextBody,
                            SuppliedEmail = email.fromAddress, Origin = 'Email');
            result.success = true;
        } catch (Exception e) {
            result.success = false;
            result.message = 'Could not create case: ' + e.getMessage();
        }
        return result;
    }
}

34.3 Custom and in-app notifications

Also: Chatter posts via ConnectApi.ChatterFeeds.postFeedElement or FeedItem DML, and Slack integration via Flow actions or the Slack Apex SDK (availability varies; check current docs).

CustomNotificationType type = [SELECT Id FROM CustomNotificationType WHERE DeveloperName = 'Deal_Alert' LIMIT 1];
Messaging.CustomNotification n = new Messaging.CustomNotification();
n.setNotificationTypeId(type.Id);
n.setTitle('Big deal closed');
n.setBody('Opportunity Acme Expansion closed for $1.2M');
n.setTargetId(oppId);                                   // record to open on click
n.send(new Set<String>{ ownerId });                     // user, group, or queue IDs