Every mail provider groups related messages differently, and that difference leaks into your app the moment you try to show a conversation. Gmail hands you a native thread ID on every message. Outlook leans on a conversationId plus References headers. Plain IMAP gives you In-Reply-To and References headers and expects you to stitch the chain together yourself. Build directly against three providers and you write three threading models.
This page explains how threading works on each provider, how a single threads endpoint collapses those models into one JSON shape, and how to send a reply that nests under the original conversation instead of starting a new one.
What is email threading and how does an email API handle threads in Gmail?
Section titled “What is email threading and how does an email API handle threads in Gmail?”Email threading groups messages that belong to the same conversation so a reply appears under the original instead of as a separate item. Gmail tracks this with a native thread ID on every message. The Nylas Email API exposes that grouping through GET /v3/grants/{grant_id}/threads, which returns one thread object per conversation across all 6 supported providers.
Gmail’s model is the cleanest of the providers. Every message carries a stable threadId, and the Gmail API endpoint users.threads.get returns the full chain in one call. The API maps that native ID onto its own thread_id field, which appears on every message object: “Every message is associated with a thread, whether that thread contains one message or many.” That single field is what lets you regroup messages without parsing References headers by hand, which is exactly what the other providers force on you.
How does email threading help an AI agent understand conversation context?
Section titled “How does email threading help an AI agent understand conversation context?”An AI agent needs the full back-and-forth, not one isolated message, to answer correctly. A thread object bundles that context: the subject, every participant, the message_ids for the whole chain, and the snippet of the most recent message. Pulling one thread instead of paging the mailbox hands the model a complete conversation in a single request.
When an agent drafts a reply or summarizes a deal, missing earlier messages cause wrong answers: the model contradicts an agreement made three messages back. Fetching GET /v3/grants/{grant_id}/threads/{thread_id} returns message_ids for the entire conversation plus one expanded latest_draft_or_message, so you feed the model the IDs it needs and expand only the bodies that matter. The earliest_message_date and latest_message_received_date fields, both Unix timestamps, let the agent reason about timing, like whether a 14-day-old quote is still open. This grouping is the difference between an agent that tracks a conversation and one that answers each message blind.
How threading works across Gmail, Outlook, and IMAP
Section titled “How threading works across Gmail, Outlook, and IMAP”Threading is provider-specific underneath the unified shape. Gmail assigns a native thread ID per message. Microsoft groups by conversationId and References headers. IMAP and Exchange rely on In-Reply-To and References headers, which clients populate inconsistently. The threads endpoint reads each model and returns one normalized object, so you write a single parser instead of three.
The table below maps each native model to the unified field. The middle columns show what you’d handle calling each provider directly; the right column is the single shape the API returns.
| Concept | Gmail API | Microsoft / IMAP | Nylas threads endpoint |
|---|---|---|---|
| Conversation ID | native threadId | conversationId / References | thread_id on every message |
| Read a conversation | users.threads.get | header chain reconstruction | GET /v3/grants/{grant_id}/threads/{thread_id} |
| Message list in thread | messages[] on thread | parse In-Reply-To chain | message_ids array |
| Reply in-thread | set threadId on send | set References header | reply_to_message_id on send |
| Providers covered | Gmail only | one provider each | all 6 providers |
How do I list and read threads with one endpoint?
Section titled “How do I list and read threads with one endpoint?”Send GET /v3/grants/{grant_id}/threads to list conversations, then GET /v3/grants/{grant_id}/threads/{thread_id} to read one. The list call accepts the same filters as messages, including subject, any_email, unread, starred, and latest_message_after, and it returns the same normalized object across all 6 providers, so one parser handles Gmail, Outlook, IMAP, and Exchange together.
The request below lists the 5 most recent threads for a grant. Use it to power a conversation view, where each row is one thread rather than one message. The limit parameter caps the page; pair it with page_token from the response to page through the rest.
curl --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/threads?limit=5' \ --header 'Authorization: Bearer <NYLAS_API_KEY>'To read one full conversation, pass the thread ID. The response carries participants, subject, snippet, the message_ids for the whole chain, and a latest_draft_or_message object expanded inline, so a single call gives you enough to render a conversation header without fetching every body.
curl --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/threads/<THREAD_ID>' \ --header 'Authorization: Bearer <NYLAS_API_KEY>'For the field-by-field walkthrough, see read a single message or thread.
What is the best way to expose email threading data to users inside a SaaS app?
Section titled “What is the best way to expose email threading data to users inside a SaaS app?”Render the thread, not the mailbox. List with GET /v3/grants/{grant_id}/threads so each row is one conversation, show subject and snippet in the row, then expand the body on click using the message_ids. This mirrors how Gmail and Outlook present mail, so the 1 view feels familiar to every user regardless of provider.
A thread object is built for this layout. The snippet holds the first 100 characters of the last received message for a preview line, unread drives the bold-row styling, and has_attachments lets you show a paperclip without a second call. To list every message inside an opened thread, send GET /v3/grants/{grant_id}/messages?thread_id=<THREAD_ID> and render each body. One honest tradeoff: if your product only ever connects Gmail and you want pixel-level control over Gmail’s label model, calling users.threads.list directly avoids an abstraction layer. The moment a second provider appears, that choice means a second threading model to maintain.
How do I reply inside an existing thread?
Section titled “How do I reply inside an existing thread?”Send POST /v3/grants/{grant_id}/messages/send with reply_to_message_id set to the message you’re answering. That single field sets the In-Reply-To and References headers correctly for you, so the reply nests under the original conversation in Gmail, Outlook, and standards-compliant IMAP clients instead of landing as a brand-new email in the recipient’s inbox.
Pick the message to reply to deliberately: the most recent inbound message is usually right, and you’ll find its ID in the thread’s message_ids array or as the latest_draft_or_message. Keep the same subject so clients that fall back to subject-matching still group it. The send below threads a reply across every provider with one request body.
curl --request POST \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/send' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' \ --data '{ "subject": "Re: Q3 proposal", "reply_to_message_id": "<MESSAGE_ID>", "to": [{ "email": "[email protected]" }], "body": "Thanks for the notes. Updated draft attached." }'For attachments, message selection, and Agent Accounts, see how to reply to an email thread.
What’s next
Section titled “What’s next”- Read a single message or thread for the full thread response fields
- How to reply to an email thread to send a reply that threads correctly
- Build a unified inbox to merge threads from every connected provider
- Getting started with Nylas to create a project, connector, and your first grant