Work with conversations by using EWS in Exchange
Learn about how to find conversations, apply actions to conversations, and get items in conversations by using the EWS Managed API or EWS in Exchange.
In the context of Exchange, conversations are a way to group and manage a related set of email messages. They can also provide a way to view related messages. Exchange defines conversations based on the Message-ID value of the first email message in a thread. All replies and related messages reference the original message's Message-ID header in their References and In-Reply-To headers.
Additionally, inside the SOAP envelope, for each message received in a mailbox, Exchange sets specific properties and elements.
Table 1. Conversation properties and elements set on all email messages
EWS Managed API property | EWS element | Description |
---|---|---|
ConversationTopic |
ConversationTopic |
Contains a normalized form of the subject value that was set on the original message. This is the same as the Thread-Topic message header. This value is read-only. |
ConversationIndex |
ConversationIndex |
Represents the position of the item in the conversation. This is the same as the Thread-Index message header. This value is read-only. |
Exchange applies the same ConversationTopic value to replies to the first message and then updates the ConversationIndex value to represent the message's position relative to the original message. If the subject of the email thread changes, Exchange applies a new ConversationTopic value and new ConversationIndex values to the new conversation.
Table 2. EWS Managed API methods and EWS operations for working with conversations
Find a conversation by using the EWS Managed API
You can find conversations by using the ExchangeService.FindConversation EWS Managed API method, as shown in the following example. This example gets the first 10 conversations in the Inbox folder that have a subject that contains the word "news". The example then writes the conversation topic, last delivery time, and global unique recipient list to the console window.
This example assumes that service is a valid ExchangeService object and that the user has been authenticated to an Exchange server.
static void FindConversation(ExchangeService service)
{
// Create the view of conversations returned in the response. This view will return at most 10 results.
ConversationIndexedItemView view = new ConversationIndexedItemView(10);
// Create the query string to search for.
String queryString = "subject:news";
// Search the Inbox for conversations and return a results set with the specified view.
// This method call results in a FindConversation call to EWS.
ICollection<Conversation> conversations = service.FindConversation(view, WellKnownFolderName.Inbox, queryString);
// Examine properties on each conversation returned in the response.
foreach (Conversation conversation in conversations)
{
Console.WriteLine("Conversation Topic: " + conversation.Topic);
Console.WriteLine("Last Delivered: " + conversation.LastDeliveryTime.ToString());
ApplyConversationActions(service, conversation);
foreach (string GlUniqRec in conversation.GlobalUniqueRecipients)
{
Console.WriteLine("Global Unique Recipient: " + GlUniqRec);
}
Console.WriteLine("");
}
}
Find a conversation by using EWS
You can find conversations by using the FindConversation EWS operation, as shown in the following example. This example gets the first ten conversations in the Inbox folder that have a subject that contains the word "news". This is also the XML request that the EWS Managed API sends when you use the EWS Managed API to find a conversation.
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2013" />
</soap:Header>
<soap:Body>
<m:FindConversation>
<m:IndexedPageItemView MaxEntriesReturned="10"
Offset="0"
BasePoint="Beginning" />
<m:ParentFolderId>
<t:DistinguishedFolderId Id="inbox" />
</m:ParentFolderId>
<m:QueryString>subject:news</m:QueryString>
</m:FindConversation>
</soap:Body>
</soap:Envelope>
The server responds to the FindConversation request with a FindConversationResponse message that includes a ResponseCode value of NoError to indicate that the operation completed successfully. The response also includes the only conversation in the mailbox that has a subject that contains the word "news".
The ItemId, ChangeKey, and ConversationId elements have been shortened for readability.
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:ServerVersionInfo MajorVersion="15"
MinorVersion="0"
MajorBuildNumber="883"
MinorBuildNumber="10"
Version="V2_10"
xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FindConversationResponse ResponseClass="Success"
xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
<ResponseCode>NoError</ResponseCode>
<Conversations>
<Conversation xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
<ConversationId Id="aO2NM+Q=" />
<ConversationTopic>Today's top news headlines</ConversationTopic>
<UniqueRecipients>
<String>Sadie Daniels</String>
</UniqueRecipients>
<GlobalUniqueRecipients>
<String>Sadie Daniels</String>
</GlobalUniqueRecipients>
<UniqueUnreadSenders>
<String>Ronnie Sturgis</String>
</UniqueUnreadSenders>
<GlobalUniqueUnreadSenders>
<String>Ronnie Sturgis</String>
</GlobalUniqueUnreadSenders>
<UniqueSenders>
<String>Ronnie Sturgis</String>
</UniqueSenders>
<GlobalUniqueSenders>
<String>Ronnie Sturgis</String>
</GlobalUniqueSenders>
<LastDeliveryTime>2014-02-18T20:42:26Z</LastDeliveryTime>
<GlobalLastDeliveryTime>2014-02-18T20:42:26Z</GlobalLastDeliveryTime>
<HasAttachments>false</HasAttachments>
<GlobalHasAttachments>false</GlobalHasAttachments>
<MessageCount>1</MessageCount>
<GlobalMessageCount>1</GlobalMessageCount>
<UnreadCount>1</UnreadCount>
<GlobalUnreadCount>1</GlobalUnreadCount>
<Size>9330</Size>
<GlobalSize>9330</GlobalSize>
<ItemClasses>
<ItemClass>IPM.Note</ItemClass>
</ItemClasses>
<GlobalItemClasses>
<ItemClass>IPM.Note</ItemClass>
</GlobalItemClasses>
<Importance>Normal</Importance>
<GlobalImportance>Normal</GlobalImportance>
<ItemIds>
<ItemId Id="sVCyAAA="
ChangeKey="CQAAAA==" />
</ItemIds>
<GlobalItemIds>
<ItemId Id="sVCyAAA="
ChangeKey="CQAAAA==" />
</GlobalItemIds>
<LastModifiedTime>2014-02-18T20:42:26Z</LastModifiedTime>
<InstanceKey>AQAAAAAAAQABAAAACbFYggAAAAA=</InstanceKey>
<HasIrm>false</HasIrm>
<GlobalHasIrm>false</GlobalHasIrm>
</Conversation>
</Conversations>
<TotalConversationsInView>1</TotalConversationsInView>
<IndexedOffset>1</IndexedOffset>
</FindConversationResponse>
</s:Body>
</s:Envelope>
Apply conversation actions by using the EWS Managed API
You can apply conversation actions to a conversation by using a number of EWS Managed API methods, as shown in the following example. This example adds categories to existing items in a conversation and applies the same categories to future items in the conversation. It also shows how to enable the automatic moving of items in the conversation to a folder. In this example, items are moved to the Drafts folder.
This example assumes that service is a valid ExchangeService object and that the user has been authenticated to an Exchange server.
For a complete list of methods that apply conversation actions, see Table 2.
static void ApplyConversationActions(ExchangeService service, Conversation conversation)
{
// Create a list of categories to apply to a conversation.
List<string> categories = new List<string>();
categories.Add("Customer");
categories.Add("System Integrator");
// Apply categorization to all items in the conversation and process the request
// synchronously after enabling this rule and after all item categorization has been applied.
// This method call results in an ApplyConversationAction call to EWS.
conversation.EnableAlwaysCategorizeItems(categories, true);
// Apply an always move rule to all items in the conversation and move the items
// to the Drafts folder. Process the request asynchronously and return the response.
// immediately. This method call results in an ApplyConversationAction call to EWS.
conversation.EnableAlwaysMoveItems(WellKnownFolderName.Drafts, false);
}
Apply conversation actions by using EWS
You can apply conversation actions, such as categorize, delete, and move, by using the ApplyConversationAction operation, as shown in the following example. This example adds categories to existing items in a conversation and applies the same categories to future items in the conversation. It also shows how to enable the automatic moving of items in the conversation to a folder; in this example, items are moved to the Drafts folder. This is also the XML request that the EWS Managed API sends when you use the EWS Managed API to apply conversation actions.
The ConversationId element has been shortened for readability.
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2013" />
</soap:Header>
<soap:Body>
<m:ApplyConversationAction>
<m:ConversationActions>
<t:ConversationAction>
<t:Action>AlwaysMove</t:Action>
<t:ConversationId Id="jG6WVpg=" />
<t:ProcessRightAway>false</t:ProcessRightAway>
<t:DestinationFolderId>
<t:DistinguishedFolderId Id="drafts" />
</t:DestinationFolderId>
</t:ConversationAction>
</m:ConversationActions>
</m:ApplyConversationAction>
</soap:Body>
</soap:Envelope>
The server responds to the ApplyConversationAction request with a ApplyConversationActionResponse message that includes a ResponseCode value of NoError to indicate that the operation completed successfully.
Get items in a single conversation by using the conversation identifier in the EWS Managed API
You can get items in a conversation by using the ExchangeService.GetConversationItems EWS Managed API method. This example provides the set of conversation nodes for the first conversation in the Inbox. The item identifier, subject, and received time for each item are returned in the response, along with the conversation index and parent conversation index properties. You can use the conversation index properties to reconstruct the node hierarchy.
In this example, all conversation items in the default Deleted Items and Drafts folders are ignored.
This example assumes that service is a valid ExchangeService object and that the user has been authenticated to an Exchange server.
static void GetConversationItemsSingleConversation(ExchangeService service)
{
try
{
// Find the first item in the mailbox.
// This method call results in an FindItem call to EWS.
FindItemsResults<Item> results = service.FindItems(WellKnownFolderName.Inbox,
new ItemView(1));
// Get the conversation identifier of the item.
ConversationId convId = results.Items[0].ConversationId;
// Specify the properties that will be
// returned for the items in the conversation.
PropertySet properties = new PropertySet(BasePropertySet.IdOnly,
ItemSchema.Subject,
ItemSchema.DateTimeReceived);
// Identify the folders to ignore.
Collection<FolderId> foldersToIgnore = new Collection<FolderId>()
{ WellKnownFolderName.DeletedItems, WellKnownFolderName.Drafts };
// Request the conversation items.
// This method call results in an GetConversationItems call to EWS.
ConversationResponse response = service.GetConversationItems(convId,
properties,
null,
foldersToIgnore,
ConversationSortOrder.TreeOrderDescending);
// Get the synchronization state of the conversation.
Console.WriteLine("SyncState: " + response.SyncState);
Collection<Item> items = new Collection<Item>();
// Process each node of conversation items.
foreach (ConversationNode node in response.ConversationNodes)
{
Console.WriteLine("Parent conversation index: " + node.ParentConversationIndex);
Console.WriteLine("Conversation index: " + node.ConversationIndex);
Console.WriteLine("Conversation node items:");
// Process each item in the conversation node.
foreach (Item item in node.Items)
{
Console.WriteLine(" Item ID: " + item.Id.UniqueId);
Console.WriteLine(" Subject: " + item.Subject);
Console.WriteLine(" Received: " + item.DateTimeReceived);
items.Add(item);
}
}
}
// This exception occurs if there is an error with the service.
catch (ServiceResponseException srException)
{
Console.WriteLine(srException);
}
}
We recommend that you cache the SyncState property for subsequent requests to get items in the conversation.
Get items in many conversations by using the ConversationRequest object in the EWS Managed API
You can use the ConversationRequest object and the ExchangeService.GetConversationItems EWS Managed API method to get items from two or more conversations. This example provides a set of conversation nodes for the first two conversations in the Inbox. The item identifier, subject, and the received time for each item will be returned in the response, along with the conversation index and parent conversation index properties. You can use the conversation index properties to reconstruct the node hierarchy. This example assumes that the first two items in the Inbox are from different conversations.
In this example, all conversation items in the default Deleted Items and Drafts folders are ignored.
This example assumes that service is a valid ExchangeService object and that the user has been authenticated to an Exchange server.
static void GetConversationItemsManyConversations(ExchangeService service)
{
try
{
// Find the first two items in the Inbox. This item will be used to call the GetConversationItems operation.
// This method call results in an FindItem call to EWS.
FindItemsResults<Item> results = service.FindItems(WellKnownFolderName.Inbox, new ItemView(2));
// Get the conversation identifier of the first two items in the Inbox.
ConversationId convId1 = results.Items[0].ConversationId;
ConversationId convId2 = results.Items[1].ConversationId;
// Identify two conversation requests.
ConversationRequest convR1 = new ConversationRequest();
convR1.ConversationId = convId1;
ConversationRequest convR2 = new ConversationRequest();
convR2.ConversationId = convId2;
// Create a collection of conversations to fetch.
Collection<ConversationRequest> conversations = new Collection<ConversationRequest>();
conversations.Add(convR1);
conversations.Add(convR2);
// Specify the properties that will be returned for the items in the conversation.
PropertySet properties = new PropertySet(BasePropertySet.IdOnly,
ItemSchema.Subject,
ItemSchema.DateTimeReceived);
// Identify the folders to ignore.
Collection<FolderId> foldersToIgnore = new Collection<FolderId>()
{ WellKnownFolderName.DeletedItems, WellKnownFolderName.Drafts };
// Request the conversation items.
// This method call results in an GetConversationItems call to EWS.
ServiceResponseCollection<GetConversationItemsResponse> responses =
service.GetConversationItems(conversations, properties, foldersToIgnore,
ConversationSortOrder.TreeOrderDescending);
// Process each conversation.
foreach (GetConversationItemsResponse resp in responses)
{
// Identify the synchronization state of the conversation.
Console.WriteLine("Sync State: " + resp.Conversation.SyncState);
// Process each node in the conversation.
foreach (ConversationNode node in resp.Conversation.ConversationNodes)
{
Console.WriteLine("Parent conversation index: " + node.ParentConversationIndex);
Console.WriteLine("Conversation index: " + node.ConversationIndex);
Console.WriteLine("Conversation node items:");
// Process each item in the conversation node.
foreach (Item item in node.Items)
{
Console.WriteLine(" Item ID: " + item.Id.UniqueId);
Console.WriteLine(" Subject: " + item.Subject);
Console.WriteLine(" Received: " + item.DateTimeReceived);
}
}
}
}
// This exception occurs if there is an error with the service.
catch (ServiceResponseException srException)
{
Console.WriteLine(srException);
}
}
As a best practice, we recommend that you return only the properties that the client application requires, rather than using the FirstClassProperties option for the BasePropertySet class. We recommend that you cache the SyncState property for subsequent requests to get items in the conversation.
Get items in conversations by using the conversation identifier in EWS
You can get items in a conversation by using the GetConversationItems EWS operation. This example provides a set of conversation nodes for the first conversation in the Inbox. The item identifier, subject, and received time for each item are returned in the response, along with the conversation index and parent conversation index properties. You can use the conversation index properties to reconstruct the node hierarchy.
In this example, all conversation items in the default Deleted Items and Drafts folders are ignored.
The ConversationId element has been shortened for readability.
To get items from more than one conversation, include additional Conversation elements.
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2013" />
</soap:Header>
<soap:Body>
<m:GetConversationItems>
<m:ItemShape>
<t:BaseShape>IdOnly</t:BaseShape>
<t:AdditionalProperties>
<t:FieldURI FieldURI="item:Subject" />
<t:FieldURI FieldURI="item:DateTimeReceived" />
</t:AdditionalProperties>
</m:ItemShape>
<m:FoldersToIgnore>
<t:DistinguishedFolderId Id="deleteditems" />
<t:DistinguishedFolderId Id="drafts" />
</m:FoldersToIgnore>
<m:SortOrder>TreeOrderDescending</m:SortOrder>
<m:Conversations>
<t:Conversation>
<t:ConversationId Id="LUQFH6Q=" />
</t:Conversation>
</m:Conversations>
</m:GetConversationItems>
</soap:Body>
</soap:Envelope>
The server responds to the GetConversationItems request with a GetConversationItemsResponse message that includes a ResponseCode value of NoError to indicate that the operation completed successfully. The response also includes the ConversationNodes in the conversation.
The ItemId, SyncState, and ConversationId elements have been shortened for readability.
<s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:ServerVersionInfo MajorVersion="15"
MinorVersion="0"
MajorBuildNumber="873"
MinorBuildNumber="9"
Version="V2_9"
xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<m:GetConversationItemsResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<m:ResponseMessages>
<m:GetConversationItemsResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:Conversation>
<t:ConversationId Id="LUQFH6Q=" />
<t:SyncState>AAAAYAm1</t:SyncState>
<t:ConversationNodes>
<t:ConversationNode>
<t:InternetMessageId>&lt;994051d7c1a346efbfce8dec2cbad509
@SN2SR01MB006.com&gt;</t:InternetMessageId>
<t:ParentInternetMessageId>&lt;faa2b1df30074380abe3527b0cd18ca5
@SN2SR01MB001.com&gt;</t:ParentInternetMessageId>
<t:Items>
<t:Message>
<t:ItemId Id="AYB1NAAA="
ChangeKey="CQAAABYAAAD/oydcA+SPQZGbKWNyvNIZAAAAYCHq" />
<t:Subject>RE: Review Proposal for Tailspin Toys</t:Subject>
<t:DateTimeReceived>2014-01-02T13:15:00Z</t:DateTimeReceived>
</t:Message>
</t:Items>
</t:ConversationNode>
<t:ConversationNode>
<t:InternetMessageId>&lt;faa2b1df30074380abe3527b0cd18ca5
@SN2SR01MB001.com&gt;</t:InternetMessageId>
<t:ParentInternetMessageId>&lt;6a8e7658524b41cda7cdc3f23c1074a5
@SN2SR01MB001.com&gt;</t:ParentInternetMessageId>
<t:Items>
<t:Message>
<t:ItemId Id="lQAAAA=="
ChangeKey="CQAAABYAAAD/oydcA+SPQZGbKWNyvNIZAAAAYAu8" />
<t:Subject>RE: Review Proposal for Tailspin Toys</t:Subject>
<t:DateTimeReceived>2014-01-02T10:02:08Z</t:DateTimeReceived>
</t:Message>
</t:Items>
</t:ConversationNode>
<t:ConversationNode>
<t:InternetMessageId>&lt;bcdb185495834370a874a1e7ebedbb96
@SN2SR01MB005.com&gt;</t:InternetMessageId>
<t:ParentInternetMessageId>&lt;e52a4de6b98d484887e141da094a2ce6
@SN2SR01MB006.com&gt;</t:ParentInternetMessageId>
<t:Items>
<t:Message>
<t:ItemId Id="igAAAA=="
ChangeKey="CQAAABYAAAD/oydcA+SPQZGbKWNyvNIZAAAAYAuj" />
<t:Subject>RE: Review Proposal for Tailspin Toys</t:Subject>
<t:DateTimeReceived>2014-01-02T16:20:10Z</t:DateTimeReceived>
</t:Message>
</t:Items>
</t:ConversationNode>
<t:ConversationNode>
<t:InternetMessageId>&lt;e52a4de6b98d484887e141da094a2ce6
@SN2SR01MB006.com&gt;</t:InternetMessageId>
<t:ParentInternetMessageId>&lt;f0db3ead01db4fe087d98022149aa16f
@SN2SR01MB001.com&gt;</t:ParentInternetMessageId>
<t:Items>
<t:Message>
<t:ItemId Id="iwAAAA=="
ChangeKey="CQAAABYAAAD/oydcA+SPQZGbKWNyvNIZAAAAYAul" />
<t:Subject>RE: Review Proposal for Tailspin Toys</t:Subject>
<t:DateTimeReceived>2014-01-02T15:30:10Z</t:DateTimeReceived>
</t:Message>
</t:Items>
</t:ConversationNode>
<t:ConversationNode>
<t:InternetMessageId>&lt;f0db3ead01db4fe087d98022149aa16f
@SN2SR01MB001.com&gt;</t:InternetMessageId>
<t:ParentInternetMessageId>&lt;88b1884ecaaa4f68b081c009d827e8c6
@SN2SR01MB003.com&gt;</t:ParentInternetMessageId>
<t:Items>
<t:Message>
<t:ItemId Id="jQAAAA=="
ChangeKey="CQAAABYAAAD/oydcA+SPQZGbKWNyvNIZAAAAYAuq" />
<t:Subject>RE: Review Proposal for Tailspin Toys</t:Subject>
<t:DateTimeReceived>2014-01-02T14:20:10Z</t:DateTimeReceived>
</t:Message>
</t:Items>
</t:ConversationNode>
<t:ConversationNode>
<t:InternetMessageId>&lt;88b1884ecaaa4f68b081c009d827e8c6
@SN2SR01MB003.com&gt;</t:InternetMessageId>
<t:ParentInternetMessageId>&lt;faa2b1df30074380abe3527b0cd18ca5
@SN2SR01MB001.com&gt;</t:ParentInternetMessageId>
<t:Items>
<t:Message>
<t:ItemId Id="kAAAAA=="
ChangeKey="CQAAABYAAAD/oydcA+SPQZGbKWNyvNIZAAAAYAux" />
<t:Subject>RE: Review Proposal for Tailspin Toys</t:Subject>
<t:DateTimeReceived>2014-01-02T12:52:09Z</t:DateTimeReceived>
</t:Message>
</t:Items>
</t:ConversationNode>
</t:ConversationNodes>
</m:Conversation>
</m:GetConversationItemsResponseMessage>
</m:ResponseMessages>
</m:GetConversationItemsResponse>
</s:Body>
</s:Envelope>
Version differences
When you are using Exchange Server 2010 Service Pack 1 (SP1), the FindConversation method has fewer options available, and the FindConversation operation has fewer elements in the request.
Table 3. Exchange 2010 SP1 version support for FindConversation
EWS Managed API method | EWS elements |
---|---|
FindConversation (ViewBase, FolderId) |
IndexedPageItemView SortOrder ParentFolderId |
The GetConversationItems EWS Managed API method and the GetConversationItems EWS operation were introduced in Exchange Server 2013. Applications that target earlier versions of Exchange can only apply conversation actions to conversations, as listed in Table 2.
The FindConversation EWS Managed API method and the FindConversation EWS method are not available in the initial release version of Exchange 2010 or in Exchange 2007.