Working with CRM Emails

On April 15, 2007, in Customization, Dynamics CRM, by Mitch Milam

Last Thanksgiving, while waiting to have dinner with some friends, I decided to set down and worth through the process of:

1) Sending an email from within CRM, using the CRM web services.

2) Using an email template.

3) And including an attachment to the email.

Pretty simple, I thought. After all, the SDK has some pretty good examples of doing all of these things; all I had to do was to just stitch the pieces together to make it work.  Yeah, right.

Three hours later, I finally figured out the last of the issues with my process and will present the solution here today.  As mentioned, most of what you're seeing is merely modified versions of the CRM SDK examples.

There is a lot to cover on this topic so the article will be pretty long.

Creating a CRM Email Template

This section is pretty much straight out of the example found in instantiatetemplate.cs in the SDK.

Basically, as with most CRM Entities, you create the properties required by the object, add them to the object instance, then instruct CRM to create the Entity.

 
   1:  // STEP 1: CREATE A TEMPLATE
   2:  // Define the body and subject of the email template 
   3:  // in XML format.
   4:  string bodyXml =
   5:  "<?xml version=\"1.0\" ?>"
   6:  + "<xsl:stylesheet xmlns:xsl="
   7:  + "\"http://www.w3.org/1999/XSL/Transform\""
   8:  + " version=\"1.0\">"
   9:  + "<xsl:output method=\"text\" indent=\"no\"/>"
  10:  + "<xsl:template match=\"/data\">"
  11:  + "<![CDATA["
  12:  + "001: This message is to notify you that a new account "
  13:  + "has been created."
  14:  + "]]></xsl:template></xsl:stylesheet>";
  15:   
  16:  string subjectXml =
  17:  "<?xml version=\"1.0\" ?>"
  18:  + "<xsl:stylesheet xmlns:xsl="
  19:  + "\"http://www.w3.org/1999/XSL/Transform\" "
  20:  + "version=\"1.0\">"
  21:  + "<xsl:output method=\"text\" indent=\"no\"/>"
  22:  + "<xsl:template match=\"/data\">"
  23:  + "<![CDATA[001: New account notification]]>"
  24:  + "</xsl:template></xsl:stylesheet>";
  25:   
  26:  string presentationXml = 
  27:  "<template><text><![CDATA["
  28:  + "This message is to notify you that a new account "
  29:  + "has been created."
  30:  + "]]></text></template>";
  31:   
  32:  string subjectPresentationXml = 
  33:  "<template><text><![CDATA[New account notification]]>"
  34:  + "</text></template>";
  35:   
  36:  // Create the template object and populate
  37:  // the required properties
  38:  template template = new template();
  39:  template.title = "New account";
  40:  template.body = bodyXml;
  41:  template.subject = subjectXml;
  42:  template.presentationxml = presentationXml;
  43:  template.subjectpresentationxml = subjectPresentationXml;
  44:   
  45:  // The type of Entity this template references.
  46:  template.templatetypecode = new EntityNameReference();
  47:  template.templatetypecode.Value = EntityName.account.ToString();
  48:   
  49:  // Is this a personal or system template
  50:  // true = personal, false = system.
  51:  template.ispersonal = new CrmBoolean();
  52:  template.ispersonal.Value = false;
  53:   
  54:  // specify the owner of the template
  55:  template.ownerid = new Owner();
  56:  template.ownerid.Value = user.UserId;
  57:  template.ownerid.type = EntityName.systemuser.ToString();
  58:   
  59:  // Specify who created the template
  60:  template.createdby = new Lookup();
  61:  template.createdby.Value = user.UserId;
  62:  template.createdby.type = EntityName.systemuser.ToString();
  63:   
  64:  // Add the template to CRM
  65:  templateID = myCRMService.Create(template);
  66:              
 

Creating an Email based on a Template

