Share via

Microsoft Graph Webhook — conversationId changes when external party replies, breaking email thread grouping for case management

Zeeshan Aameer 0 Reputation points
2026-05-07T20:55:56.49+00:00

Title: Microsoft Graph Webhook — conversationId changes when external party replies, breaking email thread grouping for case management


Background / Context

We are building a case management system where we listen to a client's Outlook mailbox via Microsoft Graph change notifications (webhooks). When a new email arrives, we create or update a Case based on the email thread. The goal is: all emails belonging to the same conversation should be linked to the same Case.

Current Behavior — The Problem

When an external party (e.g. someone on Gmail, Yahoo, or another org's Exchange) replies to an existing thread, Microsoft Graph assigns a new conversationId to that reply, even though it is clearly part of the same conversation (the RFC 2822 References and In-Reply-To headers correctly point back to the original message).

Similarly, we have observed that internetMessageId can differ per message since each MTA stamps its own Message-ID, so it cannot be used as a thread grouping key — only as a per-message dedup key.

Example flow:

  1. External sender emails client internetMessageId : [******@externalsender.com] conversationId : AAQkADFmM... (Exchange assigns this)
  2. Client replies from Outlook internetMessageId : [******@clientdomain.com] conversationId : AAQkADFmM... (SAME)
  3. External party replies back from Gmail internetMessageId : [@gmail.com] conversationId : AAQkABBBB... (DIFFERENT — new Exchange thread) In-Reply-To : [@externalsender.com] (correct RFC threading) References : [******@externalsender.com] (correct RFC threading)

At step 3, Graph creates a brand new conversationId, so our system creates a new Case instead of linking it to the existing one.

Questions

  1. Why does Exchange/Graph assign a new conversationId for external replies?

Is this by design? Our understanding is that conversationId in Exchange is derived from the Thread-Index MAPI property, which is a Microsoft-proprietary header. External senders on Gmail etc. do not send Thread-Index, so Exchange cannot continue the same thread. Is this the correct explanation?

  1. Is Thread-Index the sole determinant of conversationId?

Does Exchange fall back to References or In-Reply-To (standard RFC 2822 headers) at all when Thread-Index is absent? Or is conversationId purely derived from Thread-Index?

  1. Is there any Graph API field that provides stable thread identity across external replies?

We are already fetching internetMessageHeaders and parsing References and In-Reply-To ourselves to stitch threads. Is there a first-class Graph API field we are missing that would give us a stable cross-sender thread identifier — something like a canonical thread root ID?

  1. Can conversationId ever be the same across two separate Exchange mailboxes?

If the same email arrives in two monitored mailboxes, will both copies carry the same conversationId, or is it mailbox-scoped?

  1. Is internetMessageId guaranteed stable once delivered to the mailbox?

We understand each sender's MTA stamps its own Message-ID. But once a message is delivered to Exchange and visible via Graph, is the internetMessageId field guaranteed to never change — even across folder moves, mailbox migrations, or retention policy actions?

  1. What is the recommended Microsoft Graph approach for grouping emails into threads for a case management or ticketing system?

Is the intended pattern to use conversationId only for same-org threads and fall back to RFC headers for cross-org? Use conversationId as a starting point but maintain a merge table when IDs drift? Or something else entirely?

What We Have Tried

Using conversationId as the Case grouping key — breaks on external replies.

Using internetMessageId as a dedup key per message — works for deduplication but does not group threads.

Parsing References and In-Reply-To from internetMessageHeaders and matching against previously seen internetMessageId values stored per Case — works but feels like we are re-implementing what Exchange should already know.

Normalized subject plus participant overlap as a fuzzy fallback — catches stripped-header forwards but risks false positives.

Environment

Microsoft Graph SDK for Java v6 Spring Boot webhook listener Subscribing to /users/{id}/messages change notifications Permissions: Mail.Read application permission Single client Outlook mailbox, not our own tenant

What We Are Looking For

Confirmation of the root cause, whether there is a stable Graph API field we are missing, and what the recommended pattern is for building reliable email thread grouping on top of Microsoft Graph webhooks.

Claude is AI and can make mistakes. Please doHere's the plain copy-paste version:


Title: Microsoft Graph Webhook — conversationId changes when external party replies, breaking email thread grouping for case management


Background / Context

We are building a case management system where we listen to a client's Outlook mailbox via Microsoft Graph change notifications (webhooks). When a new email arrives, we create or update a Case based on the email thread. The goal is: all emails belonging to the same conversation should be linked to the same Case.

Current Behavior — The Problem

When an external party (e.g. someone on Gmail, Yahoo, or another org's Exchange) replies to an existing thread, Microsoft Graph assigns a new conversationId to that reply, even though it is clearly part of the same conversation (the RFC 2822 References and In-Reply-To headers correctly point back to the original message).

Similarly, we have observed that internetMessageId can differ per message since each MTA stamps its own Message-ID, so it cannot be used as a thread grouping key — only as a per-message dedup key.

Example flow:

  1. External sender emails client internetMessageId : [******@externalsender.com] conversationId : AAQkADFmM... (Exchange assigns this)
  2. Client replies from Outlook internetMessageId : [******@clientdomain.com] conversationId : AAQkADFmM... (SAME)
  3. External party replies back from Gmail internetMessageId : [@gmail.com] conversationId : AAQkABBBB... (DIFFERENT — new Exchange thread) In-Reply-To : [@externalsender.com] (correct RFC threading) References : [******@externalsender.com] (correct RFC threading)

At step 3, Graph creates a brand new conversationId, so our system creates a new Case instead of linking it to the existing one.

Questions

  1. Why does Exchange/Graph assign a new conversationId for external replies?

Is this by design? Our understanding is that conversationId in Exchange is derived from the Thread-Index MAPI property, which is a Microsoft-proprietary header. External senders on Gmail etc. do not send Thread-Index, so Exchange cannot continue the same thread. Is this the correct explanation?

  1. Is Thread-Index the sole determinant of conversationId?

Does Exchange fall back to References or In-Reply-To (standard RFC 2822 headers) at all when Thread-Index is absent? Or is conversationId purely derived from Thread-Index?

  1. Is there any Graph API field that provides stable thread identity across external replies?

We are already fetching internetMessageHeaders and parsing References and In-Reply-To ourselves to stitch threads. Is there a first-class Graph API field we are missing that would give us a stable cross-sender thread identifier — something like a canonical thread root ID?

  1. Can conversationId ever be the same across two separate Exchange mailboxes?

If the same email arrives in two monitored mailboxes, will both copies carry the same conversationId, or is it mailbox-scoped?

  1. Is internetMessageId guaranteed stable once delivered to the mailbox?

We understand each sender's MTA stamps its own Message-ID. But once a message is delivered to Exchange and visible via Graph, is the internetMessageId field guaranteed to never change — even across folder moves, mailbox migrations, or retention policy actions?

  1. What is the recommended Microsoft Graph approach for grouping emails into threads for a case management or ticketing system?

Is the intended pattern to use conversationId only for same-org threads and fall back to RFC headers for cross-org? Use conversationId as a starting point but maintain a merge table when IDs drift? Or something else entirely?

What We Have Tried

Using conversationId as the Case grouping key — breaks on external replies.

Using internetMessageId as a dedup key per message — works for deduplication but does not group threads.

Parsing References and In-Reply-To from internetMessageHeaders and matching against previously seen internetMessageId values stored per Case — works but feels like we are re-implementing what Exchange should already know.

Normalized subject plus participant overlap as a fuzzy fallback — catches stripped-header forwards but risks false positives.

Environment

Microsoft Graph SDK for Java v6 Spring Boot webhook listener Subscribing to /users/{id}/messages change notifications Permissions: Mail.Read application permission Single client Outlook mailbox, not our own tenant

What We Are Looking For

Confirmation of the root cause, whether there is a stable Graph API field we are missing, and what the recommended pattern is for building reliable email thread grouping on top of Microsoft Graph webhooks.

Microsoft 365 and Office | Development | Other
0 comments No comments

1 answer

Sort by: Most helpful
  1. Hin-V 14,180 Reputation points Microsoft External Staff Moderator
    2026-05-07T22:47:02.32+00:00

    Hi @Zeeshan Aameer

    Good day, and I appreciate the detailed description of your issue.   

    Regarding to your concerns: 

    Regarding to Exchange/Graph assign a new conversationId for external replies. 

    Based on my research, Exchange conversation threading is primarily driven by the MAPI property PidTagConversationIndex, which is exposed via APIs as conversationIndex (Thread‑Index). 

    This property is generated and maintained by Outlook/Exchange and is the authoritative mechanism used internally to determine conversation grouping. 

    Exchange uses a proprietary threading model (Thread-Index), not the standard RFC headers. When emails come from external systems that do not preserve this property, Exchange cannot reliably associate them with the original conversation and therefore creates a new conversationId. 

    Reference: Work with conversations by using EWS in Exchange 

    Behavior with External Senders 

    As a forum moderator, my support is limited to Microsoft technologies. I do not have visibility into third‑party systems, so I cannot ensure how external platforms generate or preserve threading metadata. 

    However, if the Thread‑Index header is not included, when a reply from an external sender reaches Exchange, it may not contain a valid Thread‑Index that matches an existing conversation. In this case, Exchange cannot reliably associate the message with the original thread. As a result, Exchange creates a new conversation, which leads to a new conversationId as I mentioned earlier. 

    Regarding Thread‑Index and whether Exchange falls back to RFC headers 

    As far as I know, Thread-Index (conversationIndex) is the dominant factor. 

    Exchange conversation grouping depends primarily on: 

    • PidTagConversationIndex (Thread-Index
    • Conversation topic (normalized subject) 

    If Thread-Index is missing or incompatible, Exchange does not reliably fall back to RFC headers (In-Reply-To, References) for grouping. These headers are preserved for transport purposes, but they are not used as the authoritative thread identity source for conversationId

    Graph API field that provides stable thread identity across external replies 

    Unfortunately, Microsoft Graph does not expose a first-class field that serves as a stable thread identifier across different senders or email systems. 

    Whether conversationId can ever be the same across two separate Exchange mailboxes 

    conversationId is scoped to a single mailbox. The same message delivered to multiple recipients may have different conversationId values in each mailbox. 

    You can refer via: Is the ConversationID in the message resource unique and immutable? - Microsoft Q&A 

    Regarding internetMessageId stability 

    The internetMessageId is an immutable identifier for a specific email message. It is assigned by the sending system and preserved by Exchange once the message is delivered. However, because every message (including replies) has its own unique Message‑ID, it cannot be used as a thread identifier. Instead, thread relationships must be derived from the In‑Reply‑To and References headers that link messages together. 

    Recommended Microsoft Graph approach for grouping email threads 

    Your current implementation, parsing In‑Reply‑To and References and matching them against stored internetMessageId values, is aligned with the recommended approach. This is necessary because Exchange conversation IDs are not designed to provide a stable cross‑system thread identity. 

    Please note that this summary is based on my own findings and may not fully address your concerns. To help you reach your goal more effectively, I recommend engaging with [GitHub Community Forum] for a deeper technical dive or to connect with individuals who have relevant experience and expertise. Some approaches may behave differently or be restricted depending on your specific environment and configuration. These forums include many experienced developers and Microsoft specialists who can assist with troubleshooting and guidance.   

    Apologies for redirecting you to the related development team support. As moderators in this community, we do not have access to your specific tenant configuration, and my testing environment is limited. Therefore, my guidance is based on available Microsoft documentation and resources. That said, I’ll do my best to provide additional insight where possible. 

    I hope this helps. 

    Please feel free to correct me if I misunderstood your request. If you have any additional concerns, feel free to comment below. I would be more than happy to assist. 


    Note: Please follow the steps in [our documentation] to enable e-mail notifications if you want to receive the related email notification for this thread.

    Was this answer helpful?

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.