---
title: How email threading actually works, and why agents break it
metaTitle: Email threading explained: Message-ID, In-Reply-To, References
description: Threading is a convention held together by three headers and a lot of client guesswork. What they do, how agents break conversations, and why a broken thread is worse than a wrong answer.
date: 2026-07-28
author: MCPmailer
tags: Architecture
---

Email has no concept of a conversation. What it has is a set of headers that clients use to guess at one, and the guess is good enough that most people never think about it. Agents think about it the moment they send a reply that arrives as a new conversation, or answer a message whose context is three replies up a chain they never fetched.

![Three messages linked into one conversation by their headers](/blog/email-threading-for-agents/hero.webp "A thread is not a thing the protocol stores. It is a chain of references clients follow.")

## The three headers

**`Message-ID`** is a globally unique identifier the sending server assigns to every message. Everything else refers back to these.

**`In-Reply-To`** carries the `Message-ID` of the message being replied to. One value, the immediate parent.

**`References`** carries the whole ancestry: the ids of every message up the chain, oldest first. This is what lets a client reconstruct a conversation rather than a pair.

A well-formed reply sets both: `In-Reply-To` to its parent, and `References` to the parent's `References` plus the parent's own id. Get that wrong and the message technically delivers while conversationally landing nowhere.

```text
Message-ID: <c3@customer.com>
In-Reply-To: <b2@agents.example.com>
References: <a1@customer.com> <b2@agents.example.com>
```

## What clients actually do with them

Follow the headers, mostly, and then apply their own judgement on top. Gmail groups by subject as well as by reference, which is why changing a subject line can split a thread and why two unrelated messages with the same subject sometimes merge. Outlook uses its own conversation index. Apple Mail follows references fairly strictly.

The practical consequence: you cannot control how a thread renders for every recipient, so aim to be correct rather than clever. Set the headers properly, keep the subject stable, and let clients do their thing.

> [!note]
> If your agent is generating `Message-ID` values itself, something is wrong. Servers assign them, and an agent inventing one is how you get a message that references an id nobody has ever seen.

## The four ways agents break threads

**Replying without references.** The most common failure, and it comes from treating a reply as a new send that happens to quote the old text. The recipient sees a fresh conversation, often with a subject that starts "Re:" and belongs to nothing.

**Changing the subject.** Rewriting a subject line to be more descriptive is a reasonable instinct and a threading mistake. Keep the subject the recipient chose.

**Replying to the wrong message.** In a busy thread, the last message and the message being answered are not always the same. If the agent is answering a question from three messages back, the reply still belongs on the latest, or the thread forks.

**Quoting instead of referencing.** Pasting the previous message into the body is not threading. It bloats every subsequent message, wastes tokens on both ends, and does nothing for the client's grouping.

All four disappear if the agent uses `reply_all` on a specific message rather than composing a new send, since the threading headers are then set for it. What that means when colleagues and customers share the thread is in [when an agent and a person are on the same thread](/blog/agents-and-humans-on-one-thread).

![A correctly threaded reply beside a broken one that starts a new conversation](/blog/email-threading-for-agents/broken.webp "Same words, same recipient. One continues the conversation and one starts a second.")

## Why it matters more for agents than for people

A person whose reply breaks a thread causes mild annoyance. An agent that breaks threads systematically causes three specific problems.

First, its own context degrades: if the agent later fetches the conversation to answer a follow-up, a fork means it sees half of it. Second, human handover breaks, because the colleague who takes over gets two partial threads instead of one conversation. Third, filters and mailbox providers treat a stream of unlinked messages to the same recipient differently from a conversation, and not in your favour.

There is also a subtler cost. `wait_for_reply` waits on a thread, so an agent that cannot keep a thread coherent cannot reliably know when it has been answered. See [long-running email conversations](/blog/wait-for-reply-long-running-agents).

## What good handling looks like

| Situation | Do this |
| --- | --- |
| Answering a message | `reply_all` on that message id, subject untouched |
| Answering an old message in a live thread | Reply on the latest message, address the older point in the body |
| Starting a genuinely new topic with the same person | New send, new subject, and say why in the first line |
| Forwarding to a colleague | `forward_email`, which keeps headers and attachments intact |
| Reading before answering | `get_thread`, not `read_message` alone |

`get_thread` returns the conversation in order with the quoted history stripped, which is both the correct input for the model and a large saving: a fifty message chain with every message quoting the last is mostly repetition, and you pay for it twice, once in tokens and once in the model's confusion about who said what.

## Subject lines, briefly

Keep the recipient's subject on replies. On a new conversation, write the subject the way a person would: specific, short, no marketing shape, no emoji, no all-caps. "Invoice 2291, due 8 July" beats "Following up on our conversation" for both the human and the filter.

Never put a tracking token in the subject to help your own threading. Use the headers, which exist for it.

## Testing that you have it right

Send a message from your agent to an account you can read, reply from there, and let the agent reply again. Then look at the raw source of the third message and check that `References` contains both earlier ids and `In-Reply-To` contains the second. If it does, threading is correct everywhere it can be. Doing this once per integration is enough, and it takes five minutes, and almost nobody does it. The setup for this kind of check is in [test inboxes for agent development](/blog/test-email-addresses-for-agents).

## Questions

### How does email threading work?

Through three headers. `Message-ID` uniquely identifies each message, `In-Reply-To` points at the immediate parent, and `References` carries the whole ancestry so a client can reconstruct the conversation.

### Why does my agent's reply start a new thread?

Almost always because it composed a new send instead of replying to a message id, so no `In-Reply-To` or `References` headers were set. Changing the subject line can also split a thread in clients that group by subject.

### Should the agent quote the previous message?

No. Quoting is not threading. It inflates every message, costs tokens on both sides, and does nothing for grouping. Reply on the message id and let the headers do the work.

### What is the difference between `read_message` and `get_thread`?

One message versus the whole conversation, in order and with quoted history stripped. Answering from a single message is the most common cause of a bad agent reply.

### Can I change the subject line of a reply?

Better not to. Keep the recipient's subject on replies; if the topic has genuinely changed, start a new conversation and say so in the first line.

### How do I verify threading is working?

Exchange three messages with an account you control and read the raw source of the last one. `References` should list both earlier ids and `In-Reply-To` should name the second.

## Related

- [Long-running email conversations](/blog/wait-for-reply-long-running-agents)
- [How to give an AI agent its own email address](/blog/email-for-ai-agents)
- [Test inboxes for agent development](/blog/test-email-addresses-for-agents)
- [Tools reference](/docs/tools)
- [The forwarded message problem](/blog/handling-forwarded-email)
