<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sunsetless</title>
    <description>The latest articles on DEV Community by Sunsetless (sunsetless).</description>
    <link>https://dev.to/sunsetless</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F15126%2Fedd6f14a-09cd-4912-97fa-37cbbd715219.png</url>
      <title>DEV Community: Sunsetless</title>
      <link>https://dev.to/sunsetless</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sunsetless"/>
    <language>en</language>
    <item>
      <title>What changes when EWS Managed API code moves to Microsoft Graph</title>
      <dc:creator>Jack from Sunsetless</dc:creator>
      <pubDate>Tue, 06 Oct 2026 13:31:49 +0000</pubDate>
      <link>https://dev.to/sunsetless/what-changes-when-ews-managed-api-code-moves-to-microsoft-graph-j2c</link>
      <guid>https://dev.to/sunsetless/what-changes-when-ews-managed-api-code-moves-to-microsoft-graph-j2c</guid>
      <description>&lt;p&gt;If you are porting EWS Managed API code to Microsoft Graph, you will find a Graph call for most EWS operations. Many of those calls behave differently from what your EWS code expects.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we test
&lt;/h2&gt;

&lt;p&gt;Each of our 186 test scenarios is a real piece of EWS Managed API code. We run every scenario on Exchange Online twice, once through EWS and once through Microsoft Graph v1.0, and compare the two answers field by field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which of these affect your code
&lt;/h2&gt;

&lt;p&gt;Start from the EWS operations your application calls. The EWS usage report in the Microsoft 365 admin center lists them per application. Our free tool &lt;a href="https://github.com/sunset-less/ews-scan" rel="noopener noreferrer"&gt;ews-scan&lt;/a&gt; (MIT license) reads them from the compiled assemblies and prints the Graph call and what to watch for next to each one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; Sunsetless.EwsScan
ews-scan C:&lt;span class="se"&gt;\p&lt;/span&gt;ath&lt;span class="se"&gt;\t&lt;/span&gt;o&lt;span class="se"&gt;\t&lt;/span&gt;he&lt;span class="se"&gt;\a&lt;/span&gt;pplication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sending mail
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The sent copy goes to Sent Items
&lt;/h3&gt;

&lt;p&gt;EWS lets the caller choose where the copy of a sent message is saved (&lt;code&gt;SendAndSaveCopy&lt;/code&gt; with a folder) or save none (&lt;code&gt;SendOnly&lt;/code&gt;). In Graph, sending a draft with &lt;code&gt;send&lt;/code&gt; puts the copy in Sent Items. &lt;code&gt;sendMail&lt;/code&gt; can skip the copy with &lt;code&gt;saveToSentItems: false&lt;/code&gt;, and neither call takes a folder. To file the copy elsewhere, move it after Exchange has saved it, which takes a moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending returns no ID
&lt;/h3&gt;

&lt;p&gt;Both Graph calls answer 202 with an empty body. Microsoft's documented way to find the sent message is to create the draft with the header &lt;code&gt;Prefer: IdType="ImmutableId"&lt;/code&gt;, send it, and read the message by the same ID afterwards. The copy may not be there right away.&lt;/p&gt;

&lt;h3&gt;
  
  
  Read receipts cannot be suppressed
&lt;/h3&gt;

&lt;p&gt;Marking a message as read through Graph sends the read receipt its sender asked for. EWS has &lt;code&gt;SuppressReadReceipts&lt;/code&gt; for that case. We found no Graph switch for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meetings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Attendees are always notified
&lt;/h3&gt;

&lt;p&gt;With EWS an organizer can save, change or delete a meeting without telling anyone (&lt;code&gt;SendInvitationsMode.SendToNone&lt;/code&gt;, &lt;code&gt;SendCancellationsMode.SendToNone&lt;/code&gt;). Calendar sync code relies on that. Graph sends the invitation, the update or the cancellation whenever the organizer creates a meeting with attendees, changes its time, place, subject, body or attendees, or deletes it, and no request parameter turns this off. Glen Scales describes &lt;a href="https://glenscales.substack.com/p/workarounds-for-sendinvitationsmode" rel="noopener noreferrer"&gt;workarounds&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Responses take fewer fields
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;accept&lt;/code&gt;, &lt;code&gt;decline&lt;/code&gt;, &lt;code&gt;tentativelyAccept&lt;/code&gt; and &lt;code&gt;cancel&lt;/code&gt; in Graph take a comment and a flag that says whether to send the response. The extra fields of the EWS response objects (Cc, Bcc, sensitivity, receipt requests, the folder for the saved copy) cannot be passed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The change key of a new meeting is stale
&lt;/h3&gt;

