From 6aef425fcf272040b9cb5b84bae333d73d1b9eae Mon Sep 17 00:00:00 2001 From: Matt Rubens <2600+mrubens@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:43:21 -0400 Subject: [PATCH 1/3] feat: support inbox-scoped AgentMail API keys An inbox-scoped key passes every inbox check and is then refused on the organization-level webhook endpoints, which read as a permissions problem even for a full-access key. The save now falls back to the inbox's own webhook endpoints on that refusal, records the detected scope so status and disconnect address the same endpoints, and the error names the exact request AgentMail refused when the fallback does not apply. Also fixes the doubled "(optional)" on the Pod ID field and teaches the mock AgentMail server the inbox-scoped webhook routes. --- apps/docs/environment-variables.mdx | 1 + .../providers/communications/agentmail.mdx | 10 +- .../settings/CommsProviderSection.tsx | 8 + .../web/src/trpc/commands/comms/index.test.ts | 135 +++++++++++ apps/web/src/trpc/commands/comms/index.ts | 141 ++++++++++-- .../__tests__/agentmail-api-client.test.ts | 47 ++++ .../__tests__/mock-agentmail-server.test.ts | 72 ++++++ .../communication/src/agentmail-provider.ts | 45 +++- .../src/mock-agentmail-server.ts | 217 +++++++++++------- .../src/lib/agentmail-runtime-credentials.ts | 18 ++ packages/env/src/index.ts | 3 + packages/types/src/control-plane-env-vars.ts | 1 + 12 files changed, 586 insertions(+), 112 deletions(-) diff --git a/apps/docs/environment-variables.mdx b/apps/docs/environment-variables.mdx index dbeddba413..2f6515bbc6 100644 --- a/apps/docs/environment-variables.mdx +++ b/apps/docs/environment-variables.mdx @@ -366,6 +366,7 @@ as per-task auth tokens or workspace paths. | `R_AGENTMAIL_WEBHOOK_SECRET` | Optional | AgentMail webhook secret. Overrides the value managed by the settings UI. | | `R_AGENTMAIL_INBOX_ID` | Optional | AgentMail deployment inbox. Overrides the inbox connected in the settings UI. | | `R_AGENTMAIL_POD_ID` | Optional | AgentMail pod the inbox and webhook live in; required with a pod-scoped API key. Overrides the pod entered in the settings UI. | +| `R_AGENTMAIL_KEY_SCOPE` | Optional | `inbox` when the AgentMail API key is inbox-scoped, so the webhook is managed on the inbox. Detected and saved by the settings UI; set it only for env-var-only setups. | | `AGENTMAIL_API_BASE_URL` | Optional | AgentMail API base URL override, primarily for testing. Defaults to `https://api.agentmail.to`. | | `R_MICROSOFT_CLIENT_ID` | Microsoft sign-in | Microsoft OAuth client ID. | | `R_MICROSOFT_CLIENT_SECRET` | Microsoft sign-in | Microsoft OAuth client secret. | diff --git a/apps/docs/providers/communications/agentmail.mdx b/apps/docs/providers/communications/agentmail.mdx index d935adbd57..83d433fb31 100644 --- a/apps/docs/providers/communications/agentmail.mdx +++ b/apps/docs/providers/communications/agentmail.mdx @@ -35,9 +35,17 @@ The API key must carry these AgentMail permissions (or be a full-access key): `inbox_read`, `inbox_create`, `inbox_update`, `webhook_read`, `webhook_create`, `webhook_update`, `webhook_delete`, `message_read`, and `message_send`. Missing permissions fail at save time with an error naming -the refused step, except `message_send`, which has no side-effect-free +the refused request, except `message_send`, which has no side-effect-free check and is exercised on the first reply. +The key can be an organization-level key or an inbox-scoped key. An +inbox-scoped key (created from inside the inbox in the AgentMail console) is +the least-privilege choice for a one-inbox deployment: Roomote detects it on +save and registers the webhook on the inbox itself instead of at the +organization level. Enter that inbox's address, or leave the field blank +when it is the only inbox the key can see. A pod-scoped key needs the pod id +(see [Pods and pod-scoped keys](#pods-and-pod-scoped-keys)). + In the Roomote UI (**Settings > Communications > Email (AgentMail)**), paste the API key, then save. Roomote proposes a deployment inbox address such as `roomote-yourhost-a1b2c3@agentmail.to`; edit the address before creation, or diff --git a/apps/web/src/components/settings/CommsProviderSection.tsx b/apps/web/src/components/settings/CommsProviderSection.tsx index c7f4fcf956..aab95b5c46 100644 --- a/apps/web/src/components/settings/CommsProviderSection.tsx +++ b/apps/web/src/components/settings/CommsProviderSection.tsx @@ -310,6 +310,14 @@ function AgentMailSetupStatus({
) : null} + {status.keyScope === 'inbox' ? ( ++ Inbox-scoped API key: the webhook is registered on the inbox. +
+- Pod: {status.podId} -
-- Inbox-scoped API key: the webhook is registered on the inbox. -
-- The inbox Roomote receives mail at. Leave it unset to let Roomote adopt - the account's only inbox or create one when you save. -
- {showManualInput ? ( -- Enter the AgentMail API key above to choose from the - account's inboxes. -
- )} -- {loadInboxes.error.message} -
-