Mail App Passwords via API and MCP

Create, replace, and revoke mail app passwords from code or an AI agent, switch one mailbox or many, and set the default for new mailboxes.

Article details

Type, difficulty, plans, and last updated info.

▼
Type
Reference
Difficulty
Intermediate
Plans
Starter · Pro · Agency
Last updated
Oct 3, 2026

The REST API and MCP can list, create, replace, and revoke a regular mailbox's app passwords, change its mail app sign-in mode, and set the account default for future mailboxes. This page is the reference for integrations. For dashboard and webmail instructions, see Mail App Passwords for Your Devices.

An app password opens IMAP, SMTP on ports 465 and 587, ManageSieve, and CalDAV/CardDAV. It never opens the new webmail or dashboard. Classic webmail signs in over IMAP and accepts app passwords. Mailbox 2FA protects new-webmail sign-in only; mail apps and classic webmail never ask for its code.

The feature has no separate mailbox-plan gate. Existing API and MCP plan permissions still apply; see API Scopes and Permissions.

Authentication, scopes, and member permissions

Use a Bearer token with REST requests under /api/v1. JSON writes use Content-Type: application/json and an Idempotency-Key header.

Operation Required internal scope Additional member rule
List app passwords; read mailbox resources mailboxes:read Normal account, domain, and mailbox access applies.
Create, replace, revoke, or change one or many mailbox modes mailboxes:write The member's role must include mailboxes:password:set.
Read account details account:read Normal account access applies.
Change the new-mailbox default mailboxes:write Account owner only; every member is refused, regardless of role.

The password-setting permission is checked on the member's role, in addition to the token's API scope. This applies to member tokens and connectors authorized by members. An owner's token does not need an extra password-setting scope. A missing member permission returns 403 scope_blocked_by_membership.

Hosted OAuth connectors can use the matching REST capability scopes. With the legacy bundles, mail:read supplies mailboxes:read and account:read; mail:write supplies mailboxes:write as well. Scope expansion never overrides the member-permission or owner-only rules.

Token domain_ids and mailbox_ids constraints apply, including bulk selections. App-password endpoints and both mode endpoints return 404 not_found while the feature is disabled, after authentication and middleware checks. An inaccessible or missing mailbox also returns 404, so do not interpret every 404 as a feature-status signal.

Endpoints at a glance

Paths below include the /api/v1 prefix. {mailbox} is the regular mailbox ID; {id} is an app-password row ID belonging to it.

Method Path Success
GET /api/v1/mailboxes/{mailbox}/app-passwords 200, list without secrets
POST /api/v1/mailboxes/{mailbox}/app-passwords 201, new row and one-time secret
POST /api/v1/mailboxes/{mailbox}/app-passwords/{id}:rotate 200, replacement row and one-time secret
DELETE /api/v1/mailboxes/{mailbox}/app-passwords/{id} 200, revoked row
POST /api/v1/mailboxes/{mailbox}:client-auth-mode 200, mailbox mode
POST /api/v1/mailboxes:client-auth-mode 200, bulk counts
GET /api/v1/account 200, account details and default when available
PATCH /api/v1/account 200, new-mailbox default
POST /api/v1/mailboxes/{mailbox}/password 200 or 202, password reset and revocation count

All writes in this table require Idempotency-Key. The password endpoint is an existing administrative reset operation, separate from app-password rotation.

List app passwords and understand row fields

GET /api/v1/mailboxes/42/app-passwords
Authorization: Bearer tm_live_your_token

The response has top-level mailbox_id, client_auth_mode, limit, active_count, and data, an array of rows. limit is 25 active passwords per mailbox. Active rows appear first, newest first; revoked rows remain visible for 90 days. No list response contains a secret.

Each row contains:

