Send All notes in Email through Workflow with Record link

As we all know in CRM notes are stored in annotation entity, like records. For example if you have added 3 notes in account there will be three different record in annotation entity related to that account. In my current project we got requirement to send Notification when a new notes is added to Opportunity. This can be easily achieved by OOB workflow. But while send notes we also have to include all previous notes for that opportunity, this can’t achieved through OOB workflow directly. Also we need to send link of that opportunity in notification which is again can’t be achieved through OOB workflow. So I decided to write a custom workflow to implement both these functionality below are the detail steps and code.
Step1. Create Sequence workflow library project from Visual Studio 2008.
Step2. Give appropriate name to your custom workflow, I am using AttachedNotes.
Step3. Right click on Design and select view code.
Step4. You can use below code
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.Workflow;
using System.IO;
using System.Text;
namespace AttachNotes
[CrmWorkflowActivity(“IncludeHistorialNotes”, “HistorialNotesUtility”)]
public sealed partial class NoteWF: SequentialWorkflowActivity
Guid _OpportunityGUID = Guid.Empty;
public NoteWF()
public static DependencyProperty HistoryNotesProperty = DependencyProperty.Register(“HistoryNotes”, typeof(string), typeof(NoteWF));
public string HistoryNotes
get { return (string)GetValue(HistoryNotesProperty); }

SetValue(HistoryNotesProperty, value);

public static DependencyProperty UrlProperty = DependencyProperty.Register(“Url”, typeof(string), typeof(NoteWF));
public string Url
get { return (string)base.GetValue(UrlProperty); }
set { base.SetValue(UrlProperty, value); }

public static DependencyProperty ReturnUrlProperty = DependencyProperty.Register(“ReturnUrl”, typeof(string), typeof(NoteWF));
public string ReturnUrl
get { return (string)base.GetValue(ReturnUrlProperty); }
set { base.SetValue(ReturnUrlProperty, value); }

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
string _NotestMessage = string.Empty;
IContextService contextService = (IContextService)executionContext.GetService(typeof(IContextService));
IWorkflowContext context = contextService.Context;
ICrmService crmService = context.CreateCrmService(true);

//Get RegardingID
annotation _CurrentNotes = (annotation)crmService.Retrieve(EntityName.annotation.ToString(), context.PrimaryEntityId, new ColumnSet(new string[] { “objectid” }));
//GetAll Related Notes
_NotestMessage = GetNotes(_CurrentNotes.objectid.Value, crmService);

this.HistoryNotes = _NotestMessage;
string fullUrl = string.Concat(this.Url, _CurrentNotes.objectid.Value.ToString());
this.ReturnUrl = string.Format(@”{1}“, fullUrl, “Click To Open Opportnity”);

return ActivityExecutionStatus.Closed;

private string GetNotes(Guid Regarding, ICrmService _iService)

StringBuilder _NotesMessage = new StringBuilder();

ColumnSet _ColsToFetch = new ColumnSet(new string[] { “notetext”, “objectid”, “subject”,”createdby”, “createdon” });

RetrieveMultipleResponse retrieved = null;
ConditionExpression _Condition = new ConditionExpression();
_Condition.Operator = ConditionOperator.Equal;
_Condition.AttributeName = “objectid”;
_Condition.Values = new object[] { Regarding };

FilterExpression _Filter = new FilterExpression();
_Filter.FilterOperator = LogicalOperator.And;
_Filter.Conditions.AddRange(new ConditionExpression[] { _Condition });

QueryExpression query = new QueryExpression();

query.ColumnSet = _ColsToFetch;
query.Criteria = _Filter;
query.EntityName = EntityName.annotation.ToString();
RetrieveMultipleRequest retrieve = new RetrieveMultipleRequest();
retrieve.Query = query;
retrieve.ReturnDynamicEntities = true;
retrieved = (RetrieveMultipleResponse)_iService.Execute(retrieve);

if (retrieved.BusinessEntityCollection.BusinessEntities.Count > 0)
for (int i = 0; i < retrieved.BusinessEntityCollection.BusinessEntities.Count; i++)
{//collect all notes regarding the same opportunity
DynamicEntity _Notes = (DynamicEntity)retrieved.BusinessEntityCollection.BusinessEntities[i];
if (_Notes.Properties.Contains(“subject”))

if (_Notes.Properties.Contains(“notetext”))


return _NotesMessage.ToString();


Step5. Sign your custom workflow assembly.
Step6. Register your workflow assembly.
Step7. Create workflow, select Note entity and select to run when record is created.
You will get your custom workflow section like below

Step8. Select your custom workflow step
Step9. Click on Set Properties and fill URL for opportunity

Step10.Select Send Email step and click on set Properties and set properties accordingly.
Step11. Select your custom workflow from look for picklist and select both output variable to provide url link and History notes.

Step12. Save and Publish workflow.
Next time when you will add note to opportunity you will get email with history notes and URL for that opportunity record.

2 thoughts on “Send All notes in Email through Workflow with Record link

  1. Chris


    Is this customization for CRM Online? Or is it for a server hosted version only? I am not quite sure how to register the workflow assembly. Any assistance would be much appreciated. Thank you.

    1. mahenderpal Post author

      you can only use this for onpremise version, As MS CRM 4.0 online does not support plugins/custom WF, let me know if you have any query.


Leave a Reply

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