The next step in our process is to actually create an email based on either the template we created in Step 1, or based on an existing template ( using the template's GUID ). 

 
   1:  // STEP 2: CREATE AN EMAIL BASED ON THE TEMPLATE
   2:  // Create a template instantiation request.
   3:  InstantiateTemplateRequest instTemplate = 
   4:  new InstantiateTemplateRequest();
   5:   
   6:  // The email template to use.
   7:  // You can either specify the ID of an existing 
   8:  // template within your system or utilize the 
   9:  // template you created in step 1.
  10:  instTemplate.TemplateId = 
  11:      new Guid("0AA4C2A0-247B-DB11-8667-00142A05B544");    
  12:  //instTemplate.TemplateId = templateID;
  13:   
  14:  // The object that this email is regarding.
  15:  instTemplate.ObjectId = accountID;
  16:  instTemplate.ObjectType = EntityName.account.ToString();
  17:   
  18:  // Execute the request to create an email message 
  19:  // from the template.
  20:  InstantiateTemplateResponse instTempResp = 
  21:  (InstantiateTemplateResponse)myCRMService.Execute(instTemplate);
  22:   
  23:  // Extract the newly created email object from the
  24:  // BusinessEntities collection.
  25:  email email = 
  26:  (email)
  27:  instTempResp.BusinessEntityCollection.BusinessEntities[0]; 
  28:   
  29:  // Specify who the email is FROM
  30:  activityparty from = new activityparty(); 
  31:  from.partyid = new Lookup();
  32:  from.partyid.Value = user.UserId;
  33:  from.partyid.type = EntityName.systemuser.ToString();
  34:   
  35:  // Specify who the email is TO
  36:  activityparty to = new activityparty(); 
  37:  to.partyid = new Lookup();
  38:  to.partyid.Value = accountID;
  39:  to.partyid.type = EntityName.account.ToString();
  40:   
  41:  // Add the TO and FROM parties to the email
  42:  email.from = new activityparty[] {from};
  43:  email.to = new activityparty[] {to};
  44:   
  45:  // Direction specifies if the email is Outgoing ( true )
  46:  // or Incoming ( false ).
  47:  CrmBoolean direction = new CrmBoolean();
  48:  direction.Value = true;
  49:  email.directioncode = direction;
  50:   
  51:  // Create the email in CRM
  52:  Guid emailID = myCRMService.Create(email);

 

Things I learned

I learned a couple of things working with the SDK examples here:

1) The SDK sample, sendemail.cs, uses the following code to send an email

 

   1:  // Create an email message.
   2:  email email = new email();
   3:  email.torecipients = "someone@example.com";
   4:  email.subject = "Please disregard";
   5:  email.description = "This is a test message. Please disregard";

While this is perfectly functional code, in most likelihood, it will cause issues for your users.  When you specify the email's recipients via the torecipients property, you haven't actually associated the email to anything or anyone within CRM.  This means when you look at your activities, all you see is an email like this:

If you specify ActivityParties for the To and From properties, you will see a normal CRM lookup field with the values you specified displayed, like this:

2) Sendmail.cs also has the following code that it uses to send the email:

 

   1:  TargetCreateEmail targetCreate = new TargetCreateEmail();
   2:  targetCreate.Email = email;
   3:   
   4:  CreateRequest request = new CreateRequest();
   5:  request.Target = targetCreate;
   6:   
   7:  CreateResponse response = (CreateResponse)service.Execute(request);
   8:  Guid emailID = response.id;

Again, functional, but this code does the same thing:

 

   1:  // Create the email in CRM
   2:  Guid emailID = myCRMService.Create(email);

 

Adding an Attachment to an Email

