I am using email service and apex to automatically create records on custom object when a client sends an email to the address defined in email service. The record does get created and we can also send response from Salesforce. Both emails gets linked to the original record.

Now, the problem occurs when the client response to the email received from Salesforce. This creates a new record instead linking the email to the original record.

Is it possible to get the threadId of the incoming email? or any other alternative way to link the email to the original record?

Below is my apex code:

public class TT_CaseEmailService implements Messaging.InboundEmailHandler {

public Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, 
                                                           Messaging.InboundEnvelope envelope) 
        Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();   
        TT_Case__c ttCase = new TT_Case__c();
        ttCase = createTTCase(email);
        createTTEmailMessage(ttCase.Id, email);
        if(email.textAttachments != null){
            createTextAttachment(ttCase.Id, email);
        if(email.binaryAttachments != null){
            createBinaryAttachment(ttCase.Id, email);

          return result;
    private static TT_Case__c createTTCase(Messaging.InboundEmail email){

        TT_Case__c tt = new TT_Case__c();
        tt.Requestor_Email__c = email.fromAddress;
        tt.Requestor_Name__c = email.fromName;
        tt.Description__c = email.htmlBody;
        tt.Opened__c = Datetime.now();
        tt.OwnerId = [SELECT Id FROM Group WHERE Type = 'queue' AND DeveloperName='TT_Support' LIMIT 1].Id;
        tt.Subject__c = email.subject;
        insert tt;
        return tt;
    private static void createTextAttachment(String parentRecordId, Messaging.InboundEmail emailMessage){
        List<Attachment> textAttachemntList = new List<Attachment>();
        for (Messaging.Inboundemail.TextAttachment tAttachment : emailMessage.textAttachments) {
            Attachment attachment = new Attachment();
            attachment.Name = tAttachment.fileName;
            attachment.Body = Blob.valueOf(tAttachment.body);
            attachment.ParentId = parentRecordId;
            insert textAttachemntList;
    private static void createBinaryAttachment(String parentRecordId, Messaging.InboundEmail emailMessage){
        List<Attachment> binaryAttachment = new List<Attachment>();
        for (Messaging.Inboundemail.BinaryAttachment bAttachment : emailMessage.binaryAttachments) {
            Attachment attachment = new Attachment();
            attachment.Name = bAttachment.fileName;
            attachment.Body = bAttachment.body;
            attachment.ParentId = parentRecordId;
            insert binaryAttachment;
    private static void createTTEmailMessage(String ttCaseId, Messaging.InboundEmail email){
        EmailMessage em = new EmailMessage();

        em.Subject = email.Subject;
        em.HtmlBody = email.htmlBody;
        em.Incoming = true;
        em.Status = '1';
        em.ToAddress = (email.ToAddresses == null || email.ToAddresses.isEmpty()) ? '' : String.join(email.ToAddresses, ',');
        em.FromAddress = email.fromAddress;
        em.FromName = email.fromName;
        em.RelatedToId = ttCaseId;
        em.CcAddress = (email.ccAddresses == null || email.ccAddresses.isEmpty()) ? '' : String.join(email.ccAddresses, ',');
        insert em;

  • Is the email that customer replies to the one created in method createTTEmailMessage or via the Case AutoResponse rules or other SFDC automation?
    – cropredy
    Commented Feb 23, 2023 at 18:11
  • it uses the same class/method createTTEmailMessage
    – Marjan
    Commented Feb 24, 2023 at 9:44

1 Answer 1


The first thing I would do is replace this line

em.RelatedToId = ttCaseId;

With this line:

em.ParentId = ttCaseId;

The email threading only works if SFDC thinks the email is associated with a Case - and this is done via EmailMessage.ParentId. This is the field that is set using OOTB E2C as well as any emails sent from the Case Feed Publisher.

EmailMessage.ParentId was created many moons ago as initially, EmailMessage could only be a child of Case. Later, SFDC opened up emails (Enhanced Emails) to other objects, using the EmailMessage.RelatedToId

  • thank you for your answer. I am not using the standard Case object here. Will this approach work custom object as well?
    – Marjan
    Commented Feb 26, 2023 at 12:27
  • ah - I should have seen that in your OP. I don't think email threading works at all for custom objects - you need to generate a threadID yourself and add it to every initial email, then look for that threadID in all incoming email to locate the relevant custom object. The threadId could be as simple as the TT_Case__c.Id field (perhaps obfuscated and wrapped in easy-to-find-delimiters)
    – cropredy
    Commented Feb 27, 2023 at 18:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .