EWS item IDs in Microsoft Graph: converting stored IDs and keeping them stable

Applications store EWS item IDs in their own databases: the ticket that came from this message, the appointment that belongs to this booking. Those IDs have to keep pointing at the same items after the move to Microsoft Graph.

Convert the IDs you have stored

Microsoft Graph has one call for this, translateExchangeIds. It takes up to 1,000 IDs of one format and returns them in another:

POST https://graph.microsoft.com/v1.0/users/{user}/translateExchangeIds
Content-Type: application/json

{
  "inputIds": ["AAMkAGI2..."],
  "sourceIdType": "ewsId",
  "targetIdType": "restId"
}
  • All IDs in one call have to come from the same mailbox, and the mailbox is in the address of the request. An application that stored IDs without their mailbox has to find out which mailbox each one belongs to first.
  • With application permissions the call needs User.Read.All.
  • The formats are ewsId, restId (the default Graph ID), entryId, immutableEntryId and restImmutableEntryId.

Graph IDs change when an item moves

EWS item IDs behave the same way: an ID changes when its item moves to another folder. Code that already looks an item up again after a move keeps working.

Graph also has immutable IDs, which EWS did not have. With the header below, Graph returns and accepts IDs that stay the same while the item stays in its mailbox, whatever folder it is in:

Prefer: IdType="ImmutableId"
  • The header applies to one request. Send it with every request, including the one that creates a subscription and the first one of a delta query.
  • An immutable ID changes when the item moves to an archive mailbox, or is exported and imported again.
  • Messages, attachments, events, event messages and contacts have immutable IDs. Folders do not need them; folder IDs do not change.
  • To move a database to immutable IDs, call translateExchangeIds with targetIdType set to restImmutableEntryId. It also accepts ewsId as the source, so both conversions can happen in one pass.

What ConvertId did that Graph does not

EWS ConvertId knows more formats than translateExchangeIds: HexEntryId, StoreId, OwaId and EwsLegacyId have no Graph counterpart. The binary formats Graph does return are base64 made safe for URLs, with a digit at the end that counts the removed padding characters. Code that needs a hex entry ID, for example to build a link for Outlook, decodes entryId and encodes the bytes as hex itself.

The mailbox is no longer inside the ID

An EWS ID carries its mailbox, and Exchange routes the request by it. A delegate who opened someone else’s Inbox can keep working with the bare IDs. In Graph every request names the mailbox in its address, /users/{user}/messages/{id}, and an ID sent to the wrong mailbox is not found. Store the mailbox next to every ID.

Calendars and series

  • The ID Graph lists for a calendar under /calendars is not the translated EWS folder ID of that calendar. Do not assume the two match: list the calendars in Graph and store those IDs.
  • For an occurrence of a series, iCalUId in Graph includes the date of the occurrence. EWS returns the UID of the series for every occurrence. Code that matches occurrences to a series by UID has to allow for that.

Change keys

The EWS change key has a counterpart in Graph: the changeKey property and the @odata.etag of the item. Send the ETag in an If-Match header to get the behavior of ConflictResolutionMode.NeverOverwrite. There is no counterpart to AutoResolve, where Exchange merged the fields that did not conflict.

What Sunsetless EWS does

With Sunsetless EWS the application keeps using EWS IDs, and the IDs already in its database stay valid, because the library translates them on every call. ConvertId is answered inside the library for all six formats. IDs from another mailbox work once the application has named that mailbox in the same process, as the compatibility table describes.

Sources

More guides