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

推荐订阅源

The GitHub Blog
The GitHub Blog
S
SegmentFault 最新的问题
L
LangChain Blog
Simon Willison's Weblog
Simon Willison's Weblog
N
News and Events Feed by Topic
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
WordPress大学
WordPress大学
M
MIT News - Artificial intelligence
A
About on SuperTechFans
Microsoft Security Blog
Microsoft Security Blog
I
Intezer
Know Your Adversary
Know Your Adversary
H
Heimdal Security Blog
博客园 - 叶小钗
B
Blog RSS Feed
F
Fortinet All Blogs
Hacker News: Ask HN
Hacker News: Ask HN
A
Arctic Wolf
小众软件
小众软件
Help Net Security
Help Net Security
MongoDB | Blog
MongoDB | Blog
aimingoo的专栏
aimingoo的专栏
G
Google Developers Blog
Forbes - Security
Forbes - Security
Latest news
Latest news
AI
AI
I
InfoQ
H
Hackread – Cybersecurity News, Data Breaches, AI and More
C
CXSECURITY Database RSS Feed - CXSecurity.com
W
WeLiveSecurity
C
Cybersecurity and Infrastructure Security Agency CISA
人人都是产品经理
人人都是产品经理
Cyberwarzone
Cyberwarzone
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
J
Java Code Geeks
Engineering at Meta
Engineering at Meta
C
Cyber Attacks, Cyber Crime and Cyber Security
O
OpenAI News
博客园 - 【当耐特】
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
U
Unit 42
D
Darknet – Hacking Tools, Hacker News & Cyber Security
G
GRAHAM CLULEY
Apple Machine Learning Research
Apple Machine Learning Research
宝玉的分享
宝玉的分享
Google DeepMind News
Google DeepMind News
T
Threatpost
T
The Blog of Author Tim Ferriss
罗磊的独立博客

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 your agents into workspaces by domain
Qasim · 2026-06-27 · via DEV Community

The first agent you give an inbox to is easy. You set a send cap, maybe a spam policy, attach a couple of inbound rules, and you're done. The trouble starts at the tenth agent. And the fiftieth. Because the obvious way to apply limits — configure each grant when you create it — doesn't have an "all of them" button. You end up writing a loop that re-applies the same policy to every new account, hoping nobody provisions one out of band, and discovering three weeks later that the support fleet and the sales fleet drifted into two slightly different configurations nobody can fully account for.

Configuring limits grant-by-grant doesn't scale past a handful of agents. That's the whole reason workspaces exist. A workspace is a container that groups Agent Accounts and carries exactly one policy plus a list of rules. Set the policy once on the workspace, and every account inside inherits it — including accounts that don't exist yet. Move the boundary up one level, from "configure each grant" to "configure the group," and the fleet manages itself.

I work on the Nylas CLI, so the terminal commands below are the exact ones I reach for. Every one is checked against nylas v3.1.27, and every curl call against the published OpenAPI spec. The two-angle tour — API and CLI side by side — is here because half of this work happens in a provisioning script and the other half happens at 2am when you're poking at a misbehaving fleet by hand.

The grant abstraction, one level up

If you've used Agent Accounts, you already know the spine: an Agent Account is a grant. It carries a grant_id and works with every grant-scoped endpoint — Messages, Drafts, Threads, Folders, Contacts, Calendars, Events, Webhooks. There's nothing new to learn on the data plane. Sending mail from an agent is POST /v3/grants/{grant_id}/messages/send, same as any grant.

Workspaces add one field to that picture. Every grant carries a workspace_id, and the workspace it points at holds a policy_id (limits and spam settings) and a rule_ids array (inbound and outbound mail filtering). The chain is short:

List  → holds values (domains, TLDs, addresses)
Rule  → match conditions + actions; references lists via in_list
Policy→ send caps, storage, retention, spam detection
Workspace → one policy_id + an array of rule_ids
Grant → carries workspace_id → inherits all of the above

Change the policy on the workspace and you've reconfigured every account in it at once. That's the leverage. You're not editing fifty grants; you're editing one workspace that fifty grants point at.

How accounts find their workspace

