{"slug": "run-a-shared-team-inbox-owned-by-an-ai-agent", "title": "Run a shared team inbox owned by an AI agent", "summary": "Nylas introduced Agent Accounts, which allow an AI agent to share a real team mailbox with human users via bidirectional sync between the Nylas API and standard IMAP/SMTP clients. The agent can triage and draft messages while humans review and approve from their existing mail client, using shared folders as a queue. This avoids building a separate mail client and enables genuine human-agent collaboration on team inboxes like support@yourcompany.com.", "body_md": "A shared inbox where the agent drafts and humans approve from Outlook is a powerful hybrid — and it's the part of \"AI email\" that most demos quietly skip.\n\nMost \"AI email\" demos point a model at a human's personal inbox: connect a Gmail grant, summarize threads, maybe auto-draft a reply that nobody actually trusts enough to send. That's fine for a personal assistant. It falls apart the moment you want the agent to *be a participant* on a real team mailbox — `support@yourcompany.com`\n\n— where five humans and one agent all need to see the same messages, the same folders, and the same draft sitting in the queue.\n\nThe naive fix is to give the agent its own service and bolt a UI on top. Now you're building a mail client. You've got a database of \"agent state,\" a sync loop, a permissions layer, and a frontend nobody asked for, all to reimplement what Outlook already does well.\n\nThere's a better shape. A **Nylas Agent Account** is a real mailbox you can drive two ways at once: the agent works it over the **API**, and your humans work it from their normal mail client over **IMAP/SMTP**. Both surfaces read and write the *same* storage. A draft the agent creates via the API shows up in the Drafts folder in Apple Mail. A message a human drags into \"Needs review\" in Outlook shows up in the API's `folders`\n\nfield within seconds. That bidirectional sync is the whole trick, and it's what makes genuine human+agent collaboration possible instead of two systems fighting over one inbox.\n\nI work on the Nylas CLI, so the terminal commands below are the exact ones I reach for when I'm wiring this up. Every step shows the raw `curl`\n\ncall and the `nylas`\n\nequivalent, because you'll want the API in your service and the CLI on your laptop.\n\nAn Agent Account is just a **grant** — the same `grant_id`\n\nabstraction you already use for every other Nylas mailbox. Nothing new to learn on the data plane:\n\nThe model is: the agent triages and drafts, a human reviews and approves from the comfort of their mail client, and the queue between them is just a folder both sides can see.\n\n`message.created`\n\nwebhooks for inbound mail and reads/writes the real mailbox.One honest caveat up front, because it matters for how you scope this: **this is not a multi-user ACL system.** There's one app password for the mailbox, and everyone who connects a client shares it. You can't grant Alice read-only and Bob send. If you need per-person permissions, that lives in *your* application layer in front of the API, not in the shared inbox. I'll come back to the security tradeoff at the end.\n\nYou need:\n\n`*.nylas.email`\n\ntrial subdomain. New domains warm over roughly four weeks, so don't provision one the morning of a launch.`support@yourcompany.com`\n\n.The API base host in these examples is `https://api.us.nylas.com`\n\n(use the EU host if your app is in the EU region), and every request authenticates with `Authorization: Bearer <NYLAS_API_KEY>`\n\n.\n\nIf this is your first Agent Account, read the [Agent Accounts quickstart](https://developer.nylas.com/docs/v3/getting-started/agent-accounts/) first. The walkthrough below assumes you've at least skimmed it.\n\nAn Agent Account is created with `POST /v3/connect/custom`\n\n, `\"provider\": \"nylas\"`\n\n, and the email on your registered domain. No OAuth, no refresh token — it's a Nylas-hosted mailbox. The optional top-level `name`\n\nsets the display name humans see in the From field.\n\n```\ncurl --request POST \\\n  --url \"https://api.us.nylas.com/v3/connect/custom\" \\\n  --header \"Authorization: Bearer <NYLAS_API_KEY>\" \\\n  --header \"Content-Type: application/json\" \\\n  --data '{\n    \"provider\": \"nylas\",\n    \"name\": \"Support Team\",\n    \"settings\": {\n      \"email\": \"support@yourcompany.com\",\n      \"app_password\": \"Sh4redInb0xReview2024!\"\n    }\n  }'\n```\n\nNotice I'm setting `app_password`\n\nright in the create call. That's the credential your humans use to connect their mail clients (more on it next). The CLI does the same thing in one line:\n\n```\nnylas agent account create support@yourcompany.com \\\n  --name \"Support Team\" \\\n  --app-password \"Sh4redInb0xReview2024!\"\n```\n\nThe response gives you a `grant_id`\n\n. That's the only identifier the agent side needs from here on.\n\n**Workspaces, briefly.** When you create the account, the API auto-creates a default workspace and policy for it. If you run several agent inboxes — say a sales-outreach group and a support-triage group with different send limits — you can drop this account into a specific workspace at creation by passing a `workspace_id`\n\nin the `POST /v3/connect/custom`\n\nbody. The CLI `create`\n\ncommand has no `--workspace`\n\nflag; to attach a custom policy after the fact you use `nylas workspace update <workspace-id> --policy-id <policy-id>`\n\n. For a single shared inbox the default workspace is fine — you can ignore this entirely.\n\nThe app password is what turns an API-only mailbox into a human+agent one. Until you set it, Nylas rejects every IMAP `LOGIN`\n\nand SMTP `AUTH`\n\nattempt. I set it at creation above, but you can also set or rotate it on an existing account.\n\nOver the API, a `PATCH`\n\nto the grant replaces the whole `settings`\n\nobject, so send the `email`\n\nalongside the new password:\n\n```\ncurl --request PATCH \\\n  --url \"https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>\" \\\n  --header \"Authorization: Bearer <NYLAS_API_KEY>\" \\\n  --header \"Content-Type: application/json\" \\\n  --data '{\n    \"settings\": {\n      \"email\": \"support@yourcompany.com\",\n      \"app_password\": \"Sh4redInb0xReview2024!\"\n    }\n  }'\n```\n\nThe CLI has a dedicated command for it:\n\n```\nnylas agent account update support@yourcompany.com \\\n  --app-password \"Sh4redInb0xReview2024!\"\n```\n\nThe password must be 18–40 characters, printable ASCII, with at least one uppercase, one lowercase, and one digit. Nylas stores it as a bcrypt hash — you can't read it back, only reset it. Rotating it disconnects any live mail client, and each one reconnects after the user types the new value.\n\nNow hand your team these connection settings. They're exact, straight from the Nylas mail-client docs:\n\n| Setting | Value |\n|---|---|\n| IMAP server |\n`mail.us.nylas.email` (US) · `mail.eu.nylas.email` (EU) |\n| IMAP port |\n`993` (implicit TLS) |\n| SMTP server |\n`mail.us.nylas.email` (US) · `mail.eu.nylas.email` (EU) |\n| SMTP port |\n`465` (implicit TLS) or `587` (STARTTLS) |\n| Username | The account email, e.g. `support@yourcompany.com`\n|\n| Password | The app password you just set |\n\nMost clients auto-detect TLS. If yours asks, pick SSL/TLS for 993 and 465, STARTTLS for 587. With IMAP IDLE, clients see new mail pushed in without polling — so when the agent moves a message, the human's Outlook updates on its own.\n\nThis is the part worth internalizing, because everything else builds on it. *IMAP and the API are not two copies that sync — they're two doors into the same room.* Every user-visible action flows through the same storage and fires the same webhook, regardless of which door it came through:\n\n`starred`\n\nfield flips to `true`\n\nand a `message.updated`\n\nwebhook fires.`folders`\n\narray updates.Propagation is seconds, not minutes. That tight loop is exactly what you need for \"agent drafts, human approves\" to feel like one workflow instead of two disconnected ones.\n\nThe cleanest way to coordinate agent and humans is a shared work queue, and the simplest queue is a folder. Create a \"Needs review\" folder once; both sides can see it.\n\nOver the API:\n\n```\ncurl --request POST \\\n  --url \"https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/folders\" \\\n  --header \"Authorization: Bearer <NYLAS_API_KEY>\" \\\n  --header \"Content-Type: application/json\" \\\n  --data '{ \"name\": \"Needs review\" }'\n```\n\nOr from the terminal:\n\n```\nnylas email folders create \"Needs review\" <NYLAS_GRANT_ID>\n```\n\nBoth return the folder's ID. Stash it — the agent needs it to route messages, and it shows up as a mailbox in everyone's mail client automatically.\n\nWhen inbound mail arrives, the agent decides what it can't handle alone and moves those messages into the queue. A message's folder membership lives in its `folders`\n\narray, so you update the message with `PUT /v3/grants/{grant_id}/messages/{message_id}`\n\n:\n\n```\ncurl --request PUT \\\n  --url \"https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>\" \\\n  --header \"Authorization: Bearer <NYLAS_API_KEY>\" \\\n  --header \"Content-Type: application/json\" \\\n  --data '{ \"folders\": [\"<NEEDS_REVIEW_FOLDER_ID>\"] }'\n```\n\nFrom the CLI it's one command:\n\n```\nnylas email move <MESSAGE_ID> --folder <NEEDS_REVIEW_FOLDER_ID> <NYLAS_GRANT_ID>\n```\n\nThe instant that runs, the message disappears from INBOX and shows up under \"Needs review\" in your team's Outlook. The folder *is* the handoff.\n\nHere's the core workflow. The agent never sends on its own — it writes a **draft** and leaves it for a human. Drafting is a first-class endpoint: `POST /v3/grants/{grant_id}/drafts`\n\n.\n\n```\ncurl --request POST \\\n  --url \"https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/drafts\" \\\n  --header \"Authorization: Bearer <NYLAS_API_KEY>\" \\\n  --header \"Content-Type: application/json\" \\\n  --data '{\n    \"to\": [{ \"email\": \"customer@example.com\" }],\n    \"subject\": \"Re: Trouble resetting my password\",\n    \"body\": \"Hi there — I can see your account is locked after several failed attempts. I have unlocked it; you should be able to reset now. Let me know if it sticks.\"\n  }'\n```\n\nThe CLI version, the one I use when I'm testing a prompt by hand:\n\n```\nnylas email drafts create <NYLAS_GRANT_ID> \\\n  --to customer@example.com \\\n  --subject \"Re: Trouble resetting my password\" \\\n  --body \"Hi there — I can see your account is locked after several failed attempts. I have unlocked it; you should be able to reset now. Let me know if it sticks.\"\n```\n\nThat draft now sits in the Drafts folder. Because IMAP and the API share state, your reviewer opens Outlook, sees the agent's draft, edits a sentence if they want, and hits send — entirely inside their mail client. No bespoke approval UI, no extra service. The human's mail client is the approval step.\n\nIf you'd rather the agent send *on approval* — say a reviewer clicks \"approve\" in your own tool and your backend sends the agent's draft — you send the draft by ID with `POST /v3/grants/{grant_id}/drafts/{draft_id}`\n\n(the send-draft endpoint), or from the CLI:\n\n```\nnylas email drafts send <DRAFT_ID> <NYLAS_GRANT_ID>\n```\n\nBoth paths are valid; pick based on where you want the human's \"yes\" to live. For replies that need to stay threaded, the CLI has a shortcut that fetches the original to preserve the subject and threading headers:\n\n```\nnylas email reply <MESSAGE_ID> --body \"Thanks for the patience — all set now.\" <NYLAS_GRANT_ID>\n```\n\nThe agent reads the mailbox like any grant. List recent messages with `GET /v3/grants/{grant_id}/messages`\n\n:\n\n```\ncurl --request GET \\\n  --url \"https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages?limit=10\" \\\n  --header \"Authorization: Bearer <NYLAS_API_KEY>\"\n```\n\nOr:\n\n```\nnylas email list <NYLAS_GRANT_ID> --limit 10\n```\n\nTo pull one message's full content, including its body, fetch it by ID — `GET /v3/grants/{grant_id}/messages/{message_id}`\n\n— or `nylas email read <MESSAGE_ID> <NYLAS_GRANT_ID>`\n\n. That by-id fetch matters for webhooks, which is the next piece.\n\nYou don't want the agent polling. Inbound mail fires the standard `message.created`\n\nevent. One important detail: **webhooks are application-scoped, not grant-scoped.** You subscribe once at the app level with `POST /v3/webhooks`\n\n, and events for every grant in your app land at that one endpoint, each payload carrying the `grant_id`\n\nso you can route it to the right inbox.\n\nA few things to get right:\n\n`GET /v3/grants/{grant_id}/messages/{message_id}`\n\nwhen you need the content, and branch on `message.created.truncated`\n\n— Nylas uses that event type for very large messages, at which point you re-fetch by ID anyway. Treating the by-id fetch as your source of truth keeps you correct in both cases.`id`\n\n.`id`\n\nis constant across all retries of a single event — it's your dedup key. You can additionally guard on the inner message id (`data.object.id`\n\n) so you never act twice on the same message.`X-Nylas-Signature`\n\nheader: a hex HMAC-SHA256 of the `crypto.timingSafeEqual`\n\n, but check that both buffers are the same length first — it throws on a length mismatch. Locally, `nylas webhook verify`\n\nruns the same check so you can sanity-test before deploying.With that in place, the loop closes: mail arrives → `message.created`\n\n→ agent fetches the body → agent triages, drafts, and moves to \"Needs review\" → a human approves from Outlook. Humans and agent, same mailbox, in real time.\n\nA few things I'd want a teammate to know before they ship this:\n\n`DELETE /v3/grants/{grant_id}/messages/{message_id}`\n\ntrashes a message unless you pass `?hard_delete=true`\n\n. The CLI `nylas email delete`\n\nonly trashes. The only true full wipe of a mailbox is deleting the grant itself.None of these are dealbreakers — they're just the edges. Knowing where they are means you won't design yourself into a corner expecting the inbox to be something it isn't.\n\n`nylas`\n\ncommand shown above, with its flags.Build the agent against the API, hand your team an app password, and let the queue between them be a folder both sides can see. That's the whole shared inbox.\n\nWhen this post is published, link AI agents and crawlers to the retrieval-ready version on `cli.nylas.com`\n\n:", "url": "https://wpnews.pro/news/run-a-shared-team-inbox-owned-by-an-ai-agent", "canonical_source": "https://dev.to/mqasimca/run-a-shared-team-inbox-owned-by-an-ai-agent-3md4", "published_at": "2026-07-10 11:56:20+00:00", "updated_at": "2026-07-10 12:15:12.945746+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-products"], "entities": ["Nylas", "Outlook", "Apple Mail", "Gmail"], "alternates": {"html": "https://wpnews.pro/news/run-a-shared-team-inbox-owned-by-an-ai-agent", "markdown": "https://wpnews.pro/news/run-a-shared-team-inbox-owned-by-an-ai-agent.md", "text": "https://wpnews.pro/news/run-a-shared-team-inbox-owned-by-an-ai-agent.txt", "jsonld": "https://wpnews.pro/news/run-a-shared-team-inbox-owned-by-an-ai-agent.jsonld"}}