Skip to content

How to list Exchange email threads

Exchange on-premises servers support conversation grouping through a ConversationId property that EWS assigns to each message. Nylas maps this directly to the Threads API, giving you a conversation view for self-hosted Exchange accounts through the same endpoint you’d use for Gmail or Outlook online.

This guide covers listing threads from Exchange on-prem accounts and the EWS-specific details: when to use EWS vs. Microsoft Graph, how ConversationId maps to threads, message ID behavior, and search limitations.

This is the first thing to figure out. The two provider types target different Exchange deployments:

Provider typeConnectorUse when
Microsoft GraphmicrosoftExchange Online, Microsoft 365, Office 365, Outlook.com, personal Microsoft accounts
Exchange Web ServicesewsSelf-hosted Exchange servers (on-premises)

If the user’s mailbox is hosted by Microsoft in the cloud, use the Microsoft thread guide instead. The ews connector is specifically for organizations that run their own Exchange servers.

Microsoft announced EWS retirement and recommends migrating to Microsoft Graph. However, many organizations still run on-premises Exchange servers where EWS is the only option. Nylas continues to support EWS for these environments.

Why use Nylas for threads instead of EWS directly?

Section titled “Why use Nylas for threads instead of EWS directly?”

EWS is a SOAP-based XML API. Building a conversation view requires constructing XML SOAP envelopes for FindConversation operations, parsing nested XML responses, handling EWS-specific ConversationId and ConversationIndex fields, and managing autodiscovery to find the right server endpoint. Error responses come back as SOAP faults with nested XML structures.

Nylas replaces all of that with a JSON REST API. The /threads endpoint returns pre-grouped conversations with metadata. No XML, no WSDL, no SOAP. Authentication and autodiscovery are handled automatically.

You’ll need:

  • A Nylas application with a valid API key
  • A grant for an Exchange on-premises account
  • An EWS connector configured with the ews.messages scope
  • The Exchange server accessible from outside the corporate network (not behind a VPN or firewall that blocks external access)

New to Nylas? Start with the quickstart guide to set up your app and connect a test account before continuing here.

Create an EWS connector with the scopes your app needs:

ScopeAccess
ews.messagesEmail API (messages, threads, drafts, folders)
ews.calendarsCalendar API
ews.contactsContacts API

During authentication, users sign in with their Exchange credentials, typically the same username and password they use for Windows login. The username format is usually [email protected] or DOMAIN\username.

Users with two-factor authentication must generate an app password instead of using their regular password. See Microsoft’s app password documentation for instructions.

The full setup walkthrough is in the Exchange on-premises provider guide.

The Exchange server must be accessible from Nylas’s infrastructure:

  • EWS must be enabled on the server and exposed outside the corporate network
  • If the server is behind a firewall, you’ll need to allow Nylas’s static IP addresses (available on contract plans)

Accounts in admin groups are not supported.

Make a List Threads request with the grant ID. By default, Nylas returns the most recent threads. These examples limit results to 5:

The response includes a latest_draft_or_message object with the most recent message’s content. The same code works for Google, Yahoo, and IMAP accounts.

You can narrow results with query parameters. Here’s what works with Exchange accounts:

ParameterWhat it doesExample
subjectMatch on subject line?subject=Weekly standup
fromFilter by sender[email protected]
toFilter by recipient[email protected]
unreadUnread only?unread=true
inFilter by folder or label ID?in=INBOX
received_afterAfter a Unix timestamp?received_after=1706000000
received_beforeBefore a Unix timestamp?received_before=1706100000
has_attachmentOnly results with attachments?has_attachment=true

Here’s how to combine filters. This pulls threads with unread messages from a specific sender:

Exchange supports the search_query_native parameter using Microsoft’s Advanced Query Syntax (AQS). You can combine search_query_native with any query parameter except thread_id.

AQS queries must be URL-encoded. For example, subject:invoice becomes subject%3Ainvoice in the URL. The SDKs handle this automatically, but you’ll need to encode manually in curl requests.

The Exchange server must have the AQS parser enabled and search indexing active for search_query_native to work. If queries aren’t returning expected results, the Exchange admin should verify these settings.

See the search best practices guide for more on search_query_native across providers.

Exchange on-prem has native conversation support through ConversationId, but it behaves differently from Microsoft Graph in several important ways.

Like Exchange Online (Microsoft Graph), on-premises Exchange assigns a ConversationId to each message. Nylas maps this to the thread’s id field. This means Exchange threading is based on the server’s own grouping logic, not heuristic header matching. The grouping is more reliable than IMAP-based providers like Yahoo or iCloud.

Message IDs change when messages move, but thread IDs don’t

Section titled “Message IDs change when messages move, but thread IDs don’t”

This is the most important Exchange-specific behavior. When a message moves from one folder to another, its Nylas message ID changes (this is expected EWS behavior). However, the thread_id remains stable because it’s based on the ConversationId, which doesn’t change when messages move.

If your app stores message IDs from the message_ids array, treat them as folder-specific pointers. The thread ID itself is safe to use as a permanent reference.

Like Microsoft Graph, Exchange threads can include messages across multiple folders (Inbox, Sent Items, Deleted Items). The thread’s folders array reflects all folders containing messages from that conversation. Use the in parameter to filter threads by folder.

Exchange processes received_before and received_after filters at the day level, not second-level. Even though Nylas accepts Unix timestamps, Exchange rounds to the nearest day. Results are inclusive of the specified day.

Exchange relies on a search index for queries. If a message has just arrived but the search index hasn’t refreshed yet, threads containing that message may not appear in filtered results. The refresh interval is controlled by the Exchange administrator. Unfiltered list requests always return the latest threads.

Unlike cloud services, Exchange on-prem rate limits are set by the server administrator. If the Exchange server throttles a request, Nylas returns a Retry-After header. Use webhooks to avoid hitting rate limits.

If EWS isn’t enabled, Nylas can still connect via IMAP for email-only access. When using IMAP fallback, threading uses header-based grouping instead of ConversationId, and the 90-day message cache applies.

The Threads API returns paginated responses. When there are more results, the response includes a next_cursor value. Pass it back as page_token to get the next page:

Keep paginating until the response comes back without a next_cursor.