This is the part that makes the fleet self-organizing, and it's worth getting exactly right. When a new Agent Account is created, Nylas runs a three-step decision to pick its workspace:

  1. Explicit assignment. If the POST /v3/connect/custom payload includes a workspace_id, the grant joins that workspace immediately. No guessing.
  2. Domain auto-group. If no workspace_id was passed, Nylas looks for a custom workspace whose domain matches the new account's email domain and whose auto_group is true. If one matches, the grant joins it.
  3. Default workspace. If neither applies, the grant lands in the application's default workspace — the one Nylas creates and manages for you automatically.

Step 2 is the interesting one. auto_group is a boolean on the workspace, and the spec describes it plainly: when true, "newly created grants in the application are automatically assigned to the workspace if their email address' domain matches the domain." So if you create a workspace with domain: "support.yourcompany.com" and auto_group: true, then every account you later provision as triage@support.yourcompany.com or escalations@support.yourcompany.com joins that workspace and inherits its policy without you passing a single extra field. The CLI agent account create command deliberately has no --workspace flag — auto-group is meant to carry that weight. You set the domain rule once and let provisioning be dumb.

That's the design choice I like as an SRE: the policy decision lives in the infrastructure, not in every caller. A teammate writing a provisioning script doesn't need to know which policy a @sales.yourcompany.com agent should get. They create the account; the domain match routes it.

Before you begin

You'll need:

  • A Nylas application and API key. The examples use the US data region host https://api.us.nylas.com and Authorization: Bearer <NYLAS_API_KEY>.
  • A registered sending domain, or a Nylas *.nylas.email trial subdomain. Workspaces key off this domain, so it has to be one you actually provision accounts under. New domains warm over roughly four weeks.
  • The Nylas CLI initialized (nylas init) if you want to follow the terminal half.

New to Agent Accounts? Start with the Agent Accounts overview and the provisioning guide to create your first account, then come back here to organize them.

Create a domain-scoped workspace

A workspace create takes two required fields — name and domain — plus three optional ones: auto_group, policy_id, and rule_ids. The domain ties the workspace to one email domain, and auto_group (which defaults to true) decides whether matching accounts join automatically.

Here's the full version: a workspace scoped to a domain, with a policy and a rule attached in the same call.

curl --request POST \
  --url "https://api.us.nylas.com/v3/workspaces" \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "Support fleet",
    "domain": "support.yourcompany.com",
    "auto_group": true,
    "policy_id": "<POLICY_ID>",
    "rule_ids": ["<RULE_ID>"]
  }'

The response returns a Workspace object whose workspace_id is the value you'll store on grants. From the terminal, the same thing:

nylas workspace create \
  --name "Support fleet" \
  --domain support.yourcompany.com \
  --auto-group \
  --policy-id <POLICY_ID>

A couple of honest caveats on that CLI form. nylas workspace create accepts --name, --domain, --auto-group, and --policy-id. It does not take a --rule-ids flag at create time — to attach rules you run an update afterward (shown below). And the --auto-group flag is a switch: include it to enable auto-grouping, leave it off otherwise. Both --name and --domain are required by the CLI, matching the API.

One field you can't undo: the domain is immutable once the workspace is created. The spec is explicit — "You can't change the domain after the workspace is created." That's a deliberate guardrail, but it means you should decide your domain-to-workspace map before you start provisioning, not after. If you scope a workspace to the wrong domain, you delete it and make a new one; you don't edit the domain in place.

You also don't have to attach a policy at creation. A workspace with no policy_id runs its accounts at your billing plan's maximum limits. So a perfectly reasonable order of operations is: create the grouping first, get accounts flowing into it, then attach a policy once you know the limits you want. Attaching narrows the maximums down to whatever the policy defines.

Attach a policy and rules to the whole fleet

This is the payoff. You build a policy and some rules once, attach them to the workspace, and every member inherits them — including the ones auto-group hasn't created yet.

A policy bundles the limits (daily send cap, storage, inbox/spam retention, attachment caps) and spam detection. A rule matches inbound or outbound mail and runs actions like block or assign_to_folder. Rules are inert on their own — a rule does nothing until a workspace references it through rule_ids. That's the activation step, and it's easy to forget: you can POST /v3/rules all day and nothing happens to your mail until the rule's ID lands in a workspace's array.

To attach or change either, PATCH the workspace and include only the fields you're changing:

curl --request PATCH \
  --url "https://api.us.nylas.com/v3/workspaces/<WORKSPACE_ID>" \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "policy_id": "<POLICY_ID>",
    "rule_ids": ["<RULE_ID_1>", "<RULE_ID_2>"]
  }'