Adding attachments is very similar to any other CRM operation, as long as you understand which objects need to be attached to what objects and where. 

   1:  // STEP 3: Add an attachment
   2:   
   3:  // Create a new Attachment object.
   4:  // Attach it to the email we created in Step 2.
   5:  activitymimeattachment attachment = 
   6:    new activitymimeattachment();
   7:  attachment.activityid = new Lookup();
   8:  attachment.activityid.Value = emailID;
   9:  attachment.activityid.type = EntityName.email.ToString();
  10:   
  11:  // This is the first attachment.
  12:  attachment.attachmentnumber = new CrmNumber();
  13:  attachment.attachmentnumber.Value = 1;
  14:   
  15:  // Create the Accountment in CRM.
  16:  Guid attachmentId = myCRMService.Create(attachment);
  17:   
  18:  // variable to hold our attachment data.
  19:  string data; 
  20:   
  21:  // Create an instance of StreamReader to read text from a file.
  22:  // The using statement also closes the StreamReader.
  23:  using (StreamReader sr = new StreamReader("temp.txt")) 
  24:  {
  25:      data = sr.ReadToEnd();
  26:  }
  27:   
  28:  // Encode the data using base64.
  29:  byte[] byteData = new byte[data.Length];
  30:  byteData = System.Text.Encoding.UTF8.GetBytes(data);
  31:  string encodedData = System.Convert.ToBase64String(byteData);
  32:   
  33:  // Create the request object to upload the file to CRM.
  34:  UploadFromBase64DataActivityMimeAttachmentRequest upload = 
  35:      new UploadFromBase64DataActivityMimeAttachmentRequest();
  36:   
  37:  // Set the properties of the request object.
  38:  upload.ActivityMimeAttachmentId = attachmentId;
  39:  upload.FileName = "temp.txt";
  40:  upload.MimeType = "text/plain";
  41:  upload.Base64Data = encodedData;
  42:   
  43:  // Upload the file to CRM.
  44:  UploadFromBase64DataActivityMimeAttachmentResponse uploaded = 
  45:      (UploadFromBase64DataActivityMimeAttachmentResponse)
  46:                 myCRMService.Execute(upload);
  47:   
  48:  bool success2 = false;
  49:   
  50:  activitymimeattachment attachmentCheck = 
  51:      (activitymimeattachment)myCRMService.Retrieve(
  52:          EntityName.activitymimeattachment.ToString(),
  53:          attachmentId, 
  54:          new AllColumns());
  55:   
  56:  if (attachmentCheck.filename != null)
  57:  {
  58:      success2 = true;
  59:  }
 

 

Things I learned

1) You need to create an attachment object then assign the activityid for the attachment to the ID of the email we created in Step 2.

2) Each attachment added must have a unique number that identifies it, and I'm assuming, its location within the list of attachments.  To my knowledge, CRM doesn't automatically assign values to the attachment number property. I think that I did a little research into the ability to determine the number of attachments for a specific CRM Entity, I don't recall the results of my research.  It would be nice to have a GetNextAttachmentNumber function or something like it, built into the CRM API.

3) Files must be Uploaded into CRM before they can actually be available.  As part of the Upload object creation process, you instruct CRM where the uploaded file should reside using the ActivityMimeAttachmentId property as seen on line 38.

 

Sending the Email

Finally we get to the actual sending email part, which as you can see from the code, is actually the easiest part of the whole operation:

 

   1:  // STEP 4: Send the email.
   2:  SendEmailRequest req = new SendEmailRequest();
   3:   
   4:  // ID of the email
   5:  req.EmailId = emailID;
   6:   
   7:  // You may specify your own tracking token
   8:  // or if blank, CRM will add the normal tracking token
   9:  req.TrackingToken = string.Empty;
  10:   
  11:  // When IssueSend is set to true, the email will be sent.
  12:  // Otherwise, it will be placed in Activities for later
  13:  // sending.
  14:  req.IssueSend = true;
  15:   
  16:  // Send the email message.
  17:  SendEmailResponse res = 
  18:      (SendEmailResponse)myCRMService.Execute(req);
  19:   
  20:  #region check success
  21:   
  22:  if(!res.Subject.Equals(""))
  23:  {
  24:      success = true;
  25:  }
  26:   
  27:  #endregion
  28:   

 

Things I learned

As you can see from the comments within the code, there are a few items of interest here.

1) You can provide your own TrackingToken, or use the one CRM provides.  However, you CAN NOT leave that property set to a null value.  That will result in an error.  Just make it blank ( which of course, is not null ).

2) If IssueSend is set to True, it will send the email. Otherwise, it will just place it into your Activities list.

 