Field Meaning
id, mailbox_id Integer app-password and mailbox IDs.
name Recognizable label, up to 64 characters.
created_at ISO-8601 creation time.
created_via dashboard, webmail, api, mcp, or admin.
created_by_user_id Account user ID, or null when no account user created it, such as mailbox self-service.
last_used_at ISO-8601 last successful use, or null before first use. Updates can lag by about five minutes.
last_used_ip Last-use IP address, or null.
last_used_protocol imap, smtp, sieve, or dav, or null before use.
revoked_at ISO-8601 revocation time, or null while active.
revoked_reason Machine-readable reason, or null while active.
active Boolean indicating whether the password is still active.

Public revocation reasons are revoked, rotated, mailbox_password_reset, mailbox_password_changed, login_suspended, converted_to_shared, and mailbox_trashed. The list excludes credentials minted internally by the platform.

Create an app password

POST /api/v1/mailboxes/42/app-passwords
Authorization: Bearer tm_live_your_token
Content-Type: application/json
Idempotency-Key: app-password-42-office-pc-001

{"name":"Outlook on the office PC"}

name is required: 1 to 64 printable characters. Runs of whitespace are collapsed to one space. The mailbox must be an active regular mailbox whose sign-in is not suspended, with fewer than 25 active app passwords.

The 201 response contains the complete row under data, adds data.password, and includes message. For example, these are the credential fields within that response:

{
  "data": {
    "id": 81,
    "mailbox_id": 42,
    "name": "Outlook on the office PC",
    "password": "abcdefghijklmnop"
  },
  "message": "Shown once. Use it as the password in the mail app; it does not open webmail."
}

This example omits the other row fields described above. The example secret is illustrative. A real secret is 16 generated lowercase letters, returned without spaces. Apps also accept spaces and capital letters; display it in four groups of four if presenting it to a user.

The password is returned only once. Keep it out of application logs. The user enters it directly in the mail app with their full mailbox address as username. Creation and replacement send a notice naming the app password to the mailbox and its recovery email, if set, without the secret. The first app password issued with a new mailbox is the exception (see below).

Use the Mail Client Setup API for the connection settings. A downloaded Apple profile contains no password; the user supplies the app password when macOS or iOS asks during installation.

Get the first app password with a new mailbox

POST /api/v1/mailboxes and POST /api/v1/mailboxes:bulk accept an optional boolean create_app_password. With true, each created mailbox also gets its first app password, returned once as app_password: the row fields described above plus password. It is named Created with the mailbox, and no notice email is sent for it, because the mailbox is new and the caller has just received its password. Without the field (default false) the response is unchanged. It is ignored while app passwords are not enabled.

POST /api/v1/mailboxes
Authorization: Bearer tm_live_your_token
Content-Type: application/json
Idempotency-Key: create-alice-001

{"domain_id":7,"local_part":"alice","password_mode":"generated_one_time","client_auth_mode":"app_password_only","create_app_password":true}

The 201 response then carries one_time_password, the mailbox password for webmail, and app_password.password for mail apps. In a bulk response, each created row has its own app_password. If it could not be issued, app_password is null (single create also adds _app_password_warning); the mailbox is still created, and you can create one with the endpoint above. An exact retry of a single create with the same Idempotency-Key returns the same response, both secrets included, without issuing a second app password. A bulk replay omits the secrets, as it does for one_time_password.

Replace or revoke a password

Replacement requires no JSON body:

POST /api/v1/mailboxes/42/app-passwords/81:rotate
Authorization: Bearer tm_live_your_token
Idempotency-Key: replace-app-password-81-001

The 200 response contains the new complete row under data, its one-time data.password, top-level replaced_id naming the old row, and message. The replacement has a new data.id and the same name. The old row is revoked with revoked_reason: "rotated", its secret stops immediately, and apps using it are signed out. Update the device with the replacement.

To revoke without issuing a replacement:

DELETE /api/v1/mailboxes/42/app-passwords/82
Authorization: Bearer tm_live_your_token
Idempotency-Key: revoke-app-password-82-001

No body is required. The 200 response has status: "revoked" and the complete revoked row under data. The app loses access; other devices with valid app passwords reconnect on their own. Revocation cannot be undone. Trying to replace or revoke an already revoked row with a new request returns 409 conflict.

