I'm sending emails like this:

Messaging.SingleEmailMessage m = new Messaging.SingleEmailMessage();
m.setToAddresses( new String[]{'heim@elastify.eu'} );
m.setSubject(       'test' ); 
m.setHtmlBody(      '...' );
m.setWhatId(        anyObjectId );
Messaging.EmailFileAttachment efa = new Messaging.EmailFileAttachment();
efa.setBody( pdfBlob );
efa.setContentType('application/pdf'); // does not work: (cv.FileType);
m.setFileAttachments( new Messaging.EmailFileAttachment[]{ efa });
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { m });

Now this perfectly work and the sent email shows up at the record with anyObjectId in the activity tab.

It even shows the paper-clip icon for the attachment. Only that the attachment itself is not visible:

enter image description here

It should look like this (created by sending an email OUT using the LEX UI):

enter image description here

I'm pretty sure that there is a ContentDocument, ContentVersion and a ContentDocumentLink (aka "Share") missing. Looking at the ContentDocument records of emails sent via the LEX-UI it should look like:

enter image description here

Now I would gladly manually add the missing ContentDocument, ContentVersion and a ContentDocumentLink, but only I can't because I can't get the Id of the EmailMessage as described in this question: How to get the Id of an EmailMessage after sending a SingleEmailMessage with Messaging.SendEmail()?

Any ideas on how to solve this?

  • Anyone ever find a good solution to this? I have the same problem. I can send the email via Apex with an attachment using setFileAttachments method, but can't get email Id to store attachment to the Email stored in Salesforce. – Mike Paisner Oct 10 '20 at 19:45

The only solution I could figure at all is to sadly forget about the incomplete EmailMessage Salesforce has been created for us and start over from scratch by inserting a brand new EmailMessage having proper attachments.

This is very sad, because

  • it's not even easy to delete the incomplete one with out a trigger and some kind of flagging it. So I just forget about it an let it rot.
  • currently letting it rot doesn't much because I removed the line m.setWhatId( anyObjectId ); so that the crippled record is not showing up on the parent record.
  • now my new EmailMessage has attachments, but lost the paper-clip indicating the attachments. Strangely the one without attachments Salesforce is creating has that paper-clip.

Im still looking for a better and cleaner solution on this.

@Salesforce is that overall behavior "as designed" and considered a feature - or a bug? I would love not to have to do all this on my end. It feels wrong.


The trick is to insert a ContentVersion first and then use the Messaging.SingleEmailMessage.setEntityAttachments method with the ContentVersion Id. The attachment goes out with the email and is stored in Salesforce. Sample below attaches a PDF. It is not bulkified, but you get the idea:

ContentVersion cv = new ContentVersion();
cv.VersionData = <yourBlobHere>;
cv.Title = 'myTitle';
cv.PathOnClient = 'myTitle.pdf';
cv.Description = 'myDescription';
cv.isMajorVersion = true;
insert cv;

Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
// Set your mail parameters...
mail.setEntityAttachments(new list<Id> {cv.Id});

List<Messaging.SingleEmailMessage> listEmails = new List<Messaging.SingleEmailMessage>(mail);
List<Messaging.SendEmailResult> listMailResults = Messaging.sendEmail(listEmails, false);
  • This works perfectly, you just need to create your ContentDocumentLinks etc after you've inserted the ContentVersion as normal if you want to relate the attachment files to more than just the EmailMessage e.g. if you want to directly relate them to more than just the 'WhatId'. I can post examples if needed. – Matthew Whisker Jan 22 at 16:54

You can get the email message Id on a trigger on EmailMessage. Then you can add manually the missing relationships.

  • it is not easy to do so, because in the trigger context, I missing the following infos: a) if this email has to be processed or not (one those create by the code above should be) b) the attachments are not available in the trigger context. I think a) could be solved but b) not. – Uwe Heim Jul 11 '18 at 15:05
  • But the attachments should be queryable, no? Even if you need to wait for the email's trigger context to finish, you can get an async method to run on the created records. Seems an awful lot of work, but it is the only way I can think of solving this problem. – Renato Oliveira Jul 11 '18 at 15:24
  • 1
    I doubt that the attachments are stored at all. Expressis verbis I think neither the ContentDocument, nor the ContentDocumentVersion, nor the ContentDocumentLink will be created but ONLY the EmailMessage, just having the Boolean HasAttachments set to true. My approach now is to delete the EmailMessage created by Salesforce and constructing the whole quartet of entities (EM,CD,CS,CDL) and proper relations in my code. Way easier as working in a trigger context but still not nice... i think either should Salesforce do EVERYTHING or NOTHING - but not just half of the work. – Uwe Heim Jul 11 '18 at 15:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.