&lt;p&gt;Graph saves a meeting first and sends it afterwards, and sending changes the item. The change key you get back from the create call no longer matches on the next read. Code that updates with &lt;code&gt;ConflictResolutionMode.NeverOverwrite&lt;/code&gt; right after creating has to read the item again first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding items
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Filter and sort order are tied together
&lt;/h3&gt;

&lt;p&gt;When a request has both &lt;code&gt;$filter&lt;/code&gt; and &lt;code&gt;$orderby&lt;/code&gt;, Microsoft documents three rules: the properties you sort by must also appear in the filter, in the same order, and before any other property. A request that breaks them fails with &lt;code&gt;InefficientFilter&lt;/code&gt;. An EWS &lt;code&gt;FindItem&lt;/code&gt; with both a restriction and a sort order translates only when you add conditions that are always true for the sort properties.&lt;/p&gt;

&lt;h3&gt;
  
  
  Search cannot be filtered or sorted
&lt;/h3&gt;

&lt;p&gt;An AQS query string becomes &lt;code&gt;$search&lt;/code&gt;. Microsoft documents that a search returns at most 1,000 results, sorted by the date the message was sent. In our tests it could not be combined with &lt;code&gt;$filter&lt;/code&gt; or &lt;code&gt;$orderby&lt;/code&gt;. An EWS call that passes both a query string and a restriction has to be split, or one half evaluated in your own code. For contacts, events and tasks we found no server-side text search at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Graph does not filter or sort
&lt;/h3&gt;

&lt;p&gt;These work in an EWS restriction or sort order, and we found no Graph counterpart for them: the body text, the size, an item class matched by prefix, DisplayTo and DisplayCc, bit masks on extended properties, and sorting by an extended property. The choices are to page through the folder and evaluate them yourself, or to change what the feature does.&lt;/p&gt;

&lt;h3&gt;
  
  
  Small things that change results
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Sorting by subject: Graph ignores prefixes such as RE: and FW:, while EWS sorts by the full subject.&lt;/li&gt;
&lt;li&gt;A filter that only asks whether an extended property exists needs a condition on its value, for example &lt;code&gt;ep/value ne null&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Binary extended properties cannot be used in a filter.&lt;/li&gt;
&lt;li&gt;Results of &lt;code&gt;$search&lt;/code&gt; come without usable change keys and ignore &lt;code&gt;$expand&lt;/code&gt;, so reading extended properties takes a second request by ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Items that are not mail
&lt;/h2&gt;

&lt;p&gt;In EWS every item is reached the same way. In Graph each kind has its own API, and &lt;code&gt;/messages&lt;/code&gt; shows mail only.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calendar items, tasks and contact groups do not appear under &lt;code&gt;/messages&lt;/code&gt;. Attachments of a meeting are reached through &lt;code&gt;/events&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Events, contacts and tasks have no &lt;code&gt;move&lt;/code&gt; or &lt;code&gt;copy&lt;/code&gt;. Moving one means creating it again in the new folder, with a new ID and a new creation time.&lt;/li&gt;
&lt;li&gt;Tasks live in Microsoft To Do. A To Do task has no extended properties, and its progress is a status, so a percent complete of 25 has nowhere to go.&lt;/li&gt;
&lt;li&gt;Contact groups (personal distribution lists) have no API in Graph v1.0. Microsoft's roadmap lists them as planned.&lt;/li&gt;
&lt;li&gt;A post (&lt;code&gt;IPM.Post&lt;/code&gt;) cannot be created; Graph answers &lt;code&gt;ErrorObjectTypeChanged&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Contacts, events and tasks in Deleted Items do not show up in their own APIs, and &lt;code&gt;/messages&lt;/code&gt; does not list them either.&lt;/li&gt;
&lt;li&gt;When you attach an item that has attachments of its own, Graph accepts the request and drops the inner attachments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Synchronization and notifications
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Stored sync states do not carry over
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;SyncState&lt;/code&gt; from EWS means nothing to Graph. After the switch the application does one full synchronization per folder and stores delta links from then on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Calendar delta needs a date window
&lt;/h3&gt;

