{"slug": "organize-your-agents-into-workspaces-by-domain", "title": "Organize your agents into workspaces by domain", "summary": "Nylas introduces workspaces to manage agent accounts at scale, grouping grants under a single policy and rule set. Workspaces allow automatic assignment of new accounts based on email domain, eliminating the need to configure each grant individually. The system uses auto-grouping to ensure consistent policy inheritance across fleets.", "body_md": "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.\n\nConfiguring 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.\n\nI work on the Nylas CLI, so the terminal commands below are the exact ones I reach for. Every one is checked against `nylas`\n\nv3.1.27, and every `curl`\n\ncall 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.\n\nIf you've used Agent Accounts, you already know the spine: an Agent Account *is* a grant. It carries a `grant_id`\n\nand 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`\n\n, same as any grant.\n\nWorkspaces add one field to that picture. Every grant carries a `workspace_id`\n\n, and the workspace it points at holds a `policy_id`\n\n(limits and spam settings) and a `rule_ids`\n\narray (inbound and outbound mail filtering). The chain is short:\n\n```\nList  → holds values (domains, TLDs, addresses)\nRule  → match conditions + actions; references lists via in_list\nPolicy→ send caps, storage, retention, spam detection\nWorkspace → one policy_id + an array of rule_ids\nGrant → carries workspace_id → inherits all of the above\n```\n\nChange 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.\n\nThis 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:\n\n`POST /v3/connect/custom`\n\npayload includes a `workspace_id`\n\n, the grant joins that workspace immediately. No guessing.`workspace_id`\n\nwas passed, Nylas looks for a custom workspace whose `domain`\n\nmatches the new account's email domain `auto_group`\n\nis `true`\n\n. If one matches, the grant joins it.Step 2 is the interesting one. `auto_group`\n\nis a boolean on the workspace, and the spec describes it plainly: when `true`\n\n, \"newly created grants in the application are automatically assigned to the workspace if their email address' domain matches the `domain`\n\n.\" So if you create a workspace with `domain: \"support.yourcompany.com\"`\n\nand `auto_group: true`\n\n, then every account you later provision as `triage@support.yourcompany.com`\n\nor `escalations@support.yourcompany.com`\n\njoins that workspace and inherits its policy without you passing a single extra field. The CLI `agent account create`\n\ncommand deliberately has no `--workspace`\n\nflag — auto-group is meant to carry that weight. You set the domain rule once and let provisioning be dumb.\n\nThat'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`\n\nagent should get. They create the account; the domain match routes it.\n\nYou'll need:\n\n`https://api.us.nylas.com`\n\nand `Authorization: Bearer <NYLAS_API_KEY>`\n\n.`*.nylas.email`\n\ntrial subdomain. Workspaces key off this domain, so it has to be one you actually provision accounts under. New domains warm over roughly four weeks.`nylas init`\n\n) if you want to follow the terminal half.New to Agent Accounts? Start with the [Agent Accounts overview](https://developer.nylas.com/docs/v3/agent-accounts/) and the [provisioning guide](https://developer.nylas.com/docs/v3/agent-accounts/provisioning/) to create your first account, then come back here to organize them.\n\nA workspace create takes two required fields — `name`\n\nand `domain`\n\n— plus three optional ones: `auto_group`\n\n, `policy_id`\n\n, and `rule_ids`\n\n. The `domain`\n\nties the workspace to one email domain, and `auto_group`\n\n(which defaults to `true`\n\n) decides whether matching accounts join automatically.\n\nHere's the full version: a workspace scoped to a domain, with a policy and a rule attached in the same call.\n\n```\ncurl --request POST \\\n  --url \"https://api.us.nylas.com/v3/workspaces\" \\\n  --header 'Accept: application/json' \\\n  --header 'Authorization: Bearer <NYLAS_API_KEY>' \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n    \"name\": \"Support fleet\",\n    \"domain\": \"support.yourcompany.com\",\n    \"auto_group\": true,\n    \"policy_id\": \"<POLICY_ID>\",\n    \"rule_ids\": [\"<RULE_ID>\"]\n  }'\n```\n\nThe response returns a Workspace object whose `workspace_id`\n\nis the value you'll store on grants. From the terminal, the same thing:\n\n```\nnylas workspace create \\\n  --name \"Support fleet\" \\\n  --domain support.yourcompany.com \\\n  --auto-group \\\n  --policy-id <POLICY_ID>\n```\n\nA couple of honest caveats on that CLI form. `nylas workspace create`\n\naccepts `--name`\n\n, `--domain`\n\n, `--auto-group`\n\n, and `--policy-id`\n\n. It does *not* take a `--rule-ids`\n\nflag at create time — to attach rules you run an update afterward (shown below). And the `--auto-group`\n\nflag is a switch: include it to enable auto-grouping, leave it off otherwise. Both `--name`\n\nand `--domain`\n\nare required by the CLI, matching the API.\n\nOne 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.\n\nYou also don't have to attach a policy at creation. A workspace with no `policy_id`\n\nruns 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.\n\nThis 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.\n\nA 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`\n\nor `assign_to_folder`\n\n. 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\n\n`POST /v3/rules`\n\nall day and nothing happens to your mail until the rule's ID lands in a workspace's array.To attach or change either, `PATCH`\n\nthe workspace and include only the fields you're changing:\n\n```\ncurl --request PATCH \\\n  --url \"https://api.us.nylas.com/v3/workspaces/<WORKSPACE_ID>\" \\\n  --header 'Accept: application/json' \\\n  --header 'Authorization: Bearer <NYLAS_API_KEY>' \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n    \"policy_id\": \"<POLICY_ID>\",\n    \"rule_ids\": [\"<RULE_ID_1>\", \"<RULE_ID_2>\"]\n  }'\n```\n\nThe PATCH semantics are worth internalizing because they're how you swap configuration across a fleet without churn:\n\n`policy_id`\n\ntakes one UUID string; `rule_ids`\n\ntakes an array.`null`\n\n`policy_id: null`\n\ndetaches the policy (the fleet drops back to plan maximums); `rule_ids: null`\n\nremoves every rule.From the terminal, the same update:\n\n```\nnylas workspace update <WORKSPACE_ID> \\\n  --policy-id <POLICY_ID> \\\n  --rules-ids rule-id-1,rule-id-2\n```\n\nThe CLI flag is `--rules-ids`\n\n(comma-separated, note the plural), and `--policy-id`\n\nwith an empty string — `--policy-id \"\"`\n\n— detaches the policy, the terminal equivalent of sending `null`\n\n. To see what you've got across the whole application, list every workspace and inspect one:\n\n```\ncurl --request GET \\\n  --url \"https://api.us.nylas.com/v3/workspaces\" \\\n  --header 'Authorization: Bearer <NYLAS_API_KEY>'\n\ncurl --request GET \\\n  --url \"https://api.us.nylas.com/v3/workspaces/<WORKSPACE_ID>\" \\\n  --header 'Authorization: Bearer <NYLAS_API_KEY>'\n```\n\nThe CLI mirrors both:\n\n```\nnylas workspace list\nnylas workspace get <WORKSPACE_ID>\n```\n\nEvery account whose `workspace_id`\n\npoints at this workspace is now governed by that one policy and that one rule set. Provision a new `@support.yourcompany.com`\n\nagent tomorrow and it inherits both the moment auto-group routes it in. You never touch the new grant directly.\n\nAuto-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.\n\nThe API path is the manual-assign endpoint, `POST /v3/workspaces/{workspace_id}/manual-assign`\n\n. Send grant IDs in `assign_grants`\n\n, `remove_grants`\n\n, 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:\n\n```\ncurl --request POST \\\n  --url \"https://api.us.nylas.com/v3/workspaces/<TARGET_WORKSPACE_ID>/manual-assign\" \\\n  --header 'Accept: application/json' \\\n  --header 'Authorization: Bearer <NYLAS_API_KEY>' \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n    \"assign_grants\": [\"<GRANT_ID_1>\", \"<GRANT_ID_2>\"]\n  }'\n```\n\nThat 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.\n\nFor a single account, the CLI has a dedicated verb that's the one I actually reach for:\n\n```\nnylas agent account move support@yourapp.nylas.email --workspace <WORKSPACE_ID>\n```\n\nYou can pass the agent's email or its ID. Under the hood `agent account move`\n\ncalls 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.\n\nIf you'd rather move a grant by editing it directly, `PATCH /v3/grants/{grant_id}`\n\nwith a new `workspace_id`\n\nalso works, and `POST /v3/connect/custom`\n\naccepts a `workspace_id`\n\nto place an account explicitly at creation time. Three paths to the same field; pick the one that fits where you are.\n\nA few things that'll save you a confused afternoon:\n\n**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`\n\nand `rule_ids`\n\n— so attaching a policy there covers all your stragglers in one place — but its `name`\n\n, `domain`\n\n, and `auto_group`\n\nare locked, and you can't delete it. Treat it as the catch-all, and attach a sensible default policy to it early.\n\n**Manual-assign needs auto_group: false.** The manual-assign endpoint operates on workspaces whose\n\n`auto_group`\n\nis `false`\n\n. 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.\"\n\n**Plan the domain map first.** Because `domain`\n\nis 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.\n\n`auto_group`\n\n.`nylas workspace`\n\nand `nylas agent account`\n\ncommand reference.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.", "url": "https://wpnews.pro/news/organize-your-agents-into-workspaces-by-domain", "canonical_source": "https://dev.to/mqasimca/organize-your-agents-into-workspaces-by-domain-5dl7", "published_at": "2026-06-26 17:38:45+00:00", "updated_at": "2026-06-26 18:03:33.515639+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-infrastructure"], "entities": ["Nylas", "Nylas CLI", "OpenAPI"], "alternates": {"html": "https://wpnews.pro/news/organize-your-agents-into-workspaces-by-domain", "markdown": "https://wpnews.pro/news/organize-your-agents-into-workspaces-by-domain.md", "text": "https://wpnews.pro/news/organize-your-agents-into-workspaces-by-domain.txt", "jsonld": "https://wpnews.pro/news/organize-your-agents-into-workspaces-by-domain.jsonld"}}