惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Commits to openclaw:main
Recent Commits to openclaw:main
U
Unit 42
WordPress大学
WordPress大学
Microsoft Azure Blog
Microsoft Azure Blog
Martin Fowler
Martin Fowler
人人都是产品经理
人人都是产品经理
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
博客园 - Franky
云风的 BLOG
云风的 BLOG
酷 壳 – CoolShell
酷 壳 – CoolShell
P
Palo Alto Networks Blog
NISL@THU
NISL@THU
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Vercel News
Vercel News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy & Cybersecurity Law Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
J
Java Code Geeks
Google DeepMind News
Google DeepMind News
C
Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Spread Privacy
Spread Privacy
小众软件
小众软件
T
Threat Research - Cisco Blogs
Project Zero
Project Zero
博客园 - 三生石上(FineUI控件)
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
The Hacker News
The Hacker News
F
Fortinet All Blogs
Security Latest
Security Latest
Cisco Talos Blog
Cisco Talos Blog
The GitHub Blog
The GitHub Blog
Stack Overflow Blog
Stack Overflow Blog
T
The Exploit Database - CXSecurity.com
量子位
Blog — PlanetScale
Blog — PlanetScale
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
P
Proofpoint News Feed
G
GRAHAM CLULEY
D
DataBreaches.Net
P
Privacy International News Feed
Y
Y Combinator Blog
Simon Willison's Weblog
Simon Willison's Weblog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
I
InfoQ
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Recent Announcements
Recent Announcements
P
Proofpoint News Feed

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Organize email with threads in the Nylas API
Qasim · 2026-06-24 · via DEV Community

Nobody thinks about their inbox as a pile of individual emails. They think in conversations: the back-and-forth about Wednesday's dinner, the thread with a client about an invoice. A thread is that conversation, the group of related messages that belong together, and treating it as one object is what makes an inbox feel like an inbox instead of a flat list of disconnected emails. Nylas exposes threads as a first-class resource, so you can list, read, and organize a whole conversation in a single call. This post builds the conversation view with the Email API and the CLI.

It's a worked use case rather than an endpoint tour, covering threads from two angles: the HTTP API your backend calls and the nylas CLI for browsing conversations from the terminal. I work on the CLI, so the commands below are the ones I reach for when I want to see a conversation at a glance.

A thread is a conversation, not a message

A thread groups the messages that form one conversation, and the link between them is the thread's message_ids array, the list of every message that belongs to it. Where a message is a single email, a thread is the whole exchange, so the subject, the participants, and the read state describe the conversation as a unit rather than any one reply in it. That's the mental model the API is built around.

The thread object gives you the conversation's summary without making you fetch every message in it. A single thread carries the subject, a snippet of the latest content, the participants across the conversation, has_attachments and has_drafts flags, an unread and starred state for the whole thread, and three timestamps, earliest_message_date, latest_message_received_date, and latest_message_sent_date. It also includes latest_draft_or_message, the full most-recent message inline, so a conversation list can show the latest activity without a second request.

Threads versus messages: which one to use

The distinction that matters for your code is that threads are for reading and organizing, while messages are for sending. You list threads to render a conversation view, you read a thread to see the exchange, and you organize a thread to act on the whole conversation, but you never create a thread directly. There's no "create thread" endpoint, because a thread isn't something you make; it forms automatically as messages reply to each other.

So the division of labor is clean. Display and organization happen at the thread level: the inbox list, the unread badges, the archive action. Composing happens at the message level: you send and reply with the messages endpoint, and the provider threads your reply into the right conversation based on its headers. When you want to show a conversation, reach for threads; when you want to send into one, reach for messages. Mixing that up, trying to "send a thread," is the most common conceptual stumble with this part of the API.

List conversations

Rendering a conversation view is a GET /v3/grants/{grant_id}/threads request, which returns threads with all their summary fields, enough to build an inbox list without touching the messages endpoint. Each thread comes back with its subject, snippet, participants, unread and starred state, and the inline latest_draft_or_message, so one call populates a whole screen of conversations.

curl --request GET \
  --url "https://api.us.nylas.com/v3/grants/<GRANT_ID>/threads?limit=20&unread=true" \
  --header "Authorization: Bearer <NYLAS_API_KEY>"

The same standard filters that work on messages work here: unread, starred, subject, from, in for a folder, and the date filters, so you can list, say, every unread conversation in the inbox. The thread-specific date filters are worth knowing too: latest_message_after and latest_message_before filter by when the conversation last saw activity, which is exactly how you'd sort or page a "most recent conversations" view. Each returned thread is the conversation summarized, ready to render as a row.

Browse conversations from the CLI

The terminal command nylas email threads list shows recent conversations, and it's the quick way to see what's active without building anything. It defaults to 10 threads and takes --unread, --starred, and --subject to narrow the view, plus --limit to fetch more, mapping directly onto the API's filters.

# Recent unread conversations
nylas email threads list --unread

# A specific conversation's full detail
nylas email threads show <thread-id>

