Skip to content

Message-ID, In-Reply-To and References for app email

Control how notification emails thread in Gmail and Outlook with Message-ID, In-Reply-To and References, and how to break unwanted threads apart.

Koltrix Team4 min read
A wall of numbered metal mailboxes
Photo by Tasha Kostyuk on Unsplash
On this page(9 sections)
  1. The three headers
  2. How clients actually thread
  3. Generating good Message-IDs
  4. Pattern 1: thread notifications about one object
  5. Pattern 2: keep messages separate
  6. Pattern 3: thread replies back into your system
  7. Common mistakes
  8. Checklist
  9. Key takeaways

Notification emails have a habit of threading in ways nobody intended: every "New comment" message collapses into one enormous conversation, or replies to a ticket scatter across separate threads. Mail clients make those decisions largely from three headers your application controls.

The three headers

RFC 5322 defines the identification fields that clients use for threading:

  • Message-ID: a globally unique identifier for this message, written like an address in angle brackets.
  • In-Reply-To: the Message-ID of the message this one directly replies to.
  • References: the Message-IDs of the messages in the conversation so far, typically ending with the parent.
Message-ID: <[email protected]>
In-Reply-To: <[email protected]>
References: <[email protected]> <[email protected]>

When a person clicks Reply, their client copies the original Message-ID into In-Reply-To and appends it to References. Your application can do the same thing deliberately, to make automated messages thread the way you want.

How clients actually thread

Clients do not all use the same algorithm, which is the source of most surprises:

  • Header-based clients follow In-Reply-To and References, an approach that descends from the classic threading algorithms used by newsreaders and is widely implemented.
  • Gmail groups messages into conversations using a combination of signals. In general, messages need a matching subject (ignoring prefixes such as "Re:") and a relationship through References or In-Reply-To to join a conversation. Gmail's exact rules are not formally documented and have changed over the years.
  • Outlook has historically leaned on its own conversation headers (such as Thread-Index and Thread-Topic) and on subject matching in addition to the standard headers, and newer versions group by conversation in their own way.
  • Apple Mail threads using references and subjects.

The practical result: to make messages thread reliably everywhere, align both the headers and the subject. To keep messages apart reliably, vary at least one of them, preferably both.

Generating good Message-IDs

Every message you send should have a unique Message-ID that you generate and record. Most mail libraries add one automatically, but generating it yourself lets you store it and reference it later.

A good Message-ID:

  • Is globally unique, typically by combining a random or unique token with a domain you control.
  • Uses your own domain on the right side, such as @notifications.example.com.
  • Contains no personal data, because headers are visible to recipients and anyone they forward to.
import uuid

def make_message_id(domain="notifications.example.com"):
    return f"<{uuid.uuid4().hex}@{domain}>"

Store the Message-ID alongside your record of the send. You need it to build threads and to match replies that come back.

Pattern 1: thread notifications about one object

For a ticket, an order or a document, you often want every notification about that object in one thread. Create a stable "root" identifier for the object and reference it from every notification:

Subject: [Ticket #4821] Printer offline on floor 3
Message-ID: <[email protected]>
References: <[email protected]>
In-Reply-To: <[email protected]>

The root Message-ID can belong to the first notification you sent about the object, or it can be a synthetic identifier that never belonged to a real message. Header-based clients handle a missing parent gracefully and still group messages that share it. Keep the subject identical across notifications for clients that also match on subject.

Pattern 2: keep messages separate

Sometimes threading is the problem. Password reset codes, sign-in links and daily digests should not stack into one conversation, where the latest code hides below older ones or a collapsed thread hides a security alert. To keep them apart:

  • Send each with a fresh Message-ID and no In-Reply-To or References.
  • Vary the subject, for example by including the date for digests or the code for sign-in messages.
Subject: Your sign-in code: 482913

Including the code in the subject also makes it visible in notification previews, which users appreciate, though you should weigh that against the code being visible on a lock screen.

Pattern 3: thread replies back into your system

If customers reply to notifications and your system ingests the reply (a support desk or a comment-by-email feature), threading headers help you match the reply to its object. The reply's In-Reply-To or References will contain a Message-ID you generated. Look it up in your send records to find the ticket.

Do not rely on headers alone. Some clients strip or rewrite them, and forwarded messages break the chain. Common complements are a unique reply-to address per object (for example [email protected]) and an identifier in the subject line. Using two or three signals together is much more reliable than any one.

Common mistakes

Mistake Effect
Reusing the same Message-ID for different messages Clients may treat later messages as duplicates and hide or drop them
Putting user email addresses in Message-IDs Leaks personal data in headers
Changing the subject on each notification but setting References Threads in some clients, splits in others
Same subject, no References, high volume Unwanted grouping in clients that match on subject
References that grow without bound Very long headers; trim to the root plus the most recent few

RFC 5322 allows References to be trimmed. When the list gets long, keep the first (root) identifier and the most recent several.

Checklist

  • Generate and store a unique Message-ID for every message, on your own domain.
  • Decide per template whether it should thread or stay separate.
  • For threaded objects, reference a stable root ID and keep the subject constant.
  • For codes, alerts and digests, use fresh IDs and vary the subject.
  • Match inbound replies using headers plus a per-object reply address or subject token.
  • Keep personal data out of Message-IDs and trim long References chains.

Key takeaways

  • Message-ID, In-Reply-To and References are the standard threading controls, and your application can set all three.
  • Clients also use subject matching, so align or vary subjects along with headers.
  • Thread notifications about one object around a stable root ID.
  • Keep security codes and digests out of threads with fresh IDs and distinct subjects.
  • Store every Message-ID you send so replies can be matched back to their source.

Start with Koltrix

Your domain, one inbox, and an API that sends.

A team inbox where AI sorts and drafts (nothing is sent without your click), plus the transactional API and SMTP relay your product sends with. 7 days free, no card.

SharePost on XLinkedIn