An outreach sequence or an agent flow that reads inbound mail has one job it must get right: tell a real human reply apart from a machine. When someone is on vacation, their mail server fires back an out-of-office notice within seconds, and a naive flow treats that bounce-back as genuine interest. The fix is to inspect the message headers before you act, because automated responders mark themselves with standard headers that a real reply never carries.
This recipe shows how to fetch a message with its full header set through the Nylas Email API and then classify it as automated or human in your own code.
Why detect auto-replies before acting on a reply?
Section titled “Why detect auto-replies before acting on a reply?”An out-of-office notice is a real message in the mailbox, so it shows up in a list query and triggers a message.created webhook exactly like a human reply. If you advance a sequence or hand the text to an agent, you act on noise. Studies of cold outreach put auto-reply rates around 8 to 15 percent of all responses during holiday periods. Detecting these messages keeps your pipeline clean and your reply counts honest.
The signal lives in the headers. A vacation responder sets Auto-Submitted: auto-replied per RFC 3834, and most servers add a vendor header on top. A human reply carries none of them, so a header check separates the two groups with near-zero false positives.
How do I fetch a message with its full headers?
Section titled “How do I fetch a message with its full headers?”Send a GET /v3/grants/{grant_id}/messages/{message_id} request with fields=include_headers. Nylas returns the message object with a headers array of every name-value pair on the message, which is where the auto-reply markers live. Without that query parameter the response omits headers entirely.
The fields=include_headers value returns the full set on Google, Microsoft, and EAS. On EWS and IMAP, those 2 providers expose only the headers Nylas generates for MIME, so the vendor auto-reply headers may not survive. For the exact provider behavior, see using email headers and MIME data. Always pass fields=include_headers here rather than include_basic_headers, because the basic set carries only the 3 threading headers and none of the auto-reply markers.
curl --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>?fields=include_headers' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>'{ "request_id": "5fa64c92-e840-4357-86b9-2aa364d35b88", "data": { "grant_id": "<NYLAS_GRANT_ID>", "object": "message", "id": "<MESSAGE_ID>", "subject": "Automatic reply: Re: Project kickoff", "headers": [ { "name": "Auto-Submitted", "value": "auto-replied" }, { "name": "X-Auto-Response-Suppress", "value": "All" } ], "snippet": "I am out of the office until Monday and will reply on my return." }}Which headers mark an auto-reply?
Section titled “Which headers mark an auto-reply?”The most reliable marker is Auto-Submitted, defined in RFC 3834. Any value other than no means the message was generated by an automated process, and vacation responders set it to auto-replied. Treat the header name as case-insensitive and the value as a prefix, since some servers append parameters after a semicolon.
RFC 3834 was published in 2007 and is the one cross-vendor standard, but real mailboxes carry vendor headers too. Match these names verbatim: Auto-Submitted, X-Autoreply, X-Autorespond, and Microsoft’s X-Auto-Response-Suppress. The first 4 cover the bulk of providers, and X-Auto-Response-Suppress flags messages from Exchange and Outlook rules. If any of these is present, classify the message as automated.
| Header | Auto-reply value | Source |
|---|---|---|
Auto-Submitted | auto-replied (any value except no) | RFC 3834 |
X-Autoreply | yes | Common vendor header |
X-Autorespond | present (any value) | Common vendor header |
X-Auto-Response-Suppress | All, OOF, or similar | Microsoft Exchange and Outlook |
How do I classify a message in code?
Section titled “How do I classify a message in code?”Walk the headers array and check each name against the marker list, then fall back to a subject heuristic when no header matches. Messages may repeat a header name, and Nylas does not de-duplicate the list, so iterate the full array rather than building a single lookup map. The function below returns true for any automated message and runs in well under 1 ms per message.
A few servers, especially older IMAP setups, send a vacation notice without any auto-reply header. For those, a subject check on the localized “out of office” and “automatic reply” phrases catches most of the gap. Keep the header check primary and the subject check secondary, because a subject match alone can misfire on a human who literally writes about being out of office.
const AUTO_HEADERS = new Set([ "auto-submitted", "x-autoreply", "x-autorespond", "x-auto-response-suppress",]);
const SUBJECT_HINTS = [/out of office/i, /automatic reply/i, /auto[- ]?reply/i];
function isAutomatedReply(message) { const headerHit = (message.headers ?? []).some((header) => { const name = header.name.toLowerCase(); if (name === "auto-submitted") { return header.value.trim().toLowerCase() !== "no"; } return AUTO_HEADERS.has(name); }); if (headerHit) return true; return SUBJECT_HINTS.some((pattern) => pattern.test(message.subject ?? ""));}What should I do when a reply is automated?
Section titled “What should I do when a reply is automated?”When isAutomatedReply returns true, suppress the action you would take on a human reply. In an outreach tool that means holding the sequence in place rather than marking the contact as engaged, since an out-of-office notice carries zero intent. In an agent flow it means skipping the message instead of generating an answer to a robot.
Wire the check into your inbound handler so it runs on every message before any downstream logic. The cleanest place is the message.created webhook flow, where you already fetch each new message by ID. Add fields=include_headers to that fetch and gate your business logic on the result. One header check on every inbound message adds 1 field to a call you already make, which is a small price for keeping automated noise out of a flow that should only react to people.
What’s next
Section titled “What’s next”- Using email headers and MIME data for the full
fieldsparameter behavior across providers - Read a single message or thread to fetch the message body alongside its headers
- Build a new email webhook to run this check on every inbound message in real time