The PATCH semantics are worth internalizing because they're how you swap configuration across a fleet without churn:

  • Omit a field and it's left untouched.
  • Set a field to a value and it overwrites. policy_id takes one UUID string; rule_ids takes an array.
  • Set a field to null and it clears. policy_id: null detaches the policy (the fleet drops back to plan maximums); rule_ids: null removes every rule.

From the terminal, the same update:

nylas workspace update <WORKSPACE_ID> \
  --policy-id <POLICY_ID> \
  --rules-ids rule-id-1,rule-id-2

The CLI flag is --rules-ids (comma-separated, note the plural), and --policy-id with an empty string — --policy-id "" — detaches the policy, the terminal equivalent of sending null. To see what you've got across the whole application, list every workspace and inspect one:

curl --request GET \
  --url "https://api.us.nylas.com/v3/workspaces" \
  --header 'Authorization: Bearer <NYLAS_API_KEY>'

curl --request GET \
  --url "https://api.us.nylas.com/v3/workspaces/<WORKSPACE_ID>" \
  --header 'Authorization: Bearer <NYLAS_API_KEY>'

The CLI mirrors both:

nylas workspace list
nylas workspace get <WORKSPACE_ID>

Every account whose workspace_id points at this workspace is now governed by that one policy and that one rule set. Provision a new @support.yourcompany.com agent tomorrow and it inherits both the moment auto-group routes it in. You never touch the new grant directly.

Moving an account between fleets

Auto-group handles new accounts. For existing ones — say an account that was created before you set up its domain workspace, or one you want to reassign — you move it.

The API path is the manual-assign endpoint, POST /v3/workspaces/{workspace_id}/manual-assign. Send grant IDs in assign_grants, remove_grants, or both, up to 500 IDs per list. Assigning a grant moves it into the target workspace even if it currently belongs to another one:

curl --request POST \
  --url "https://api.us.nylas.com/v3/workspaces/<TARGET_WORKSPACE_ID>/manual-assign" \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "assign_grants": ["<GRANT_ID_1>", "<GRANT_ID_2>"]
  }'

That bulk endpoint is how you reorganize a fleet in one shot — pull a batch of grants out of the default workspace and drop them into a domain-scoped one. The 500-per-list cap is generous enough that most reorganizations are a single request.

For a single account, the CLI has a dedicated verb that's the one I actually reach for:

nylas agent account move support@yourapp.nylas.email --workspace <WORKSPACE_ID>

You can pass the agent's email or its ID. Under the hood agent account move calls the same manual-assign API, so "the target workspace's policy and rules govern the account immediately" — the move is the reconfiguration. There's no separate apply step.

If you'd rather move a grant by editing it directly, PATCH /v3/grants/{grant_id} with a new workspace_id also works, and POST /v3/connect/custom accepts a workspace_id to place an account explicitly at creation time. Three paths to the same field; pick the one that fits where you are.

Guardrails worth knowing

A few things that'll save you a confused afternoon:

The default workspace is special. Every application has exactly one, created automatically, and it's where any unassigned, non-auto-grouped account lands. You can update its policy_id and rule_ids — so attaching a policy there covers all your stragglers in one place — but its name, domain, and auto_group are locked, and you can't delete it. Treat it as the catch-all, and attach a sensible default policy to it early.

Manual-assign needs auto_group: false. The manual-assign endpoint operates on workspaces whose auto_group is false. Auto-grouped workspaces are meant to fill by domain match, not by hand, so the two assignment models stay separate by design.

One workspace per archetype, not per account. The temptation is to over-segment. Resist it. A sales-outreach fleet and a support-triage fleet have genuinely different send limits and spam tolerances, so they each deserve their own workspace and policy. But ten near-identical support agents share one. The right granularity is "groups that need different rules," not "every individual."

Plan the domain map first. Because domain is immutable, sketch which sending domains map to which workspaces before you provision anything. Getting this wrong isn't catastrophic — you delete and recreate — but it's friction you can avoid with five minutes of planning.

What's next

The mental shift is small but it compounds: stop thinking about configuring agents, start thinking about configuring the groups agents belong to. Scope a workspace to a domain, attach a policy once, and the fleet inherits — today's accounts and tomorrow's alike.