&lt;p&gt;Messages have a delta query per folder. For events, Graph v1.0 documents delta only on a calendar view, a range between two dates, and it returns the occurrences of a series there. &lt;code&gt;SyncFolderItems&lt;/code&gt; on a calendar folder has no direct counterpart.&lt;/p&gt;

&lt;h3&gt;
  
  
  No streaming notifications
&lt;/h3&gt;

&lt;p&gt;EWS pull notifications map to delta queries and push notifications map to Graph subscriptions with a webhook. The streaming connection has no counterpart. See &lt;a href="https://sunsetless.com/guides/ews-streaming-notifications-graph" rel="noopener noreferrer"&gt;EWS streaming notifications in Microsoft Graph&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delta reports net changes
&lt;/h3&gt;

&lt;p&gt;Exchange logs every change, and EWS notifications replay them. A delta query compares two states. An item created and deleted between two polls produces nothing. A hard delete shows up as a deletion, where EWS reported a move to Recoverable Items. Delta does not say whether a message is new or changed; you work that out from what you have seen before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifiers and time
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Stored EWS IDs can be converted with &lt;code&gt;translateExchangeIds&lt;/code&gt;, up to 1,000 per call. Graph IDs change when an item moves unless you ask for immutable IDs. See &lt;a href="https://sunsetless.com/guides/ews-item-ids-graph" rel="noopener noreferrer"&gt;EWS item IDs in Microsoft Graph&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;An EWS ID names its mailbox, and Exchange routes by it. Graph wants the mailbox in the URL of every request, so store the mailbox next to the ID.&lt;/li&gt;
&lt;li&gt;Graph returns times to the second, so &lt;code&gt;DateTimePrecision.Milliseconds&lt;/code&gt; has no Graph counterpart.&lt;/li&gt;
&lt;li&gt;Graph takes time zones by name. A custom time zone built with &lt;code&gt;TimeZoneInfo.CreateCustomTimeZone&lt;/code&gt;, which the EWS Managed API sends as a full definition, is not accepted.&lt;/li&gt;
&lt;li&gt;Graph returns every body as HTML unless the request says &lt;code&gt;Prefer: outlook.body-content-type="text"&lt;/code&gt;. &lt;code&gt;BodyType.Best&lt;/code&gt; in EWS returned text for a plain-text message.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mailbox settings
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Out of office: Graph does not show whether external replies are allowed (&lt;code&gt;AllowExternalOof&lt;/code&gt;), and it keeps no language for the reply text.&lt;/li&gt;
&lt;li&gt;Inbox rules: the conditions WithinDateRange, FromConnectedAccounts, ItemClasses and MessageClassifications and the actions SendSMSAlertToRecipients and ServerReplyWithMessage have no Graph counterpart. A rule update replaces all conditions, exceptions and actions of the rule.&lt;/li&gt;
&lt;li&gt;Delegates: Graph covers only the calendar part of a delegate's access, through calendar permissions. Delegate permissions on Inbox, Tasks, Contacts, Notes and Journal have no Graph counterpart.&lt;/li&gt;
&lt;li&gt;User configuration objects: Graph cannot list or create them today; Microsoft's roadmap plans them for the end of 2026. Categories and working hours have their own APIs.&lt;/li&gt;
&lt;li&gt;Other people's calendars: an application gets free/busy information only, with no further detail.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What has no Graph API
&lt;/h2&gt;

&lt;p&gt;Microsoft's retirement page on Microsoft Learn has a roadmap of the gaps. On the day we checked it, general access to public folders, to Microsoft 365 group mailboxes and to discovery mailboxes was listed as not coming. Access to online archives, and export and import of public folder items, were planned for the last quarter of 2026. The page also says not to expect a capability that is not on the roadmap before EWS is switched off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/migrate-exchange-web-services-api-mapping" rel="noopener noreferrer"&gt;Microsoft Learn: EWS to Microsoft Graph API mappings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/deprecation-of-ews-exchange-online" rel="noopener noreferrer"&gt;Microsoft Learn: retirement of EWS in Exchange Online&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;First published at &lt;a href="https://sunsetless.com/guides/ews-to-graph-differences" rel="noopener noreferrer"&gt;sunsetless.com/guides/ews-to-graph-differences&lt;/a&gt;. Based on our own test results and Microsoft's documentation.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>microsoftgraph</category>
      <category>api</category>
    </item>
  </channel>
</rss>
