Creating Custom Email Rest API Apex class in Salesforce using SingleEmailMessage class and C# example code to consume the Api
My web application was integrated with Salesforce and I had a requirement to use Salesforce as Email Client, but with a Rest API approach to send the email message to Salesforce. Salesforce provides SingleEmailMessage class for sending emails. Explanation about its methods can be found at the link :
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_email_outbound_single.htm.
Below is the custom Apex Rest API class code for sending email using the SingleEmailMessage class :
To handle the email basic requirements, create the custom emailmessage class object which can be used to store the sender information, recipient information like to, cc etc. and message body. Then create class email request for receiving the complete email object that will comprise of email message object, its attachments, and embedded objects. Where attachments and embedded objects are handled as key-value Map objects, where key contains the filename and Value is the blob data format of the attachment/embedded.
@RestResource(urlMapping='/ForwardEmail/*') global with sharing class SendEmailClass { public class EmailRequest { EmailMessage emailMessage; Map<String,Blob> attachmentObjects; //used to receive the attachment files as Blob data along with filename as Key value Map<String,Blob> embeddedObjects; //used to receive the embedded images as Blob data with filename as Key value } public class EmailMessage { public String Sender{ get;set; } public String CC { get; set; } public String To { get; set; } public String Subject { get; set; } public String Body { get; set; } } public class EmailStatus { public Boolean Success{ get;set; } public String ErrorMsg { get; set; } } @HttpPost global static String SendEmail(string jsonString) { EmailRequest objWhole= (EmailRequest)System.JSON.deserialize(jsonString, EmailRequest.class); EmailMessage obj=objWhole.emailMessage; EmailStatus emailStatus= new EmailStatus(); String response; Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage(); String[] toSplitArray = obj.To.split(';'); //split the multiple To email addresses and store in string array. message.toAddresses=toSplitArray; message.optOutPolicy = 'FILTER'; message.subject = obj.Subject; String htmlbody= obj.body; message.htmlBody= htmlbody; Messaging.SingleEmailMessage[] messages = new List<Messaging.SingleEmailMessage> {message}; Messaging.SendEmailResult[] results = Messaging.sendEmail(messages); ……………… // Code to handle attachments & embedded Images- discussed below step by step if (results[0].success) { emailStatus.Success=true; emailStatus.ErrorMsg=''; // return success on successful email operation } else { emailStatus.Success=false; emailStatus.ErrorMsg=results[0].errors[0].message;// return error on exception in email operation. } response=System.JSON.serialize(emailStatus);// send the email response success/unsuccess to the requesting web application return response; } }
C# Sample Code to consume the email rest API :
We need to prepare the EmailRequest JSON Object to send a request to Salesforce Custom email Rest API and accordingly we create the following the Classes :
public class EmailMessage // Email Message Class corresponding to Apex Email Message Class { public string Sender { get; set; } public string CC { get; set; } public string To { get; set; } public string Subject { get; set; } public string Body { get; set; } } public class EmailRequest // Email Request Class corresponding to Apex Email Request Class { public EmailMessage emailMessage; public Dictionary<string, byte[]> attachmentObjects; public Dictionary<string, byte[]> embeddedObjects; // Bytes array corresponding to Blob Object in Salesforce }
To Handle attachments, corresponding to Map Object In Salesforce, we can use Dictionary Object Containing the Key – Value Pairs(Key as a filename and Values as bytes array containing bytes from that file).
a.Preparing the EmailMessage
The code below uses some sample values to prepare the EmailMessage Object:
EmailMessage Email = new EmailMessage(); Email.Sender=’My Name’; Email.To= ‘someone@yahoo.com’; Email.Subject=’My Demo Email’; Email.Body=’<html> <body>some html content here</body> </html>’;
b. Preparing attachments Object/Embedded Images Object
Suppose I have a List of string type that contains the path of files which I am using as attachments (let’s say List<string> files) and I have to store this in attachment Dictionary Object as Key and corresponding file bytes as Value:
Dictionary<string, byte[]> attachments = new Dictionary<string, byte[]>(); foreach(string filepath in files) { byte[] buff = null; FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); BinaryReader br = new BinaryReader(fs); buff = File.ReadAllBytes(filePath); string filename= Path.GetFileName(filepath); attachments.Add(filename, buff); }
Similarly, we can prepare embedded Images Object in this way.
Using the EmailMessage object, attachments and embedded images, prepare the complete EmailRequest Object to be sent in the proper JSON format as follows:
EmailRequest emailRequest = new EmailRequest() { emailMessage = Email, attachmentObjects = attachments, embeddedObjects = embeddedImages }; String jsonStringEmail = JsonConvert.SerializeObject( new { jsonString =JsonConvert.SerializeObject(emailRequest ) });
To send the email using Salesforce: we need to login into Salesforce to get access token, which we will use to send email from our web app:
Use the URL of the email API service which will as follows:
String uri= “ <instance-url of salesforce>/services/apexrest/ForwardEmail ”;
Send the email jsonstring object in Post Request as follows :
using (var client = new HttpClient()) { client.Timeout = new TimeSpan(1, 0, 0); client.BaseAddress = address; client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", <access_token>); StringContent content = new StringContent(jsonStringEmail, Encoding.UTF8,"application/json"); Task<HttpResponseMessage> taskResponse = client.PostAsync("", content); taskResponse.Wait(); HttpResponseMessage response = taskResponse.Result; if (response.IsSuccessStatusCode) { Task<string> taskread = response.Content.ReadAsStringAsync(); taskread.Wait(); result = taskread.Result; // return and display some success message to user "Email has been sent successfully } else { Task<string> taskread = response.Content.ReadAsStringAsync(); taskread.Wait(); result = taskread.Result; // return and display Unsucess message. }
Handle Attachments, embedded Images, and some Useful Customizations through Salesforce Email
Handling the attachments:
In the email request class object, attachment filename list and corresponding bytes data were stored as Map Object attachmentObjects as String Key and Blob Value Pair.
Multiple files can be attached to the email message object as follows:
List<Messaging.Emailfileattachment> efaList = new List<Messaging.Emailfileattachment>(); Messaging.Emailfileattachment efa ; // Create attachment Object if(objWhole.attachmentObjects.size()>0) { // Use the Blob Object to set the body of attachment and key to set the filename for (String key: objWhole.attachmentObjects.keySet()) { efa = new Messaging.Emailfileattachment(); efa.setFileName(key); // set the Key as filename efa.setBody(objWhole.attachmentObjects.get(key)); - Statement-1* // set the attachment body with Blob Object value efaList.add(efa); // Add the attachment to the attachment List Object } message.setFileAttachments(efaList); // Add the attachments List to the message Object. }
Note: One important thing here is to handle empty document case, for example, our text document is empty, so in this case, Blob Object will be null and it throws null exception while setting the attachment body in – Statement-1*
We can handle this exception by using space and retrieving its Blob Value, Thus we can send it as Empty document.
if(objWhole.attachmentObjects.get(key)!=null) { // Check if Blob Value is null efa.setBody(objWhole.attachmentObjects.get(key)); } else { Blob emptybody=Blob.valueof(' '); // Add single space to send as empty document efa.setBody(emptybody); // Add Blob Object of new line character }
Handling the embedded images:
Suppose our web application email editor contains the embedded images in the body. The issues with salesforce email class are that it does not provide a solution to send embedded images into a body. Neither the base64 encoded file string works nor it provides the way to handle the embedded images by setting Content-id in the header of the attachment. Other custom ways that we can use to handle the embedded images are.
a. Send these as inline attachment by setting the setInline property to true.
b. Secondly, we can store the file in our salesforce(or in our web application) and replace the image source with the URL of that stored file.
a. Send as inline attachment:
To send as inline attachment, we can follow the same steps as we use for creating normal attachments, but along with setting the Inline property to true as follows:
efa.setInline(true);
b. Storing the file as document in salesforce and replacing the image src with the Url of the file:
In this case, I need the folder id of the folder in which I want to save my Images and that must be public so that this can be accessed from outside email as source link in the image tag :
for (String key: objWhole.embeddedObjects.keySet()) { Blob image =objWhole.embeddedObjects.get(key); String newFileName=datetime.now()+key.Substring(key.IndexOf('/')+1); Document doc = new Document(); doc.Name = newFileName; doc.IsPublic = true; doc.body =image; doc.folderId = '00l46000001GnBV'; //your folder id insert doc; String docId=doc.Id; String url=getmyimageurl(docId); // Invoke the custom method that returns the url of file htmlbody= htmlbody.replaceAll(key,url); } message.htmlBody =htmlbody; global static String getmyimageurl (String docId) { document doc = [Select LastModifiedDate from Document where Id=:docId limit 1]; Organization org = [SELECT id, Name FROM Organization]; Id orgId = org.Id; String oid=orgId; Long lastModTimeStamp=doc.LastModifiedDate.getTime(); // fetch the Last modified date time in timestamp format using getTime() conversion return 'https://core.na11.content.force.com/servlet/servlet.ImageServer?id=' + docId +'&oid='+orgId+'&lastMod='+lastModTimeStamp+''; }
https://core.na11.content.force.com is a sample URL specific to your Salesforce Org’s document.
Other than this, image URL is composed of document id, Org Id of Salesforce, and Last modified of that file in timestamp format.
Note: One more important thing that I observed is, If the attachments total size exceeds its standard size, then it sends some of the attached documents as Html attachment. This html attachment redirects to the Link, which will give the attachment link as downloadable. In Other words, that file gets automatically stored in the Salesforce and send to the user as Html Link Page for downloading the file.
Below are the screenshots showing how the HTML link appears:
Html Page provides us the downloadable link as shown below:
Customize the Sender Information in Email :
Sometimes we need to customize the sender Information that is to be displayed in the email. As an example, I had the requirement to set the sender email as ‘noreply@salesforceuser.com’.
In a general way, it appears as:
MyDisplayName <myemail@gmail.com> via nb005mg5xiok89.46-rhcreay.na10.bnc.salesforce.com
We can Customize the Sender Information in the email in two ways :
1. Changing the Display Name :
Suppose I want to set the noreply@salesforceuser.com’ in place of Sender’s Display Name or with some Other email address or username as an example that I received from EmailMessage Object’s Sender attribute.
Then I can customize the display name as follows:
- message.setSenderDisplayName(‘noreply@salesforce.com’);
Or message.setSenderDisplayName(objWhole.Sender);
This will only change the display name indicated as MyDisplayName in the Sender Information.
2. Using the Organization-wide email address :
The second way to customize the sender information is using OrgWideEmailAddress:
In this case, information is stored in the OrgWideEmailAddress Salesforce object where a user can insert the information of email address and a display name which is set commonly for all emails.
And then Id of the record will be used to set the property of email message object as follows :
OrgWideEmailAddress orwideemail=[Select id from OrgWideEmailAddress where Address=’noreply@salesforceuser.com’]; message.setOrgWideEmailAddressId(orwideemail.Id);
You can create a record in OrgWideEmailAddress Object as follows:
Go to Setup -> Administration Setup -> Email Administration -> Organization-Wide Addresses
Create record by filling the email address as ‘noreply@salesforceuser.com’ and set the display name(say ‘Salesforce User’).
It will ask for verification of email address on this email address.
Based on the Id of the record passed in the message, display name and email address will be automatically set in the message object.
* Disadvantage, in this case, is common display name and email address will be used for all the users and we can not modify the display name for individual users.