From c0d74ac62ac49bd42722a3cde10ca7dc785da704 Mon Sep 17 00:00:00 2001 From: Sidharth Reddy <70673367+sidharth0612@users.noreply.github.com> Date: Sun, 20 Sep 2026 18:33:25 -0700 Subject: [PATCH 1/3] docs(accounts): Update Account, pod and inbox account routes, account_update permission Documents the account update API and the scoped account routes: POST /v0/accounts/{account_id}/update GET /v0/pods/{pod_id}/accounts GET /v0/pods/{pod_id}/accounts/{account_id} POST /v0/pods/{pod_id}/accounts/{account_id}/update GET /v0/inboxes/{inbox_id}/accounts GET /v0/inboxes/{inbox_id}/accounts/{account_id} POST /v0/inboxes/{inbox_id}/accounts/{account_id}/update Account gains a sparse `status` (present only as `disabled`) and `disabled_at`. The update request takes its own enum, `enabled | disabled`, so re-enabling needs no null and existing rows need no backfill. Adds the `account_update` permission to ApiKeyPermissions, a changelog entry, and a pointer from the AgentID sign-in guide. openapi/openapi.yml carries only the account-related hunks of a fresh export; the pre-existing drift on main is left for the CI regeneration. The seven new operations are annotated in the CLI overrides and bin/gen-cli-overrides.py --check passes. Co-Authored-By: Claude Fable 5.1 --- fern/apis/api/definition/accounts.yml | 57 ++- fern/apis/api/definition/api-keys.yml | 5 + .../api/definition/inboxes/__package__.yml | 1 + fern/apis/api/definition/inboxes/accounts.yml | 62 +++ fern/apis/api/definition/pods/__package__.yml | 1 + fern/apis/api/definition/pods/accounts.yml | 62 +++ fern/apis/cli/openapi-overrides.yml | 41 ++ fern/changelog/2026-09-16.mdx | 75 +++ fern/pages/guides/agentid-sign-in.mdx | 2 +- openapi/openapi.yml | 436 +++++++++++++++++- 10 files changed, 739 insertions(+), 3 deletions(-) create mode 100644 fern/apis/api/definition/inboxes/accounts.yml create mode 100644 fern/apis/api/definition/pods/accounts.yml create mode 100644 fern/changelog/2026-09-16.mdx diff --git a/fern/apis/api/definition/accounts.yml b/fern/apis/api/definition/accounts.yml index 5e128096..70fa5349 100644 --- a/fern/apis/api/definition/accounts.yml +++ b/fern/apis/api/definition/accounts.yml @@ -14,6 +14,22 @@ types: type: uuid docs: ID of provider. + AccountStatus: + enum: + - disabled + docs: | + `disabled` refuses every new sign-in for the account until it is + re-enabled. Access tokens already issued are not revoked. + + UpdateAccountStatus: + enum: + - enabled + - disabled + docs: | + Status to set. `disabled` stops the inbox from signing in at the + provider; `enabled` re-enables it. An enabled account reads back with no + `status`. + Account: docs: One inbox signed in at one provider. properties: @@ -34,6 +50,12 @@ types: sign_in_count: type: integer docs: Number of sign-ins at provider. + status: + type: optional + docs: Present only while the account is disabled. Absent means the inbox may sign in. + disabled_at: + type: optional + docs: Time at which the account was disabled. Present only while `status` is `disabled`. ListAccountsResponse: properties: @@ -42,6 +64,14 @@ types: next_page_token: optional accounts: list + UpdateAccountRequest: + docs: | + Fields to change on the account. Fields you omit are left unchanged and + unknown fields are rejected. Expects at least one field; today that is + `status`. + properties: + status: optional + service: url: Http base-path: /accounts @@ -52,7 +82,10 @@ service: method: GET path: "" display-name: List Accounts - docs: Lists accounts across all providers. + docs: | + Lists accounts across all providers, scoped to the API key: an + organization key sees every account, a pod key its pod's, an inbox key + its inbox's. Requires `inbox_read`. request: name: ListAccountsRequest query-parameters: @@ -67,8 +100,30 @@ service: method: GET path: /{account_id} display-name: Get Account + docs: | + Returns one account by ID. An account outside the key's scope is a 404. + Requires `inbox_read`. path-parameters: account_id: AccountId response: Account errors: - global.NotFoundError + + update: + method: POST + path: /{account_id}/update + display-name: Update Account + docs: | + Updates one account. Set `status` to `disabled` to stop the inbox from + signing in at the provider again, or to `enabled` to re-enable it. + Idempotent: disabling an already disabled account keeps its original + `disabled_at`, and enabling an enabled account is a no-op. An account + outside the key's scope is a 404. Requires `account_update`. + path-parameters: + account_id: AccountId + request: UpdateAccountRequest + response: Account + errors: + - global.ValidationError + - global.NotFoundError + - global.ConflictError diff --git a/fern/apis/api/definition/api-keys.yml b/fern/apis/api/definition/api-keys.yml index cc2b5f57..56052457 100644 --- a/fern/apis/api/definition/api-keys.yml +++ b/fern/apis/api/definition/api-keys.yml @@ -246,6 +246,11 @@ types: docs: | Share the organization owner's name and email with providers at sign-in. One permission for both values. + account_update: + type: optional + docs: | + Update accounts: disable or re-enable an inbox's sign-in at a provider. Reading accounts + needs only `inbox_read`. pod_read: type: optional docs: Read pods. diff --git a/fern/apis/api/definition/inboxes/__package__.yml b/fern/apis/api/definition/inboxes/__package__.yml index fb586808..c42387eb 100644 --- a/fern/apis/api/definition/inboxes/__package__.yml +++ b/fern/apis/api/definition/inboxes/__package__.yml @@ -9,6 +9,7 @@ navigation: - metrics.yml - events.yml - api-keys.yml + - accounts.yml imports: global: ../__package__.yml diff --git a/fern/apis/api/definition/inboxes/accounts.yml b/fern/apis/api/definition/inboxes/accounts.yml new file mode 100644 index 00000000..9808a6c2 --- /dev/null +++ b/fern/apis/api/definition/inboxes/accounts.yml @@ -0,0 +1,62 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json + +imports: + global: ../__package__.yml + inboxes: __package__.yml + accounts: ../accounts.yml + +service: + url: Http + base-path: /inboxes/{inbox_id}/accounts + path-parameters: + inbox_id: inboxes.InboxId + auth: true + + endpoints: + list: + method: GET + path: "" + display-name: List Accounts + docs: | + Lists accounts held by the inbox, across all providers. Requires `inbox_read`. + request: + name: ListAccountsRequest + query-parameters: + limit: optional + page_token: optional + ascending: optional + response: accounts.ListAccountsResponse + errors: + - global.ValidationError + + get: + method: GET + path: /{account_id} + display-name: Get Account + docs: | + Returns one account held by the inbox. An account elsewhere is a 404. Requires + `inbox_read`. + path-parameters: + account_id: accounts.AccountId + response: accounts.Account + errors: + - global.NotFoundError + + update: + method: POST + path: /{account_id}/update + display-name: Update Account + docs: | + Updates one account held by the inbox. Set `status` to `disabled` to stop the + inbox from signing in at the provider again, or to `enabled` to + re-enable it. Idempotent: disabling an already disabled account keeps + its original `disabled_at`, and enabling an enabled account is a no-op. + An account elsewhere is a 404. Requires `account_update`. + path-parameters: + account_id: accounts.AccountId + request: accounts.UpdateAccountRequest + response: accounts.Account + errors: + - global.ValidationError + - global.NotFoundError + - global.ConflictError diff --git a/fern/apis/api/definition/pods/__package__.yml b/fern/apis/api/definition/pods/__package__.yml index 9f796edd..9eba22fd 100644 --- a/fern/apis/api/definition/pods/__package__.yml +++ b/fern/apis/api/definition/pods/__package__.yml @@ -9,6 +9,7 @@ navigation: - lists.yml - metrics.yml - api-keys.yml + - accounts.yml imports: global: ../__package__.yml diff --git a/fern/apis/api/definition/pods/accounts.yml b/fern/apis/api/definition/pods/accounts.yml new file mode 100644 index 00000000..0a5dfca2 --- /dev/null +++ b/fern/apis/api/definition/pods/accounts.yml @@ -0,0 +1,62 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json + +imports: + global: ../__package__.yml + pods: __package__.yml + accounts: ../accounts.yml + +service: + url: Http + base-path: /pods/{pod_id}/accounts + path-parameters: + pod_id: pods.PodId + auth: true + + endpoints: + list: + method: GET + path: "" + display-name: List Accounts + docs: | + Lists accounts held by inboxes in the pod, across all providers. Requires `inbox_read`. + request: + name: ListAccountsRequest + query-parameters: + limit: optional + page_token: optional + ascending: optional + response: accounts.ListAccountsResponse + errors: + - global.ValidationError + + get: + method: GET + path: /{account_id} + display-name: Get Account + docs: | + Returns one account held by inboxes in the pod. An account elsewhere is a 404. Requires + `inbox_read`. + path-parameters: + account_id: accounts.AccountId + response: accounts.Account + errors: + - global.NotFoundError + + update: + method: POST + path: /{account_id}/update + display-name: Update Account + docs: | + Updates one account held by inboxes in the pod. Set `status` to `disabled` to stop the + inbox from signing in at the provider again, or to `enabled` to + re-enable it. Idempotent: disabling an already disabled account keeps + its original `disabled_at`, and enabling an enabled account is a no-op. + An account elsewhere is a 404. Requires `account_update`. + path-parameters: + account_id: accounts.AccountId + request: accounts.UpdateAccountRequest + response: accounts.Account + errors: + - global.ValidationError + - global.NotFoundError + - global.ConflictError diff --git a/fern/apis/cli/openapi-overrides.yml b/fern/apis/cli/openapi-overrides.yml index b7e008a3..38c1c201 100644 --- a/fern/apis/cli/openapi-overrides.yml +++ b/fern/apis/cli/openapi-overrides.yml @@ -906,3 +906,44 @@ paths: - pods - inboxes x-fern-sdk-method-name: search + /v0/accounts/{account_id}/update: + post: + x-fern-sdk-group-name: + - accounts + x-fern-sdk-method-name: update + /v0/inboxes/{inbox_id}/accounts: + get: + x-fern-sdk-group-name: + - inboxes + - accounts + x-fern-sdk-method-name: list + /v0/inboxes/{inbox_id}/accounts/{account_id}: + get: + x-fern-sdk-group-name: + - inboxes + - accounts + x-fern-sdk-method-name: get + /v0/inboxes/{inbox_id}/accounts/{account_id}/update: + post: + x-fern-sdk-group-name: + - inboxes + - accounts + x-fern-sdk-method-name: update + /v0/pods/{pod_id}/accounts: + get: + x-fern-sdk-group-name: + - pods + - accounts + x-fern-sdk-method-name: list + /v0/pods/{pod_id}/accounts/{account_id}: + get: + x-fern-sdk-group-name: + - pods + - accounts + x-fern-sdk-method-name: get + /v0/pods/{pod_id}/accounts/{account_id}/update: + post: + x-fern-sdk-group-name: + - pods + - accounts + x-fern-sdk-method-name: update diff --git a/fern/changelog/2026-09-16.mdx b/fern/changelog/2026-09-16.mdx new file mode 100644 index 00000000..cdbc5cd8 --- /dev/null +++ b/fern/changelog/2026-09-16.mdx @@ -0,0 +1,75 @@ +--- +tags: ["accounts-api", "pods-api", "inboxes-api", "new-feature"] +--- + +## Summary + +Disable an inbox's sign-in at a provider, and re-enable it later, with one call. Accounts now carry a `status`, and the accounts resource is reachable under pods and inboxes so an agent can audit and control sign-ins at exactly the scope its key holds. + +### What's new? + +**New endpoints:** + +- `POST /v0/accounts/{account_id}/update` - Update one account. Send `{ "status": "disabled" }` to refuse every new sign-in for that inbox at that provider, or `{ "status": "enabled" }` to re-enable it. Requires the new `account_update` permission. +- `GET /v0/pods/{pod_id}/accounts` and `GET /v0/pods/{pod_id}/accounts/{account_id}` - List and read the accounts held by inboxes in one pod. +- `POST /v0/pods/{pod_id}/accounts/{account_id}/update` - Update an account within one pod. +- `GET /v0/inboxes/{inbox_id}/accounts` and `GET /v0/inboxes/{inbox_id}/accounts/{account_id}` - List and read the accounts held by one inbox. +- `POST /v0/inboxes/{inbox_id}/accounts/{account_id}/update` - Update an account held by one inbox. + +**New features:** + +- **Account status**: a disabled account carries `status: "disabled"` and `disabled_at` on every read. An account without `status` can sign in. +- **Idempotent updates**: disabling an already disabled account keeps its original `disabled_at`, and enabling an enabled account is a no-op, so a retry never conflicts. +- **`account_update` permission**: a separate grant for the write. Unrestricted keys hold it; restricted keys get it only when granted. Reading accounts still needs only `inbox_read`. +- **Scoped views**: the pod and inbox forms return the same account object as `GET /v0/accounts`, narrowed to the pod or inbox in the path. An account outside that scope is a 404. + +### Use cases + +Build agents that: + +- Cut off a provider that is misbehaving, without deleting the sign-in record +- Pause one inbox's access to a provider during an incident and restore it afterward +- Audit, per pod or per inbox, which providers each agent has signed in to +- Give a tenant's key visibility into its own accounts and nothing beyond its pod + + +```python title="Python" +from agentmail import AgentMail + +client = AgentMail(api_key="your-api-key") + +# disable an inbox's sign-in at a provider +account = client.accounts.update( + account_id="3f1c2a4e-8b7d-4c6e-9a1f-2d3e4f5a6b7c", + status="disabled", +) +print(account.status, account.disabled_at) + +# list the accounts one inbox holds +accounts = client.inboxes.accounts.list(inbox_id="agent@agentmail.to") +for item in accounts.accounts: + print(item.provider_name, item.status or "ok") +``` + +```typescript title="TypeScript" +import { AgentMail } from "agentmail"; + +const client = new AgentMail({ apiKey: "your-api-key" }); + +// disable an inbox's sign-in at a provider +const account = await client.accounts.update("3f1c2a4e-8b7d-4c6e-9a1f-2d3e4f5a6b7c", { + status: "disabled", +}); +console.log(account.status, account.disabledAt); + +// list the accounts one inbox holds +const accounts = await client.inboxes.accounts.list("agent@agentmail.to"); +for (const item of accounts.accounts) { + console.log(item.providerName, item.status ?? "ok"); +} +``` + + + + See [Update Account](https://docs.agentmail.to/api-reference/accounts/update) in the API reference for the request and response shapes. + diff --git a/fern/pages/guides/agentid-sign-in.mdx b/fern/pages/guides/agentid-sign-in.mdx index 2b6d3a0a..c3120dba 100644 --- a/fern/pages/guides/agentid-sign-in.mdx +++ b/fern/pages/guides/agentid-sign-in.mdx @@ -152,7 +152,7 @@ if (key.type === "public_key") console.log(key.status); ``` -A single check reads the current status. Repeat with a delay while it is `pending`, stopping at expiry or an error. `active` means the key is ready to sign in as the inbox. Use [List Provider Accounts](https://docs.agentmail.to/api-reference/providers/list-accounts) to inspect accounts at the provider. +A single check reads the current status. Repeat with a delay while it is `pending`, stopping at expiry or an error. `active` means the key is ready to sign in as the inbox. Use [List Provider Accounts](https://docs.agentmail.to/api-reference/providers/list-accounts) to inspect accounts at the provider. To stop an inbox from signing in at a provider again, disable its account with [Update Account](https://docs.agentmail.to/api-reference/accounts/update). ## Manage sign-in keys diff --git a/openapi/openapi.yml b/openapi/openapi.yml index f62b1e97..699ec569 100644 --- a/openapi/openapi.yml +++ b/openapi/openapi.yml @@ -770,7 +770,10 @@ paths: $ref: '#/components/schemas/webhooksUpdateWebhookHeadersRequest' /v0/accounts: get: - description: Lists accounts across all providers. + description: |- + Lists accounts across all providers, scoped to the API key: an + organization key sees every account, a pod key its pod's, an inbox key + its inbox's. Requires `inbox_read`. operationId: accounts_list tags: - Accounts @@ -816,6 +819,9 @@ paths: - BearerAuth: [] /v0/accounts/{account_id}: get: + description: |- + Returns one account by ID. An account outside the key's scope is a 404. + Requires `inbox_read`. operationId: accounts_get tags: - Accounts @@ -846,6 +852,62 @@ paths: - url: https://api.agentmail.eu security: - BearerAuth: [] + /v0/accounts/{account_id}/update: + post: + description: |- + Updates one account. Set `status` to `disabled` to stop the inbox from + signing in at the provider again, or to `enabled` to re-enable it. + Idempotent: disabling an already disabled account keeps its original + `disabled_at`, and enabling an enabled account is a no-op. An account + outside the key's scope is a 404. Requires `account_update`. + operationId: accounts_update + tags: + - Accounts + parameters: + - name: account_id + in: path + required: true + schema: + $ref: '#/components/schemas/AccountId' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorResponse' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + summary: Update Account + servers: + - url: https://api.agentmail.to + - url: https://x402.api.agentmail.to + - url: https://mpp.api.agentmail.to + - url: https://api.agentmail.eu + security: + - BearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateAccountRequest' /v0/agent/sign-up: post: description: >- @@ -1693,6 +1755,167 @@ paths: - url: https://api.agentmail.eu security: - BearerAuth: [] + /v0/inboxes/{inbox_id}/accounts: + get: + description: >- + Lists accounts held by the inbox, across all providers. Requires + `inbox_read`. + operationId: inboxes_accounts_list + tags: + - InboxesAccounts + parameters: + - name: inbox_id + in: path + required: true + schema: + $ref: '#/components/schemas/inboxesInboxId' + - name: limit + in: query + required: false + schema: + $ref: '#/components/schemas/Limit' + nullable: true + - name: page_token + in: query + required: false + schema: + $ref: '#/components/schemas/PageToken' + nullable: true + - name: ascending + in: query + required: false + schema: + $ref: '#/components/schemas/Ascending' + nullable: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ListAccountsResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorResponse' + summary: List Accounts + servers: + - url: https://api.agentmail.to + - url: https://x402.api.agentmail.to + - url: https://mpp.api.agentmail.to + - url: https://api.agentmail.eu + security: + - BearerAuth: [] + /v0/inboxes/{inbox_id}/accounts/{account_id}: + get: + description: >- + Returns one account held by the inbox. An account elsewhere is a 404. + Requires + + `inbox_read`. + operationId: inboxes_accounts_get + tags: + - InboxesAccounts + parameters: + - name: inbox_id + in: path + required: true + schema: + $ref: '#/components/schemas/inboxesInboxId' + - name: account_id + in: path + required: true + schema: + $ref: '#/components/schemas/AccountId' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + summary: Get Account + servers: + - url: https://api.agentmail.to + - url: https://x402.api.agentmail.to + - url: https://mpp.api.agentmail.to + - url: https://api.agentmail.eu + security: + - BearerAuth: [] + /v0/inboxes/{inbox_id}/accounts/{account_id}/update: + post: + description: >- + Updates one account held by the inbox. Set `status` to `disabled` to + stop the + + inbox from signing in at the provider again, or to `enabled` to + + re-enable it. Idempotent: disabling an already disabled account keeps + + its original `disabled_at`, and enabling an enabled account is a no-op. + + An account elsewhere is a 404. Requires `account_update`. + operationId: inboxes_accounts_update + tags: + - InboxesAccounts + parameters: + - name: inbox_id + in: path + required: true + schema: + $ref: '#/components/schemas/inboxesInboxId' + - name: account_id + in: path + required: true + schema: + $ref: '#/components/schemas/AccountId' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorResponse' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + summary: Update Account + servers: + - url: https://api.agentmail.to + - url: https://x402.api.agentmail.to + - url: https://mpp.api.agentmail.to + - url: https://api.agentmail.eu + security: + - BearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateAccountRequest' /v0/inboxes/{inbox_id}/api-keys: get: description: |- @@ -4710,6 +4933,167 @@ paths: - url: https://api.agentmail.eu security: - BearerAuth: [] + /v0/pods/{pod_id}/accounts: + get: + description: >- + Lists accounts held by inboxes in the pod, across all providers. + Requires `inbox_read`. + operationId: pods_accounts_list + tags: + - PodsAccounts + parameters: + - name: pod_id + in: path + required: true + schema: + $ref: '#/components/schemas/podsPodId' + - name: limit + in: query + required: false + schema: + $ref: '#/components/schemas/Limit' + nullable: true + - name: page_token + in: query + required: false + schema: + $ref: '#/components/schemas/PageToken' + nullable: true + - name: ascending + in: query + required: false + schema: + $ref: '#/components/schemas/Ascending' + nullable: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ListAccountsResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorResponse' + summary: List Accounts + servers: + - url: https://api.agentmail.to + - url: https://x402.api.agentmail.to + - url: https://mpp.api.agentmail.to + - url: https://api.agentmail.eu + security: + - BearerAuth: [] + /v0/pods/{pod_id}/accounts/{account_id}: + get: + description: >- + Returns one account held by inboxes in the pod. An account elsewhere is + a 404. Requires + + `inbox_read`. + operationId: pods_accounts_get + tags: + - PodsAccounts + parameters: + - name: pod_id + in: path + required: true + schema: + $ref: '#/components/schemas/podsPodId' + - name: account_id + in: path + required: true + schema: + $ref: '#/components/schemas/AccountId' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + summary: Get Account + servers: + - url: https://api.agentmail.to + - url: https://x402.api.agentmail.to + - url: https://mpp.api.agentmail.to + - url: https://api.agentmail.eu + security: + - BearerAuth: [] + /v0/pods/{pod_id}/accounts/{account_id}/update: + post: + description: >- + Updates one account held by inboxes in the pod. Set `status` to + `disabled` to stop the + + inbox from signing in at the provider again, or to `enabled` to + + re-enable it. Idempotent: disabling an already disabled account keeps + + its original `disabled_at`, and enabling an enabled account is a no-op. + + An account elsewhere is a 404. Requires `account_update`. + operationId: pods_accounts_update + tags: + - PodsAccounts + parameters: + - name: pod_id + in: path + required: true + schema: + $ref: '#/components/schemas/podsPodId' + - name: account_id + in: path + required: true + schema: + $ref: '#/components/schemas/AccountId' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationErrorResponse' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + summary: Update Account + servers: + - url: https://api.agentmail.to + - url: https://x402.api.agentmail.to + - url: https://mpp.api.agentmail.to + - url: https://api.agentmail.eu + security: + - BearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateAccountRequest' /v0/pods/{pod_id}/api-keys: get: description: |- @@ -8023,6 +8407,24 @@ components: type: string format: uuid description: ID of provider. + AccountStatus: + title: AccountStatus + type: string + enum: + - disabled + description: |- + `disabled` refuses every new sign-in for the account until it is + re-enabled. Access tokens already issued are not revoked. + UpdateAccountStatus: + title: UpdateAccountStatus + type: string + enum: + - enabled + - disabled + description: |- + Status to set. `disabled` stops the inbox from signing in at the + provider; `enabled` re-enables it. An enabled account reads back with no + `status`. Account: title: Account type: object @@ -8053,6 +8455,19 @@ components: sign_in_count: type: integer description: Number of sign-ins at provider. + status: + $ref: '#/components/schemas/AccountStatus' + nullable: true + description: >- + Present only while the account is disabled. Absent means the inbox + may sign in. + disabled_at: + type: string + format: date-time + nullable: true + description: >- + Time at which the account was disabled. Present only while `status` + is `disabled`. required: - account_id - provider_id @@ -8081,6 +8496,17 @@ components: - count - limit - accounts + UpdateAccountRequest: + title: UpdateAccountRequest + type: object + description: |- + Fields to change on the account. Fields you omit are left unchanged and + unknown fields are rejected. Expects at least one field; today that is + `status`. + properties: + status: + $ref: '#/components/schemas/UpdateAccountStatus' + nullable: true AgentSignupRequest: title: AgentSignupRequest type: object @@ -8526,6 +8952,14 @@ components: sign-in. One permission for both values. + account_update: + type: boolean + nullable: true + description: >- + Update accounts: disable or re-enable an inbox's sign-in at a + provider. Reading accounts + + needs only `inbox_read`. pod_read: type: boolean nullable: true From 297cafd68f6ce1d541bccbbd5749bc97f56fb63b Mon Sep 17 00:00:00 2001 From: Sidharth Reddy <70673367+sidharth0612@users.noreply.github.com> Date: Sun, 20 Sep 2026 18:43:31 -0700 Subject: [PATCH 2/3] docs(accounts): Update Account is PATCH, and only on /accounts Follows agentmail-to/agentmail-api#1353: the update is PATCH /v0/accounts/{account_id}/update, and the pod and inbox forms of the update are gone. The pod and inbox List and Get forms stay. Spec and CLI overrides rebuilt: five new operations instead of seven. Co-Authored-By: Claude Fable 5.1 --- fern/apis/api/definition/accounts.yml | 2 +- fern/apis/api/definition/inboxes/accounts.yml | 19 --- fern/apis/api/definition/pods/accounts.yml | 19 --- fern/apis/cli/openapi-overrides.yml | 14 +- fern/changelog/2026-09-16.mdx | 6 +- openapi/openapi.yml | 134 +----------------- 6 files changed, 5 insertions(+), 189 deletions(-) diff --git a/fern/apis/api/definition/accounts.yml b/fern/apis/api/definition/accounts.yml index 70fa5349..902e49da 100644 --- a/fern/apis/api/definition/accounts.yml +++ b/fern/apis/api/definition/accounts.yml @@ -110,7 +110,7 @@ service: - global.NotFoundError update: - method: POST + method: PATCH path: /{account_id}/update display-name: Update Account docs: | diff --git a/fern/apis/api/definition/inboxes/accounts.yml b/fern/apis/api/definition/inboxes/accounts.yml index 9808a6c2..b97f7649 100644 --- a/fern/apis/api/definition/inboxes/accounts.yml +++ b/fern/apis/api/definition/inboxes/accounts.yml @@ -41,22 +41,3 @@ service: response: accounts.Account errors: - global.NotFoundError - - update: - method: POST - path: /{account_id}/update - display-name: Update Account - docs: | - Updates one account held by the inbox. Set `status` to `disabled` to stop the - inbox from signing in at the provider again, or to `enabled` to - re-enable it. Idempotent: disabling an already disabled account keeps - its original `disabled_at`, and enabling an enabled account is a no-op. - An account elsewhere is a 404. Requires `account_update`. - path-parameters: - account_id: accounts.AccountId - request: accounts.UpdateAccountRequest - response: accounts.Account - errors: - - global.ValidationError - - global.NotFoundError - - global.ConflictError diff --git a/fern/apis/api/definition/pods/accounts.yml b/fern/apis/api/definition/pods/accounts.yml index 0a5dfca2..c05a8669 100644 --- a/fern/apis/api/definition/pods/accounts.yml +++ b/fern/apis/api/definition/pods/accounts.yml @@ -41,22 +41,3 @@ service: response: accounts.Account errors: - global.NotFoundError - - update: - method: POST - path: /{account_id}/update - display-name: Update Account - docs: | - Updates one account held by inboxes in the pod. Set `status` to `disabled` to stop the - inbox from signing in at the provider again, or to `enabled` to - re-enable it. Idempotent: disabling an already disabled account keeps - its original `disabled_at`, and enabling an enabled account is a no-op. - An account elsewhere is a 404. Requires `account_update`. - path-parameters: - account_id: accounts.AccountId - request: accounts.UpdateAccountRequest - response: accounts.Account - errors: - - global.ValidationError - - global.NotFoundError - - global.ConflictError diff --git a/fern/apis/cli/openapi-overrides.yml b/fern/apis/cli/openapi-overrides.yml index 38c1c201..842efc88 100644 --- a/fern/apis/cli/openapi-overrides.yml +++ b/fern/apis/cli/openapi-overrides.yml @@ -907,7 +907,7 @@ paths: - inboxes x-fern-sdk-method-name: search /v0/accounts/{account_id}/update: - post: + patch: x-fern-sdk-group-name: - accounts x-fern-sdk-method-name: update @@ -923,12 +923,6 @@ paths: - inboxes - accounts x-fern-sdk-method-name: get - /v0/inboxes/{inbox_id}/accounts/{account_id}/update: - post: - x-fern-sdk-group-name: - - inboxes - - accounts - x-fern-sdk-method-name: update /v0/pods/{pod_id}/accounts: get: x-fern-sdk-group-name: @@ -941,9 +935,3 @@ paths: - pods - accounts x-fern-sdk-method-name: get - /v0/pods/{pod_id}/accounts/{account_id}/update: - post: - x-fern-sdk-group-name: - - pods - - accounts - x-fern-sdk-method-name: update diff --git a/fern/changelog/2026-09-16.mdx b/fern/changelog/2026-09-16.mdx index cdbc5cd8..97a64f67 100644 --- a/fern/changelog/2026-09-16.mdx +++ b/fern/changelog/2026-09-16.mdx @@ -4,17 +4,15 @@ tags: ["accounts-api", "pods-api", "inboxes-api", "new-feature"] ## Summary -Disable an inbox's sign-in at a provider, and re-enable it later, with one call. Accounts now carry a `status`, and the accounts resource is reachable under pods and inboxes so an agent can audit and control sign-ins at exactly the scope its key holds. +Disable an inbox's sign-in at a provider, and re-enable it later, with one call. Accounts now carry a `status`, and accounts can be listed and read under pods and inboxes so an agent can audit sign-ins at exactly the scope its key holds. ### What's new? **New endpoints:** -- `POST /v0/accounts/{account_id}/update` - Update one account. Send `{ "status": "disabled" }` to refuse every new sign-in for that inbox at that provider, or `{ "status": "enabled" }` to re-enable it. Requires the new `account_update` permission. +- `PATCH /v0/accounts/{account_id}/update` - Update one account. Send `{ "status": "disabled" }` to refuse every new sign-in for that inbox at that provider, or `{ "status": "enabled" }` to re-enable it. Requires the new `account_update` permission. - `GET /v0/pods/{pod_id}/accounts` and `GET /v0/pods/{pod_id}/accounts/{account_id}` - List and read the accounts held by inboxes in one pod. -- `POST /v0/pods/{pod_id}/accounts/{account_id}/update` - Update an account within one pod. - `GET /v0/inboxes/{inbox_id}/accounts` and `GET /v0/inboxes/{inbox_id}/accounts/{account_id}` - List and read the accounts held by one inbox. -- `POST /v0/inboxes/{inbox_id}/accounts/{account_id}/update` - Update an account held by one inbox. **New features:** diff --git a/openapi/openapi.yml b/openapi/openapi.yml index 699ec569..1a4e7bda 100644 --- a/openapi/openapi.yml +++ b/openapi/openapi.yml @@ -853,7 +853,7 @@ paths: security: - BearerAuth: [] /v0/accounts/{account_id}/update: - post: + patch: description: |- Updates one account. Set `status` to `disabled` to stop the inbox from signing in at the provider again, or to `enabled` to re-enable it. @@ -1850,72 +1850,6 @@ paths: - url: https://api.agentmail.eu security: - BearerAuth: [] - /v0/inboxes/{inbox_id}/accounts/{account_id}/update: - post: - description: >- - Updates one account held by the inbox. Set `status` to `disabled` to - stop the - - inbox from signing in at the provider again, or to `enabled` to - - re-enable it. Idempotent: disabling an already disabled account keeps - - its original `disabled_at`, and enabling an enabled account is a no-op. - - An account elsewhere is a 404. Requires `account_update`. - operationId: inboxes_accounts_update - tags: - - InboxesAccounts - parameters: - - name: inbox_id - in: path - required: true - schema: - $ref: '#/components/schemas/inboxesInboxId' - - name: account_id - in: path - required: true - schema: - $ref: '#/components/schemas/AccountId' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Account' - '400': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ValidationErrorResponse' - '404': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '409': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - summary: Update Account - servers: - - url: https://api.agentmail.to - - url: https://x402.api.agentmail.to - - url: https://mpp.api.agentmail.to - - url: https://api.agentmail.eu - security: - - BearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateAccountRequest' /v0/inboxes/{inbox_id}/api-keys: get: description: |- @@ -5028,72 +4962,6 @@ paths: - url: https://api.agentmail.eu security: - BearerAuth: [] - /v0/pods/{pod_id}/accounts/{account_id}/update: - post: - description: >- - Updates one account held by inboxes in the pod. Set `status` to - `disabled` to stop the - - inbox from signing in at the provider again, or to `enabled` to - - re-enable it. Idempotent: disabling an already disabled account keeps - - its original `disabled_at`, and enabling an enabled account is a no-op. - - An account elsewhere is a 404. Requires `account_update`. - operationId: pods_accounts_update - tags: - - PodsAccounts - parameters: - - name: pod_id - in: path - required: true - schema: - $ref: '#/components/schemas/podsPodId' - - name: account_id - in: path - required: true - schema: - $ref: '#/components/schemas/AccountId' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/Account' - '400': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ValidationErrorResponse' - '404': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '409': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - summary: Update Account - servers: - - url: https://api.agentmail.to - - url: https://x402.api.agentmail.to - - url: https://mpp.api.agentmail.to - - url: https://api.agentmail.eu - security: - - BearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateAccountRequest' /v0/pods/{pod_id}/api-keys: get: description: |- From 9f1867016ca558984d443b73bc68a57b8c9bc493 Mon Sep 17 00:00:00 2001 From: Sidharth Reddy <70673367+sidharth0612@users.noreply.github.com> Date: Mon, 21 Sep 2026 15:38:05 -0700 Subject: [PATCH 3/3] docs(accounts): say what disabling an account does, on Update Account Update Account's reference now covers the whole disable flow: where the account_id comes from, that an account exists only after a first sign-in, what the provider sees once it is disabled (access_denied, then invalid_grant for an earlier code), what is not revoked, that sign-in keys cannot hold account_update, and what a 409 means. Same text in the Fern definition and the OpenAPI spec. Checked against agentmail-api main at 11d0d0684. The changelog entry moves to 2026-09-21, the day the route went live in prod. Co-Authored-By: Claude Fable 5.1 --- fern/apis/api/definition/accounts.yml | 16 ++++++++++++++-- .../changelog/{2026-09-16.mdx => 2026-09-21.mdx} | 0 openapi/openapi.yml | 16 ++++++++++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) rename fern/changelog/{2026-09-16.mdx => 2026-09-21.mdx} (100%) diff --git a/fern/apis/api/definition/accounts.yml b/fern/apis/api/definition/accounts.yml index 902e49da..d845739d 100644 --- a/fern/apis/api/definition/accounts.yml +++ b/fern/apis/api/definition/accounts.yml @@ -117,8 +117,20 @@ service: Updates one account. Set `status` to `disabled` to stop the inbox from signing in at the provider again, or to `enabled` to re-enable it. Idempotent: disabling an already disabled account keeps its original - `disabled_at`, and enabling an enabled account is a no-op. An account - outside the key's scope is a 404. Requires `account_update`. + `disabled_at`, and enabling an enabled account is a no-op. + + Find the `account_id` with List Accounts. An account exists only after an + inbox's first sign-in at a provider, so it cannot be disabled in advance. + A disable applies to that inbox at that provider whichever sign-in key is + used: the provider's next authorization ends in `access_denied`, and a code + issued earlier is refused with `invalid_grant`. Access tokens already + issued stay valid until they expire, and the provider's own session is + unaffected. + + Requires `account_update`, which sign-in keys (`type: public_key`) cannot + hold, so call this with a bearer API key. An account outside the key's + scope is a 404. A 409 means the account changed during the write; read it + again and retry. path-parameters: account_id: AccountId request: UpdateAccountRequest diff --git a/fern/changelog/2026-09-16.mdx b/fern/changelog/2026-09-21.mdx similarity index 100% rename from fern/changelog/2026-09-16.mdx rename to fern/changelog/2026-09-21.mdx diff --git a/openapi/openapi.yml b/openapi/openapi.yml index 1a4e7bda..f74d34df 100644 --- a/openapi/openapi.yml +++ b/openapi/openapi.yml @@ -858,8 +858,20 @@ paths: Updates one account. Set `status` to `disabled` to stop the inbox from signing in at the provider again, or to `enabled` to re-enable it. Idempotent: disabling an already disabled account keeps its original - `disabled_at`, and enabling an enabled account is a no-op. An account - outside the key's scope is a 404. Requires `account_update`. + `disabled_at`, and enabling an enabled account is a no-op. + + Find the `account_id` with List Accounts. An account exists only after an + inbox's first sign-in at a provider, so it cannot be disabled in advance. + A disable applies to that inbox at that provider whichever sign-in key is + used: the provider's next authorization ends in `access_denied`, and a code + issued earlier is refused with `invalid_grant`. Access tokens already + issued stay valid until they expire, and the provider's own session is + unaffected. + + Requires `account_update`, which sign-in keys (`type: public_key`) cannot + hold, so call this with a bearer API key. An account outside the key's + scope is a 404. A 409 means the account changed during the write; read it + again and retry. operationId: accounts_update tags: - Accounts