Change one mailbox's mail app sign-in mode

POST /api/v1/mailboxes/42:client-auth-mode
Authorization: Bearer tm_live_your_token
Content-Type: application/json
Idempotency-Key: require-app-passwords-42-001

{"mode":"app_password_only"}

mode is required and accepts:

  • app_password_only: mail apps require an app password. Connections using the mailbox password are signed out; apps using a valid app password reconnect on their own.
  • password_or_app_password: mail apps accept the mailbox password or an app password.

The 200 response has mailbox_id, client_auth_mode, and message. Setting the current mode again returns 200 and changes nothing. Changing a mode does not revoke existing app passwords.

Create passwords for the devices before requiring them. A refused mailbox-password sign-in may show: Sign-in failed. This mailbox accepts app passwords only: create one in webmail under Settings > App passwords. Some apps show only a generic password error.

Shared mailboxes have no direct sign-in and return 422 mailbox_not_eligible on this endpoint. Platform system mailboxes cannot be switched to app_password_only; that returns 422 system_mailbox_protected.

Neither mode changes new-webmail sign-in, All inboxes, Message API tokens, migrations into the mailbox, mail rules, or forwarding. Shared-mailbox members use their own regular mailbox's credentials and mode.

Change modes in bulk

POST /api/v1/mailboxes:client-auth-mode
Authorization: Bearer tm_live_your_token
Content-Type: application/json
Idempotency-Key: require-app-passwords-domain-7-001

{"domain_id":7,"mode":"app_password_only"}

Supply mode and exactly one selector:

Selector Selection
"mailbox_ids": [42, 43] Explicit nonempty array, at most 1000 IDs. Duplicates count once.
"domain_id": 7 Mailboxes on a domain belonging to this account.
"all": true All mailboxes accessible to the token. false does not count as a selector.

Account and token constraints narrow every selection. An explicit ID outside access, or an unknown ID, returns 404 rather than applying a partial selection. An unknown or foreign-account domain returns 422 validation_error. Zero or multiple selectors return 422 invalid_selection.

At most 1000 mailboxes may match. A larger selection returns 422 selection_too_large before any change. Narrow the domain or send explicit batches.

{
  "data": {
    "client_auth_mode": "app_password_only",
    "matched": 24,
    "updated": 21,
    "skipped": 3
  }
}

matched counts selected mailboxes; updated counts actual mode changes; skipped counts shared, trashed, or deleting mailboxes, plus platform system mailboxes when requiring app passwords. Paused and sign-in-suspended mailboxes can have their mode updated for when access returns. Already matching mailboxes count as matched but not updated or skipped, so the operation is safe to repeat. Suspended accounts are refused with 403.

Read mailbox state and set the account default

While app passwords are enabled, GET /api/v1/mailboxes and GET /api/v1/mailboxes/{mailbox} include these fields on mailbox resources:

  • client_auth_mode: app_password_only or password_or_app_password.
  • app_passwords_count: integer count of active visible app passwords, excluding platform-internal credentials.

Both fields are omitted while the feature is disabled. Shared mailboxes have no usable direct sign-in mode or app passwords; request credentials for a regular member mailbox instead.

GET /api/v1/account requires account:read. Its normal top-level fields remain available: id, name, email, plan, effective_plan_slug, subscription_status, limits, features, usage, safety_limits, and created_at. It adds new_mailbox_client_auth_mode only when app passwords are enabled and the platform applies the account default to new mailboxes. Otherwise GET remains available and omits that field.

Only the account owner can change the default:

PATCH /api/v1/account
Authorization: Bearer tm_live_owner_token
Content-Type: application/json
Idempotency-Key: new-mailbox-default-001

{"new_mailbox_client_auth_mode":"app_password_only"}

The required field accepts the same two modes. This is the only writable account field here. The response has top-level id, new_mailbox_client_auth_mode, and message. PATCH returns 404 unless both conditions for exposing the field are met, and 403 scope_blocked_by_membership for any member token or member-authorized connector.