Where list gives you the conversation summaries, nylas email threads show <thread-id> prints one thread's detail, its subject, participants, message count, the earliest and latest dates, a snippet, and the list of message_ids (plus a draft count and status when present), so you can see what the conversation holds at a glance in the terminal. This is genuinely useful for triage, scanning unread conversations and opening the one that matters, and it's how I check what a thread actually contains before wiring thread handling into an app. The CLI mirrors the API's read-and-organize model: it lists, shows, marks, and deletes threads, but it doesn't send into them; to reply into a conversation you use nylas email send --reply-to <message-id>, which threads onto the message you're answering.

Organize a whole conversation in one call

Here's where threads earn their place: you can act on an entire conversation at once. A PUT /v3/grants/{grant_id}/threads/{thread_id} request updates the whole thread, so setting unread to false marks every message in the conversation read, starred stars the conversation, and a folders array moves all of its messages to a folder in a single operation.

curl --request PUT \
  --url "https://api.us.nylas.com/v3/grants/<GRANT_ID>/threads/<THREAD_ID>" \
  --header "Authorization: Bearer <NYLAS_API_KEY>" \
  --header "Content-Type: application/json" \
  --data '{ "unread": false, "starred": true }'

This is the operation that would be tedious at the message level. Marking a ten-message conversation read by hand means ten message updates; at the thread level it's one PUT. The same goes for moving a conversation to an archive folder or starring it for follow-up, you act on the thread and every message inside it comes along. From the CLI, nylas email threads mark <thread-id> --read and --star do the same whole-conversation update, which is the natural way to clear or flag an entire exchange.

Deletion works the same way, on the whole conversation. A DELETE on the thread moves the entire exchange, every message included, to the Trash rather than erasing it outright, and nylas email threads delete <thread-id> does it from the terminal, so a "delete conversation" action is one call rather than a loop over every message. As with everything at the thread level, the operation fans out to all the messages the thread holds, which is the point: the conversation is the unit you act on.

Filter to the conversations that matter

A conversation view is only as good as its filtering, and threads support the filters that map onto real inbox actions. Beyond unread and starred for the obvious views, you filter by participant with from or any_email to find every conversation involving a particular person, by in to scope to a folder, and by has_attachment to surface conversations carrying files. Each narrows the thread list to a meaningful slice.

For ordering and paging a conversation view, the date filters are the ones you reach for. latest_message_after and latest_message_before bound the list by the conversation's most recent activity, which is how a "recent conversations" screen stays current, and they pair with limit and cursor paging to walk a long inbox. Because the thread already carries latest_message_received_date, you have the value to sort on without opening any message. One provider caveat: Microsoft, IMAP, iCloud, Yahoo, and EWS return threads ordered reverse-chronologically by latest message received, but Google doesn't guarantee that ordering because of a Gmail API limitation, though setting the in folder filter raises the odds of it noticeably.

How replies keep a conversation together

Since you can't create a thread directly, it's worth knowing how messages end up in the same one. When you send a reply with the messages endpoint, the provider groups it into the existing conversation using the message's headers, the references that tie a reply to what it answers. Nylas surfaces the result as the thread's growing message_ids array, so the conversation you see reflects the provider's own threading.

The practical upshot is that you reply at the message level and trust the threading to happen. To continue a conversation, you send a message in reply to one already in the thread, and it lands in the same thread rather than starting a new one. This is why the API has no "add to thread" operation: threading is a property of how messages reference each other, not something you set, so your job is to send the reply correctly and read the updated thread afterward. Get the reply headers right and the conversation stays whole.

Where threads fit

The same conversation model sits under a range of email features, and the thread is almost always the right unit for the UI. A few that map straight on:

  • A conversation inbox. Render threads, not messages, so the list shows conversations with a subject, snippet, and participant set, the way every modern mail client does.
  • Bulk triage. Mark a whole conversation read or move it to a folder in one PUT, so clearing an inbox is per-conversation rather than per-message.
  • Follow-up flagging. Star a thread for follow-up and filter by starred to build a "needs reply" view across conversations.
  • Activity sorting. Sort and page on latest_message_received_date for a recency-ordered conversation list without opening a single message, a guaranteed ordering on every provider except Google.

Each treats the thread as the conversation unit, with the messages endpoint reserved for composing and the thread endpoint for everything you display and organize.

Things to keep in mind

A short list of details keeps thread handling predictable.

  • Threads are read and organize, not create. There's no create-thread endpoint; conversations form as messages reply to each other.
  • Organize at the thread level. A single PUT marks every message read, stars the conversation, or moves all its messages to a folder, which beats updating messages one by one.
  • The thread carries the summary. Subject, snippet, participants, and latest_draft_or_message come back in the list call, so a conversation view needs no per-message fetch.
  • Send with messages, display with threads. Compose and reply through the messages endpoint; use threads for the conversation view and organization.
  • Sort on latest_message_received_date. The thread already includes it, so a recency-ordered list needs no extra request.
  • Replies thread by headers. A correctly addressed reply lands in the existing conversation; there's no manual "add to thread."

Wrapping up

Threads are how you build an inbox that works in conversations instead of loose emails. List them with GET /v3/grants/{grant_id}/threads to render a conversation view complete with subject, snippet, participants, and the latest message, and organize a whole exchange with a single PUT that marks it read, stars it, or moves all its messages at once. From the terminal, nylas email threads list and mark do the same. Remember the division of labor: you read and organize at the thread level and send at the message level, and conversations form themselves as replies reference what came before.

Where to go next: