Sep 12, 2025

Attach PDF file programmatically to Email and send

Let's see how PDF file is attached to an Email and sending programmatically. Lets see why this is important.

1) Email Templates cannot be created with an attachment. (Ironically!)
2) If we attach programmatically we can change the attachment based on logic if required.

In Summery, we can initiate Email in Draft state, then attach the PDF and then send.

Initiation and sending the e-mail part is explained in Programmatically create a draft Email using Email Template (C#).

Lets see the code

// Initiate Email in Draft state

// Create Attachment
byte[] embededPdf = LoadEmbeddedPdf("PluginProject.Resources.ApplicationForm.pdf")
Guid attachmentId = CreatePdfAttchment(OrgSvc,emailId, Convert.ToBase64String(embededPdf));

// Send Email

// Methods to use
private byte[] LoadEmbeddedPdf(string resourceName)
{
    var assembly = Assembly.GetExecutingAssembly(); 
    using (Stream stream = assembly.GetManifestResourceStream(resourceName))
    {
        if (stream == null)
            throw new FileNotFoundException("Embedded PDF file not found: " + resourceName);
        using (MemoryStream ms = new MemoryStream())
        {
            stream.CopyTo(ms);
            return ms.ToArray();
        }
    }
}

public Guid CreatePdfAttchment(IOrganizationService OrgSvc, Guid emailId, string base64Pdf)
{
    Entity attachment = new Entity("activitymimeattachment");
    attachment["subject"] = "Application Form";
    attachment["filename"] = "ApplicationForm.pdf";
    attachment["mimetype"] = "application/pdf";
    attachment["body"] = base64Pdf;
    attachment["attachmentnumber"] = 1;
    attachment["objectid"] = new EntityReference("email", emailId);
    attachment["objecttypecode"] = "email";
    return OrgSvc.Create(attachment);
}

There are two important things here;

1. PDF is saved within the Project and path needs to be passed correctly in below standard.

<Project Namespace><Folder><filename>.pdf

So you will realize when I pass PluginProject.Resources.ApplicationForm.pdf, my project namespace is  PluginProject, folder name is Resources and pdf file name is ApplicationForm.pdf.

2. Go to properties of the PDF file (In VS) and do below setting.
    


Email Template to populate dynamic values from any entity

Email Templates of Dynamics 365/ Dataverse is very useful yet its one big limitation is it only can dynamically populate values of a one Entity type. Word Document templates don't have that limitation but it is actually for generation of a document not to use in Email content. Alternatively, Word Document Templates can be used to generate a document and attach to email which is widely done.

Today we will see how to programmatically enhance the usual Email Template to have any value. Below are the steps we do.

1) When creating Email Template, introduce special tags for dynamics values we plan to populate from other entities

2) Create email with Template in Draft status.

3) Replace special tags we introduce (Template will have many other dynamic fields as usual from specific Entity type it designed to fetch data from)

4) Send the Email

Please refer Programmatically create a draft Email using Email Template (C#) for steps 1 and 3.

Lets check how code would flow with the newly introduced method. Obviously values you need to populate should be query first to replace the tags.

// Step 2: Create Draft Email
// email object is defined here

// Step 3: Replace special tags
ReplaceEmailContent(email, "{{Coordinator}}", <Value need to populate>);
ReplaceEmailContent(email, "{{ContractId}}", <Value need to populate>);

// Step 4: Send Email

// Method to call
public Entity ReplaceEmailContent(Entity email, string tagName, string tagValue)
{
    var secondValue = email.Attributes.Values.ElementAt(1);
    if (secondValue is string strValue)
    {
        var replaced = strValue.Replace(tagName, tagValue);
        var key = email.Attributes.Keys.ElementAt(1);
        email.Attributes[key] = replaced;
    }
    return email;
}

Same way, if we need to add dynamics tags to Subject of the email, that's also possible. For that, use below method.

public Entity ReplaceEmailSubject(Entity email, string tagName, string tagValue)
{
    var secondValue = email.Attributes.Values.ElementAt(0);
    if (secondValue is string strValue)
    {
        var replaced = strValue.Replace(tagName, tagValue);
        var key = email.Attributes.Keys.ElementAt(0);
        email.Attributes[key] = replaced;
    }
    return email;
}