An owner token limited by domain_ids or mailbox_ids returns 403 token_resource_constrained. Use an unrestricted owner token, or change the default in Account settings.

The default affects future dashboard, bulk, invite, API, and agent-created mailboxes. It never changes existing ones. A single API mailbox creation can explicitly supply client_auth_mode in POST /api/v1/mailboxes; omitting it follows the account default. Existing mailboxes keep password_or_app_password at rollout. The new-mailbox default is app_password_only unless the account owner changes it.

A mailbox password reset automatically revokes app passwords

POST /api/v1/mailboxes/{mailbox}/password requires mailboxes:write, the same member password-setting permission, and Idempotency-Key. Its body requires password, the new mailbox password, following the mailbox password policy. It is not an app-password creation endpoint.

Every successful administrative reset through this endpoint, including an MCP agent's password change, revokes all app passwords with reason mailbox_password_reset. There is no opt-out. While the feature is enabled, the response includes app_passwords_revoked, an integer count, alongside status, sync_pending, and message:

  • 200, status: "updated", sync_pending: false when mail-server synchronization completed.
  • 202, status: "update_pending", sync_pending: true when the password was saved and synchronization is pending. App passwords are already revoked at this point.

The reset also revokes existing mailbox message tokens. This is a consequence of resetting the mailbox password, not of rotating an individual app password or changing the mail app mode.

Self-service password changes in webmail revoke app passwords only when the user selects Also revoke all app passwords. Password recovery, sign-in suspension, conversion to a shared mailbox, and moving to Recently deleted revoke all of them. Restoring access or the mailbox does not restore revoked secrets. See Mailbox Sign-In Suspension via API.

Idempotency and one-time secrets

Use a fresh Idempotency-Key for each deliberate write, and reuse it only for a transport retry of the same method, path, and body. Keys are required and may be at most 255 characters. Successful responses are cached for the default 24-hour window; reusing a key for a different request returns 409 idempotency_mismatch.

An app-password create or rotate replay returns the same safe identifiers but omits data.password. It includes _idempotency_replay_warning and the response header X-Idempotency-Replayed: true. A replay cannot recover a lost secret. Use the returned data.id to rotate the active row with a fresh key and obtain a usable replacement. Track the new ID after rotation.

Replaying a successful revoke with the same key returns its saved result. A fresh revoke request against that revoked row returns 409 conflict. Mode changes are naturally repeatable, but give each deliberate change a new key: reusing an earlier key after switching modes can replay an old response instead of applying your new intent.

Rate limits and errors

Creation is limited to 60 per hour per account, and replacement to 30 per hour per account. These account budgets are shared by API and MCP callers rather than being separate allowances per token. Bulk mode changes have an additional 10 requests per minute throttle. The normal API limiter also applies to these routes; its default is 60 requests per minute per credential. A rate-limited request returns 429 rate_limited; honor the Retry-After header before retrying.

Errors use the standard error object with code, message, hint, request_id, and retryable. Handle the machine-readable code rather than matching prose.

Status and code Meaning or next step
401 unauthenticated Missing, invalid, or expired authentication.
403 insufficient_scope Required token scope is missing.
403 scope_blocked_by_membership Member lacks password-setting permission, or a member tried to change the account default.
403 token_resource_constrained An owner token limited to some domains or mailboxes cannot change the account-wide default; use an unrestricted owner token or Account settings.
403 token_scope_blocked_by_plan A previously granted scope is unavailable on the account's current plan.
403 forbidden Access is refused; a suspended account is also refused by the bulk endpoint.
404 not_found Feature disabled, unavailable account-default operation, or inaccessible/missing mailbox or app-password row.
409 conflict Password already revoked, or a concurrent operation prevents completion.
409 idempotency_mismatch Key reused for a different request.
422 validation_error Missing or invalid request field or invalid domain selector.
422 invalid_name App-password name is not 1 to 64 printable characters.
422 app_password_limit_reached Mailbox already has 25 active passwords; revoke an unused one.
422 mailbox_not_eligible Create/rotate needs an active regular mailbox with sign-in available; shared mailboxes also cannot have their own mode set.
422 system_mailbox_protected A platform system mailbox must keep accepting its mailbox password.
422 invalid_selection Bulk request has zero or multiple selectors.
422 selection_too_large More than 1000 mailboxes match the bulk selector.
422 missing_idempotency_key or invalid_idempotency_key Write omitted the required key or exceeded 255 characters.
429 rate_limited A rate limit was reached; wait before retrying.
503 idempotency_unavailable Idempotency cannot identify this caller; refresh authentication before retrying.

