Source: https://cli.nylas.com/guides/email-threads-cli

# Email Threads from Terminal: List, Read, Reply

You have 47 unread messages but only 12 actual conversations. Email threads group those messages by subject and reply headers so you can navigate conversations instead of individual emails. This guide covers listing threads, reading them with jq, finding threads by participant, and replying — all from the terminal.

Written by [Qasim Muhammad](https://cli.nylas.com/authors/qasim-muhammad) Staff SRE

Updated May 30, 2026

> **TL;DR:** Use `nylas email threads list --json` to get conversations, pipe through `jq` to find the longest threads or most active participants, then read individual messages with `nylas email read MSG_ID` and reply by reusing the subject line with `nylas email send`. Gmail and Outlook thread natively; IMAP threading depends on server support.

Command references used in this guide: [`nylas email threads list`](https://cli.nylas.com/docs/commands/email-threads-list), `nylas email threads show`, [`nylas email read`](https://cli.nylas.com/docs/commands/email-read), [`nylas email send`](https://cli.nylas.com/docs/commands/email-send), and `nylas email threads mark`.

## What are email threads and how does threading work?

An email thread is a group of messages linked by shared subject and reply headers. When you reply to an email, your client adds an `In-Reply-To` header pointing to the previous message ID and a `References` header listing all prior IDs in the chain. Mail servers and clients use these headers to reconstruct the conversation order — no subject-line matching required. A single thread can contain dozens of messages spanning weeks, with branches where multiple people reply to the same parent.

Threading support varies by provider. Gmail has threaded since 2004 and groups messages server-side, so every API call returns a stable `thread_id`. According to [Google's Gmail API docs](https://developers.google.com/gmail/api/reference/rest/v1/users.threads), a thread contains all messages with the same `threadId`. Microsoft Outlook also threads natively via the Graph API using the `conversationId` field. IMAP servers vary: some compute threads server-side (RFC 5256), others don't and rely on the client to reconstruct the chain from headers.

## How do you install and authenticate the CLI?

The CLI installs with a single Homebrew command on macOS and Linux. Set a Nylas API key with `nylas auth config --api-key`, then connect a provider via `nylas auth login` (OAuth browser flow) — no per-provider app passwords and no SMTP credentials. Once authenticated, all thread commands work across Gmail, Outlook, Exchange, Yahoo, iCloud, and IMAP with the same syntax.

Install with Homebrew, then run the config command with your API key. The key is stored locally in `~/.config/nylas/` and reused for every subsequent command. For other install methods (shell script, PowerShell, Go), see the [getting started guide](https://cli.nylas.com/guides/getting-started).

```bash
# Install (macOS or Linux)
brew install nylas/nylas-cli/nylas

# Authenticate with your Nylas API key
nylas auth config --api-key YOUR_API_KEY
```

## How do you list email threads from the terminal?

The `nylas email threads list` command fetches your most recent conversations sorted by the latest message date. It returns 10 threads by default. Each thread object includes the subject, participant list, a snippet from the most recent message, and a `message_ids` array containing every message ID in the conversation. The `--json` flag outputs structured data you can pipe into `jq` for filtering and analysis.

Adding `--limit` controls how many threads come back. A typical inbox has roughly 1 thread for every 3-4 messages, so fetching 25 threads covers about 75-100 individual messages. The snippet field is a 200-character preview of the latest message — useful for triaging without fetching full bodies.

```bash
# List 10 most recent threads (default)
nylas email threads list

# List 25 threads as JSON
nylas email threads list --json --limit 25

# Show subject and participant count for each thread
nylas email threads list --json | \
  jq '.[] | {subject, messages: (.message_ids | length), participants: (.participants | length)}'

# Find threads with 5 or more messages
nylas email threads list --json --limit 50 | \
  jq '[.[] | select((.message_ids | length) >= 5)] | sort_by(-(.message_ids | length))'
```

The `message_ids` array is ordered chronologically — first entry is the oldest message, last entry is the newest. Counting its length gives you the exact message count per thread without fetching each message body.

## How do you read a full email thread?

Reading a full thread means fetching each message in the `message_ids` array using `nylas email read MSG_ID`. Each call returns the full HTML and plain text body, sender, recipients, and attachment metadata. For a thread with 8 messages, that's 8 separate read commands — or one short shell loop. The CLI returns plain text in the `body` field, which is typically 40-60% smaller than the HTML version.

The workflow below fetches a specific thread by ID, then loops over its message IDs to print each sender and a 500-character body preview. This gives you a quick conversation view without a GUI client. Thread display time depends on message count and network latency.

```bash
# Get a specific thread by ID
nylas email threads show THREAD_ID --json

# Read the latest message in a thread
THREAD_ID="your-thread-id-here"
LATEST_MSG=$(nylas email threads show $THREAD_ID --json | jq -r '.message_ids[-1]')
nylas email read "$LATEST_MSG" --json | jq '{subject, from: .from[0].email, snippet}'

# Print all messages in a thread (sender + body preview)
nylas email threads show $THREAD_ID --json | jq -r '.message_ids[]' | while read msg_id; do
  nylas email read "$msg_id" --json | \
    jq -r '"--- \(.from[0].email) ---\n\(.body[:500])"'
done
```

## How do you analyze threads with jq?

The JSON output from `nylas email threads list` unlocks thread analysis that's impossible in a GUI inbox. You can rank threads by length, find your most active correspondents, or calculate how quickly a conversation is moving. Piping JSON through jq eliminates the click-wait-scroll cycle of a browser inbox and turns thread analysis into a repeatable script.

The three `jq` patterns below cover the most common thread analysis workflows. The longest-threads query is useful for prioritization: a 20-message thread has more unresolved context than a 2-message one. The participant frequency query surfaces who you're exchanging the most email with, which is useful for CRM updates and response-time tracking. Fetching threads is fast because the CLI returns metadata without downloading full bodies.

```bash
# Rank threads by message count (longest first)
nylas email threads list --json --limit 50 | \
  jq 'sort_by(-(.message_ids | length)) | .[] | "\(.message_ids | length) messages: \(.subject)"'

# Count how many threads each participant appears in
nylas email threads list --json --limit 50 | \
  jq '[.[].participants[].email] | group_by(.) | map({email: .[0], threads: length}) | sort_by(-.threads) | .[:10]'

# Find threads where a specific person participated
nylas email threads list --json --limit 50 | \
  jq --arg email "colleague@company.com" \
  '[.[] | select(.participants[].email == $email)] | length'

# Identify threads updated in the last 24 hours
CUTOFF=$(date -v-1d +%s 2>/dev/null || date -d "yesterday" +%s)
nylas email threads list --json --limit 100 | \
  jq --argjson cutoff "$CUTOFF" '[.[] | select(.latest_message_received_date > $cutoff)]'
```

## How do you reply to an email thread from the terminal?

Replying to a thread means sending a new message that the recipient's client groups with the original conversation. Proper threading requires `In-Reply-To` and `References` headers per RFC 5322, but many providers also group messages heuristically by subject. The `nylas email send` command handles the SMTP and OAuth layer, so you don't need `sendmail` or Postfix. Prepending `Re:` to the original subject and addressing the sender is enough for Gmail and Outlook to group the reply into the existing thread.

The workflow below reads the latest message in a thread, extracts the sender's email, and sends a reply in one pipeline. According to [RFC 5322 section 3.6.4](https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.4), a proper reply sets `In-Reply-To` to the `Message-ID` of the parent message. Gmail and Outlook thread by subject heuristically, so the `Re:` prefix is sufficient for grouping in most cases.

```bash
# Reply to the sender of the latest message in a thread
THREAD_ID="your-thread-id-here"

# Extract sender email and subject from the latest message
LATEST=$(nylas email threads show $THREAD_ID --json | jq -r '.message_ids[-1]')
MSG=$(nylas email read "$LATEST" --json)
SENDER=$(echo "$MSG" | jq -r '.from[0].email')
SUBJECT=$(echo "$MSG" | jq -r '"Re: \(.subject)"')

# Send the reply
nylas email send \
  --to "$SENDER" \
  --subject "$SUBJECT" \
  --body "Thanks for the update. I'll follow up by Friday."

# Reply with a multi-line body
nylas email send \
  --to "$SENDER" \
  --subject "$SUBJECT" \
  --body "$(cat reply-body.txt)"
```

## How do you mark email threads as read?

Marking a thread as read with `nylas email threads mark THREAD_ID --read` sets the read flag on every message in the conversation in a single API call. This is more efficient than marking each message individually with `nylas email mark-read --id MSG_ID`, which requires one call per message. For a 10-message thread, that's 10 API calls vs 1. Marking threads as read is the standard way to clear unread count in Gmail and Outlook without deleting messages.

The loop below marks all threads older than 7 days as read. It fetches 100 threads, filters by date, and passes each thread ID to the mark command. Runtime scales linearly with thread count and is safe to run repeatedly — marking an already-read thread is a no-op on both Gmail and Outlook.

```bash
# Mark a single thread as read
nylas email threads mark THREAD_ID --read

# Mark all threads from a specific sender as read
nylas email threads list --json --limit 100 | \
  jq -r --arg email "newsletter@company.com" \
  '.[] | select(.participants[].email == $email) | .id' | \
  while read thread_id; do
    nylas email threads mark "$thread_id" --read
  done

# Mark threads older than 7 days as read
CUTOFF=$(date -v-7d +%s 2>/dev/null || date -d "7 days ago" +%s)
nylas email threads list --json --limit 100 | \
  jq --argjson cutoff "$CUTOFF" -r '[.[] | select(.latest_message_received_date < $cutoff)] | .[].id' | \
  while read tid; do
    nylas email threads mark "$tid" --read
    echo "Marked as read: $tid"
  done
```

## When should you use threads vs individual messages?

Threads and individual messages solve different problems. Use `nylas email threads list` when you want a conversation-level view: how many open conversations do you have, who are the active participants, which discussions are growing fastest. Use `nylas email list` or `nylas email search` when you want a message-level view: did a specific email arrive, what did the subject line say, filter by date range or sender. The two surfaces are complementary — threads for navigation, messages for search and inspection.

| Use case | Command | Why |
| --- | --- | --- |
| See all open conversations | `email threads list` | Groups 47 messages into 12 conversations |
| Find a specific message | `email search "query"` | Full-text search with provider-native operators |
| Read a full conversation | `email threads show` | Returns message_ids array in order |
| Read one message's body | `email read MSG_ID` | Returns full HTML + plain text + attachments |
| Mark conversation read | `email threads mark --read` | 1 API call vs N calls per message |
| Reply to a conversation | `email send` with Re: subject | Heuristic grouping by subject |

Gmail threads natively — every message has a stable `thread_id` from day one. Outlook assigns a `conversationId` through Microsoft Graph and threads work the same way. For IMAP providers, threading depends on whether the server implements RFC 5256 (THREAD extension). If it doesn't, the CLI reconstructs threads client-side using `In-Reply-To` and `References` headers.

## Next steps

- [Read email from terminal](https://cli.nylas.com/guides/read-email-from-terminal) — compare NeoMutt, aerc, and the CLI for reading individual messages
- [Search email from terminal](https://cli.nylas.com/guides/search-email-from-terminal) — find specific messages across providers with Gmail operators, Graph `$filter`, and IMAP SEARCH
- [Summarize email threads with AI](https://cli.nylas.com/guides/summarize-email-threads-ai) — feed a thread into an LLM and extract decisions, action items, and deadlines
- [Send email from terminal](https://cli.nylas.com/guides/send-email-from-terminal) — send, attach files, and manage drafts without SMTP configuration
- [Full command reference](https://cli.nylas.com/docs/commands) — every flag and subcommand documented