Conclusion

Well, that's about it.  I originally started this process because I needed to create a Workflow Assembly that would allow me to specify not only a pre-defined email template but to also allow me to attach a file to the email.  It took me a whole lot longer that I expected because there were a lot more little "gotchas" than I expected.  Hopefully, I've saved you a bit of time should you need to create a similar solution.

If anyone has any questions or comments, please let me know.

 

35 Responses to Working with CRM Emails

  1. Simon Cartwright says:

    email email =(email)instTempResp.BusinessEntityCollection.BusinessEntities[0];

    This line fails as there is no "BusinessEntityCollection" attribute of the "InstantiateTemplateResponse" object.
    Any ideas why this would be so, I'm running CRM 3.0 Update rollup 2.
    The CRM SDK help file also confirms that this attribute does not exist.

  2. mitch says:

    Simon,

    If you don't have BusinessEntityCollection, then you pretty much can't work with any CRM Entity, because that is the base class for most things.

    It sounds to me like your WSDL is not complete.

    Take a look in your web references folder of your project and open the CRMService folder ( or whatever you called that reference ).

    If the reference.cs file found in that directory is NOT in excess of 2MB, you don't have a good WSDL download.

    If that is the case, here's how you fix it:

    1) Open a browser pointing to your CRM web site.
    2) In VS, expand the Web References folder.
    3) Right-click on the CRMService entry and Click Refresh

    If you now look at the reference.cs file you should see it occupy about 2.4MB of space. Rebuild your application and see what happens next.

    Mitch

  3. Jack says:

    Hi Mitch,

    Excellent sample…One of the only full working examples I've found.

    One question though, instead of setting IssueSend to True and sending the email, or False and having it placed in the Activities list, is it possible to pop it up for editing and sending..? I am writing a VB.NET WinForms app…

    Cheers,
    Jack

  4. mitch says:

    Hi Jack,

    Not really. Server-side development doesn't relate much to the client-side experience.

    About the only thing you could do would be to launch an IE session and pass it a URL that you've constructed using the base activities URL ( it's in the SDK ) and adding the ID of the email activity was created.

    That being said, it's probably only going to work properly if you are using the code from a Windows App or a ASP.NET application. If you're creating a workflow or callout, that tactic will not work since you don't know where the user is.

    Mitch

  5. Simon Cartwright says:

    Thanks Mitch,
    My issue was the Reference, I selected it and chose "Update Web Reference" from the context menu. Hey presto it now works.

    Microsoft should update their online documentation.
    Simon

  6. xtravert says:

    about torecipients property
    is it absolutуly useless? how can I send email to someone who is not a systemuser?

  7. Steve Osborn says:

    Mitch,

    I found your explaination of sending email very good.

    I can send email to any established entity withing CRM but my
    email.torecipients never works. It just doesn't send any mail?

    If I open the CRM standard email form and type in a valid email address it does send email just fine.

    What must I check on to see why the code never sends to the torecipients?

    Any ideas?

    Thanks much

  8. mitch says:

    Steve, are the torecipients inside the CRM system?

  9. Graham says:

    Hi Mitch,

    Have you got an example of adding attachment to email for CRM 4.0 ?

    UploadFromBase64DataActivityMimeAttachmentRequest & UploadFromBase64DataActivityMimeAttachmentResponse are no longer around.
    Thanks,

  10. mitch says:

    Hi Graham,

    Look in the CRM 4.0 SDK for the TargetCreateActivityMimeAttachment message. That should do it.

    Thanks, Mitch

  11. Graham says:

    Thanks Mitch, I now have that working,
    Thanks very much.

    PS Chancing my luck!! Any ideas on how to add multiple recipients to the To: and CC fields?

    Graham

  12. mitch says:

    just add more people to the activityparty list.

  13. Greg says:

    Hi Mitch

    I've drawn on this post a lot recently. I am constructing a process to allow bulk emails with attachments. Most of the challenge is handling the user experience rather than the mechanics of constructing the emails, however I'm encountering a problem:

    I'm create an instance of the SDK email object and setting description, subject and sender all OK. I then create a quick campaign programmatically and populate Activity = my email instance, ListId to a valid marketing list guid, FriendlyName (some text) then, importantly I set Propagate = false and sendEmail = false. My understanding is that Propagate = false shuld attempt to actually send the email messages out once they are created. sendEmail determines (i think) whether a notification is sent to the QC owner.

    My problem? If Propagate = true, QC is created, so too are all emails (all look fine with correct content etc) but not sent out – as expected.

    If Propagate = false, QC is created then aborted, no emails are created and error message, deep in logs, states "Must specify a valid Template Id, ErrorCode: -2147220717".

    Any idea why I need a templateId? I'm a bit confused as they SDK is all a bit vague about the Propagate/sendEmail properties…

  14. Andrew says:

    Hi Mitch,
    I used one of your examples about a year ago in developing a plug in to send email templates. It was very helpful. However, I have a situation where we've upgraded to CRM 4.0 and what worked in 3.0 no longer does.

    We have cases where we must use to.addressused and cc.addressused in order to use an email string rather than a Guid. In the case of cc.addressused I was able to string together multiple email addresses seperated by a semi-colon and plug that in. However, since our upgrade I can no longer do that. cc.addressused only allows for a single address.

    I've never used torecipients so am unfamiliar with this. It doesn't appear based on the posts above that it would solve my issue. Have you ever seen this before and if so do you have any thoughts?

    Thanks, Andrew

  15. mitch says:

    Andrew,

    I have not seen that particular problem but for multiple emails, I do know that the to and from recipients works fine.

    Look up PartyList in the SDK for more info.

    Mitch

  16. Raj says:

    Any example on how to add multiple recipients to the To: and CC fields? Thanks

  17. mitch says:

    Raj, please review the SDK for the email.to field for more info. Here is how it works:

    35: // Specify who the email is TO
    36: activityparty to = new activityparty();
    37: to.partyid = new Lookup();
    38: to.partyid.Value = accountID;
    39: to.partyid.type = EntityName.account.ToString();
    40:
    41: // Add the TO and FROM parties to the email
    42: email.from = new activityparty[] {from};
    43: email.to = new activityparty[] {to};

  18. Raj says:

    Mitch, thanks for the quick response. I am able to send emails to single recipients. But having trouble sending to multiple in the To: field. In the post (12) above, you mentioned "add more people to the activityparty list".

    Can you please give an example to that. I really appreciate your help. Thanks.

  19. Raj says:

    Here is the code for adding multiple recipients…
    // RSUsers is an array with dynamic GUIDs of users
    List partyList = new List();
    foreach (string RSUsers in systemUsers)
    {
    activityparty party = new activityparty();
    party.partyid = new Lookup();
    party.partyid.type = EntityName.systemuser.ToString();
    party.partyid.Value = new Guid(RSUsers);
    partyList.Add(party);
    }
    Email.to = partyList.ToArray();

  20. Stalin says:

    email.to is working fine from the following code
    currentUserId = new Guid("8F163A26-DC21-DD11-95BD-0013723C6CB7");
    activityparty[] toparty1 = new activityparty[1];
    toparty1[0] = new activityparty();
    toparty1[0].partyid = new Lookup();
    toparty1[0].partyid.type = EntityName.systemuser.ToString();
    toparty1[0].partyid.Value = currentUserId;
    em.to = toparty1;

    but for email.torecipients getting following error
    "Invalid share id". Any idea how to send mail non CRM users mail id

  21. Ken Bagwell says:

    Hi Mitch, thx for the nice example. Do you have any idea on how to direct a created email like this to a particular Queue?

    Thanks again,

    Ken

  22. Andrew says:

    Mitch,

    Thanks for responding to my post. Took me a while to get back to your response as I was working on other things. I'm not sure I fully follow it though. Without going into the business cases, here's what I was doing which I had working in 3.0:

    activityparty to = new activityparty();
    to.addressused = "user@domain.com";
    email.to = new activityparty[] { to };

    email.cc = new activityparty();
    cc .addressused = "user2@domain.com; user3@domain.com";
    email.cc = new activityparty[] { cc };

    I could string together as many email addresses as I needed for recipients who were not yet contacts in our database. But when we upgraded to 4.0 this no longer worked and threw the following error:

    "Invalid e-mail address. For more information, contact your system administrator. "

    I apologize if I'm being dense but I just didn't understand your response.

    Took me a while to get back to your response as I was working on other things.
    Thanks in advance, Andrew

  23. Great sample :-) Only problem is that it's not very copy past freindly :-)

  24. LANdango says:

    Mitch,
    Great article! I have a quick question.

    Everything worked in your article as described… a beautiful thing. :-) Now I can send new account notifications and change the .description to populate the user temporary password:

    email.description = email.description.Replace("[TEMPORARYPASSWORD]", "Hello World");

    But the problem is that we need to send the email with the password, but need to strip out the password (change the .description back to what it was) and resave it in the history. This way, other CRM users cannot see the temporary password and possibly gain access.

    here's what I tried:

    //save the body in a temp variable and populate the password

    string preSendEmailBody = email.description;
    email.description = email.description.Replace("[TEMPORARYPASSWORD]", "Hello World");

    … send the email using your example…

    //now update it without the password.
    email.description = preSendEmailBody;
    crmService.Update(email);

    Of course, the .Update() method fails, but it's a generic error.

    Any ideas how I can update the email?
    Thanks!

  25. mitch says:

    Mike,

    If I read this correctly, you're trying to modify a Sent email? Is that correct? If so, then that's your problem. I don't think you can modify completed activiites. If you turn on Tracing, you can get a good description of the cause of the error.

    Mitch

  26. LANdango says:

    wow… that was fast! thanks for the reply.

    yes. we want the activity history to indicate we sent an email to the person, but we don't want a CRM user to see the password contained in the email.

    Right now, I'm looking through the CRM objects for a response object in hopes that I can get the history email by emailID and then modify that. no luck so far.

    (stoooopid question: where do I turn on tracing?)

    -m

  27. Kuttu s says:

    Hi Mitch,

    How will I use an attribute value in the message text. for example i wanrtt to use account number in the email template created through crmservice. is it possible? if then how?

  28. Mitch Milam says:

    if you are using a template, you simply need to specify the account to use. CRM will automatically take care of putting the account number into the pre-defined space in the template.

  29. Sruthi says:

    How to send an email to 2 different emails within one contact. For example, I need to send emails to email 1 and email 2 for a certain contact.

  30. amit says:

    I am using the same method,
    But it is showing me the following error.
    with id is doesnot exist or don't have authorization.

    //"\n 0x80040217\n systemuser With Id = b49b0359-61c5-df11-8385-001d9285cebd Does Not Exist\n Platform\n"

    and every time it change the ID,But when i check in my code Id with that code is not in my solution.

  31. David Withers says:

    Does anyone have a complete example for 4.0. Surprisingly this is the most complete example I found. So to the Author, thank you!

  32. Mitch Milam says:

    David. that is about a complete example for 4.0 that I can give you.

  33. Pekka says:

    You really know how to tell a story. This is a superior/complete/well-commented example – I was strugling with templates in my own codebase and your "tutorial" solved it all.

    Thanks !

  34. Yang says:

    Hi Mitch,

    Your post is very helpful. Thanks a lot!

    Meanwhile, when I try to work on attachment, I got a compile error in my code: "The type or namespace name 'UploadFromBase64DataActivityMimeAttachmentRequest' could not be found".

    It is very confuse to me. Should 'UploadFromBase64DataActivityMimeAttachmentRequest' in the SDK? Do you know where can I find its assambly?

    Thank you very much!

    Yang

  35. Mitch Milam says:

    Yang,

    That post was written in 2007 and is probably for CRM 3.0. Here is the code you need ( in the SDK ):

    http://msdn.microsoft.com/en-us/library/dd548518.aspx

    Mitch

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>