MCP tools and destructive gating

MCP uses the same REST authorization and response fields. Direct tools are:

Tool Inputs and action
list_mailbox_app_passwords mailbox_id; returns the list, mode, limit, and active count without secrets. Read-only.
create_mailbox_app_password mailbox_id, name; issues one password, with one-time data.password.
rotate_mailbox_app_password mailbox_id, app_password_id; revokes the old row and returns a replacement and replaced_id.
revoke_mailbox_app_password mailbox_id, app_password_id; revokes the credential permanently.
set_mailbox_client_auth_mode client_auth_mode and exactly one of mailbox_id, mailbox_ids, domain_id, or all: true; sets one mailbox or a bulk selection.
get_account No inputs; reads account details and the new-mailbox default when available.
update_account new_mailbox_client_auth_mode; sets the future default, owner only.

The mailbox creation tools create_mailbox_generated_password and bulk_create_mailboxes take the same optional create_app_password input as REST.

Write tools also accept optional idempotency_key. REST uses the body field mode for mailbox mode changes; the MCP tool calls that input client_auth_mode. Its bulk selectors have the same access rules and 1000-mailbox cap as REST.

list_mailbox_app_passwords(mailbox_id=42)
create_mailbox_app_password(mailbox_id=42, name="Outlook on the office PC")
rotate_mailbox_app_password(mailbox_id=42, app_password_id=81)
revoke_mailbox_app_password(mailbox_id=42, app_password_id=82)
set_mailbox_client_auth_mode(mailbox_id=42, client_auth_mode="app_password_only")
set_mailbox_client_auth_mode(domain_id=7, client_auth_mode="app_password_only")
update_account(new_mailbox_client_auth_mode="app_password_only")

In the self-hosted server, every write above requires TREKMAIL_ALLOW_DESTRUCTIVE=true. Creating a credential is gated too, because it grants mailbox access. Listing and account reads do not require that flag. Ask the user to approve the intended credential or access change before invoking a write. Direct secret-returning tools instruct the agent to show the password once, have the user paste it into the app, and never store it in files or memory or repeat it in later messages or tool calls.

ChatGPT/OpenAI and Claude directory profiles

These profiles provide a secure dashboard setup link for creation and replacement instead of minting a secret in the chat. Mailbox creation is a dashboard link there too, so the first app password comes from the dashboard's Mailbox created card, not from create_app_password. The destination is /app/mailboxes/{mailbox_id}/security#app-passwords; the user signs in and completes the action there.

The OpenAI profile exposes get_mailbox_app_password_setup_link and get_mailbox_app_password_replacement_setup_link. The Claude profile keeps the names create_mailbox_app_password and rotate_mailbox_app_password, but returns the secure setup link rather than data.password. Do not promise a secret from those directory tools or ask the user to paste one into the conversation.

For connection coordinates, use get_mail_client_setup. For general connector setup, see Connecting AI Agents. White Label mailboxes use the same API feature and branded webmail and mail hosts; call the credential an app password in user-facing instructions.

Related articles

Jump to nearby guides that continue the workflow.

Sign in to TrekMail

Access your dashboard, mailboxes and DNS.

12 characters passwords match

Reset email sent

If an account exists for this email, we've sent password reset instructions.

By continuing, you agree to TrekMail's Terms and Privacy Policy.