Email threading across provider boundaries is why a reply from your AI agent sometimes opens a fresh conversation in Gmail while creating a nested thread in Outlook — and why that inconsistency breaks conversation context for both the agent and the human. The fix isn't provider-specific glue code. It's correct use of three RFC 5322 headers that every standards-compliant mail server already understands.
How email threading actually works
Every mail provider constructs threads differently at the UI layer, but every standards-compliant server agrees on the underlying headers. Threading comes down to three fields in the message envelope:
- Message-ID — a globally unique identifier the sending server stamps on each message, in the form
<unique-string@domain>. This is the canonical identity of a message. - In-Reply-To — contains the Message-ID of the single message being replied to.
- References — a space-separated ordered list of all Message-IDs in the conversation chain, oldest first, most recent at the end.
When a mail client renders a thread, it walks the References chain and groups messages that share common ancestors. Gmail uses References exclusively for grouping. Outlook uses both References and subject-line matching, with a heuristic that strips Re: and Fwd: prefixes. Exchange layers its own Thread-Index and Thread-Topic headers on top.
The consequence: if your agent sends a reply with correct In-Reply-To but a truncated or missing References chain, Gmail will group it correctly, but Outlook may start a new thread — because Outlook's subject-matching fallback fires and the full ancestry is absent.
The three-header contract
Get these three right on every reply and you will thread correctly on every major provider.
Message-ID generation
Every message your agent originates — including replies — must carry a fresh, globally unique Message-ID. The format is specified in RFC 5322 section 3.6.4:
<local-part@domain>
The local part is typically a UUID or a timestamp+random combination. The domain must be one you or your sending infrastructure controls. It matters: a Message-ID whose domain doesn't match the sending domain is a weak signal to spam filters, and some providers log it as a misconfiguration.
Message-ID: <01JXXXXXXXXXXXXXX@send.yourdomain.com>
Don't reuse Message-IDs. Don't leave the field blank and rely on the upstream MTA to generate one — MTAs will generate valid IDs, but you won't know them at send time, so you can't store them for future References construction.
In-Reply-To
Set this to the Message-ID of the specific message you're replying to. If the human sent you a message with:
Message-ID: <abc123@gmail.com>
Your reply must include:
In-Reply-To: <abc123@gmail.com>
This establishes the parent-child relationship. Gmail threads on this alone if References is absent, but Microsoft's threading logic requires References for deep chains.
References
References is an append-only log. When you receive a message, it already carries a References header listing the full ancestry. Your reply appends the Message-ID of the message you received:
References: <root@example.com> <second@gmail.com> <abc123@gmail.com>
If the incoming message had no References header — meaning it was the first message in the thread — your References contains only the In-Reply-To value:
In-Reply-To: <abc123@gmail.com>
References: <abc123@gmail.com>
Truncating References — keeping only the last N entries to save header bytes — breaks Outlook and Exchange threading. Some MTAs truncate automatically at 998 characters (the RFC line-length limit per header line). The right fix is to fold long References values across multiple lines with whitespace indentation, not drop entries.
Where cross-provider threading breaks
sequenceDiagram
participant H as Human on Gmail
participant A as AI Agent
participant O as Outlook Recipient
H->>A: Message-ID msg1
A->>O: Reply with In-Reply-To msg1 and References msg1
O->>A: Reply with Message-ID msg2 References msg1
A->>H: Reply missing References msg1 and msg2
Note over H: Thread splits in Gmail
Note over O: Thread splits in Outlook
The four most common failure modes:
Agent generates no Message-ID on outbound replies. The upstream MTA assigns one, but the agent never stores it. The next reply in the chain has nothing to put in References, so the chain breaks.
Agent stores the wrong identifier. Some email APIs return an internal send ID — a UUID scoped to the API — rather than the actual SMTP Message-ID that travels with the message. These are different things. If you store the API's internal ID and use it in References, recipients see a References header pointing to a string no message in the thread contains. Threading fails silently.
Subject line mutation. Outlook's subject-matching heuristic treats
Re: Your question about pricingandRE: Your question about pricingas the same subject — butYour question about pricing (follow-up)will not match. If your agent modifies the subject on a reply (even by appending text), you lose Outlook's fallback threading even when References is correct.Provider rewrites Message-IDs. Some providers — notably certain Exchange Online configurations and some mailing list managers — rewrite Message-ID values as mail passes through. When a message your agent sent gets forwarded through a list exploder, the Message-ID on the copy the recipient sees may differ from what you stamped. Store the Message-ID your agent assigned at send time AND parse the Message-ID of every inbound message independently. Don't assume they'll match.
Storing thread state across agent turns
Thread identity from the agent's perspective requires persisting these fields per message:
| Field | Source | Use |
|---|---|---|
message_id |
Your outbound header or inbound header | References construction |
references |
Inbound header, parsed | Append to for next reply |
in_reply_to |
Inbound header | Confirm parent linkage |
subject |
Inbound subject, normalized | Fallback thread matching |
thread_id |
Your internal key | Group messages in your store |
The thread_id is your own construct — a stable key you assign when the first message in a conversation arrives or is sent. Everything else is derived from RFC 5322 headers. Never use provider-assigned thread identifiers (Gmail's threadId, Outlook's conversation ID) as your canonical thread key. They don't survive provider boundaries and disappear entirely when a human moves a message between accounts.
A minimal thread state record in TypeScript:
interface ThreadState {
thread_id: string; // Your stable key
root_message_id: string; // Message-ID of the first message
latest_message_id: string; // Message-ID of the most recent message
references: string[]; // Ordered list, oldest first
subject: string; // Normalized: strip Re:/Fwd: prefixes
}
On each reply, construct the outbound headers:
const refsHeader = [...state.references, state.latest_message_id].join(' ');
// In-Reply-To: state.latest_message_id
// References: refsHeader
// Subject: `Re: ${state.subject}` (if not already prefixed)
Then update latest_message_id to your new outbound Message-ID and append it to references.
Practical notes for agent email infrastructure
If you're building on an email API rather than raw SMTP, verify that the API exposes the outbound Message-ID in its send response — not just an internal message handle. If it doesn't, you can't build correct References chains, and threading will degrade for long conversations.
For inbound email parsing, your webhook payload should include the full raw headers or at minimum the parsed Message-ID, References, and In-Reply-To values. Without raw References, you're reconstructing the chain from your own partial records — which fails when the human replies from a different device or mail client that extends the References chain independently.
Platforms like Mails.ai expose these threading headers directly in inbound events, and the client.messages.reply() method sets In-Reply-To and References automatically from the original message ID, so you don't manually reconstruct the References string on each reply.
Outlook and Exchange: extra headers
Microsoft's ecosystem adds two proprietary threading headers:
- Thread-Index — a base64-encoded binary value that Exchange uses to construct conversation trees, derived from the root message's Thread-Index by appending a time-based suffix. If your agent originates a thread, Outlook will generate Thread-Index on delivery. If your agent replies, leave it alone. Don't set it, don't modify it. Outlook reads it correctly if you leave it absent and falls back to References.
- Thread-Topic — the subject of the root message, without Re:/Fwd: prefixes. Set this on the first message in a thread. Leave it alone on replies.
In practice: set Thread-Topic on new outbound threads, omit Thread-Index entirely, and rely on correct References/In-Reply-To for Outlook threading. Exchange Online handles the rest.
Apple Mail and standards compliance
Apple Mail is the most strictly standards-compliant of the major clients. It threads exclusively on References and In-Reply-To. Get the RFC 5322 chain right, and Apple Mail threads it correctly. It doesn't fall back to subject matching, which means a broken References chain produces a broken thread with no recovery path. The upside: fixing References for Apple Mail fixes it for every other client too.
Testing thread continuity
Manual testing across providers is the only reliable verification. Set up test accounts on Gmail, Outlook.com, and an Exchange Online tenant. Send a 4-5 turn conversation from your agent through each. Check:
- All messages appear in one thread in each client.
- The thread still groups correctly after the human replies from mobile (mobile clients sometimes strip or modify headers).
- A reply from a different email address in the same domain threads correctly.
- A forwarded message starts a new thread (expected behavior — References breaks on forward).
For automated testing, parse raw message headers using tools like mail-parser (Node.js) or Python's email.parser. Assert that References on message N contains all Message-IDs from messages 1 through N-1.
See the Mails.ai API docs for how to access raw inbound headers in webhook events, and the architecture overview for how threading state fits into a multi-turn agent conversation model.
Frequently Asked Questions
Why does Gmail thread correctly when Outlook doesn't, given the same headers?
Gmail threads exclusively on References and In-Reply-To, with no subject-line fallback. Outlook threads on References first, then falls back to normalized subject matching. If your References chain is broken but the subject is correct, Outlook creates a correct-looking thread from the subject fallback — masking the header bug. When a reply finally arrives with a subject mutation or from a different client, Outlook's fallback fails and the thread splits.
Should I store the email provider's thread ID or my own?
Your own. Provider thread IDs (Gmail's threadId, Outlook's conversationId) are scoped to a single provider account. They don't travel with the message, don't survive forwarding, and are useless if the human changes email providers. Use provider thread IDs only as a secondary index for display purposes within a known provider's API.
What happens if the inbound message has no References header?
That means it's either the first message in a conversation or the sender's client stripped headers. Treat it as a new thread. Assign a fresh internal thread_id. Set your reply's In-Reply-To to the inbound message's Message-ID, and set References to that same Message-ID. This correctly initializes the chain for all future replies.
Does folding long References headers across multiple lines break anything?
No — RFC 5322 explicitly permits header folding (inserting CRLF followed by whitespace before continuation). References: <id1> <id2>\r\n <id3> <id4> is valid. What breaks threading is dropping entries to shorten the header. Fold, don't truncate.
Can my agent reconstruct a broken thread from partial data?
Sometimes. If you have all outbound Message-IDs stored in your own database and you receive an inbound message whose In-Reply-To matches one of them, you can reconstruct a partial References chain from your records. But you can't reconstruct Message-IDs for messages you didn't originate — messages the human sent that your agent received but didn't store headers for. The correct fix is to store the full References header of every inbound message at receipt time.
Related guides: Reply-To vs From in Email: Route Agent Replies Right, Inbound Email Parsing: Webhooks, Classification & Events, Email OAuth Token Management for AI Agents and Model Context Protocol for Email Tools: Dev Guide.
Explore the product: Inbound email API, All features and Pricing.
