Skip to main content

Hi everyone,

I’m using a Scheduled Flow that calls an Invocable Apex action to send contract expiration reminder emails.

The flow passes the correct Contract Ids to Apex, and the Apex code creates Messaging.SingleEmailMessage records using setToAddresses() for both the Relationship Manager (User email) and Party Contact (Contact email).

Here is the email sending part of my Apex code:

public class ContractReminderController {    // Wrapper class to accept a collection of Contract Ids from Flow    public class ContractRequest {        @InvocableVariable(required=true)        public List<Id> contractIds;    }    @InvocableMethod(label='Send Contract Expiration Reminders')    public static void sendReminders(List<ContractRequest> requests) {        if (requests == null || requests.isEmpty()) {            return;        }        Set<Id> contractIds = new Set<Id>();        // Collect all Contract Ids from the Flow collection        for (ContractRequest req : requests) {            if (req.contractIds != null) {                contractIds.addAll(req.contractIds);            }        }        if (contractIds.isEmpty()) {            return;        }        // Query Contracts        List<Contract__c> contracts = [            SELECT Id,                   Name,                   Contract_End_Date__c,                   Send_Expiration_Reminder_Days_Before__c,                   Relationship_Manager__c,                   Relationship_Manager__r.Name,                   Relationship_Manager__r.Email,                   Party_Contact__c,                   Party_Contact__r.Name,                   Party_Contact__r.Email            FROM Contract__c            WHERE Id IN :contractIds        ];        List<Messaging.SingleEmailMessage> emailList = new List<Messaging.SingleEmailMessage>();        for (Contract__c con : contracts) {            // Relationship Manager Email            if (con.Relationship_Manager__c != null &&                String.isNotBlank(con.Relationship_Manager__r.Email)) {                Messaging.SingleEmailMessage rmEmail = new Messaging.SingleEmailMessage();                rmEmail.setToAddresses(new List<String>{                    con.Relationship_Manager__r.Email                });                rmEmail.setSubject('Action Required: Contract Expiration Reminder - ' + con.Name);                rmEmail.setPlainTextBody(                    'Dear ' + con.Relationship_Manager__r.Name + ',\n\n' +                    'This is a reminder that the contract "' + con.Name +                    '" is scheduled to expire in ' +                    String.valueOf(con.Send_Expiration_Reminder_Days_Before__c) +                    ' days.\n\n' +                    'Contract Details:\n' +                    '- Contract Name: ' + con.Name + '\n' +                    '- End Date: ' + String.valueOf(con.Contract_End_Date__c) + '\n\n' +                    'Please review the contract and initiate the renewal or closure process before the expiration date.\n\n' +                    'Regards,\n' +                    'Contract Management System'                );                emailList.add(rmEmail);            }            // Party Contact Email            if (con.Party_Contact__c != null &&                String.isNotBlank(con.Party_Contact__r.Email)) {                Messaging.SingleEmailMessage contactEmail = new Messaging.SingleEmailMessage();                contactEmail.setToAddresses(new List<String>{                    con.Party_Contact__r.Email                });                contactEmail.setSubject('Upcoming Contract Expiration Notice - ' + con.Name);                contactEmail.setPlainTextBody(                    'Dear ' + con.Party_Contact__r.Name + ',\n\n' +                    'This is a reminder that the contract "' + con.Name +                    '" is scheduled to expire in ' +                    String.valueOf(con.Send_Expiration_Reminder_Days_Before__c) +                    ' days.\n\n' +                    'Contract Details:\n' +                    '- Contract Name: ' + con.Name + '\n' +                    '- End Date: ' + String.valueOf(con.Contract_End_Date__c) + '\n\n' +                    'If you wish to continue the agreement, please contact your Relationship Manager to discuss the renewal process before the contract expires.\n\n' +                    'Regards,\n' +                    'Contract Management System'                );                emailList.add(contactEmail);            }        }        // Send all emails        if (!emailList.isEmpty()) {            Messaging.sendEmail(emailList);        }    }}

The Apex debug log shows:

  • Messaging.sendEmail() returns Success: true
  • The Salesforce Email Log File shows Mail Event = D (Delivered) for the recipient email addresses

However, the emails never appear in either the Gmail inbox or the company Outlook inbox (including Spam/Junk folders).

A simple Anonymous Apex email using the same setToAddresses() approach is delivered successfully.

Has anyone seen a case where emails sent from an Invocable Apex method triggered by a Scheduled Flow are marked as Delivered in the Email Log File but are not received by the recipients? Are there any limitations, sender context differences (Automated Process), or additional settings I should check?

Thanks! 

 

#Salesforce Developer

 

@* Salesforce Developers *

5 risposte
  1. 4 ago, 06:35

    I found the solution.

    The issue was not with the Flow or the Invocable Apex logic. The emails were being sent from a Scheduled Flow, which runs as the Automated Process user. Although Messaging.sendEmail() returned success and the Email Log File showed Delivered, the recipients were not receiving the emails.

    The fix was to send the emails using a verified Organization-Wide Email Address.

    I updated the Apex code to retrieve the Organization-Wide Email Address dynamically and set it as the sender:

    OrgWideEmailAddress owea = [

    SELECT Id

    FROM OrgWideEmailAddress

    LIMIT 1

    ];

    email.setOrgWideEmailAddressId(owea.Id);

    After setting the From Address to a verified Organization-Wide Email Address, the reminder emails were delivered successfully to the recipients.

    Hopefully this helps anyone facing the same issue with Scheduled Flows and Invocable Apex email delivery.

0/9000