Accounts
Compose, inspect, label, disconnect, and purge service accounts.
An Account is a provider-neutral, user-owned composition of service bindings. It is the ownership, grant-selector, routing, event-sequence, and erasure boundary; provider health and capabilities belong to its bindings. The current public Section-A surface supports mail bindings. Contacts and calendar roles are reserved but not advertised or grantable.
The account and connection methods are available through Gateway's JSON-RPC boundary.
Account resources
| Field | Type | Notes |
|---|---|---|
id | string | Stable Account selector (account_…) |
lifecycle | AccountLifecycle | Whole-account lifecycle only |
service_bindings | ServiceBinding[] | Role-qualified provider, adapter, lifecycle, generation, freshness, capability, and issue state |
presentation | AccountPresentation | Gateway-owned nullable personal label plus positive revision and update time |
mine | boolean | Whether the authenticated user owns the Account |
provider_agent_optin | ProviderAgentOptin | Whether AI agents may act on the mailbox's provider; absent without a mail read binding. See AI agent access. |
Every successful binding result carries a positive binding_generation.
Provider, cursor, freshness, and aggregate health fields do not exist on the
Account.
A binding whose credential the provider refused carries one issue with the
RECONNECT_ACCOUNT action under the refusal's code (AUTH_EXPIRED,
CREDENTIALS_REJECTED, AUTHENTICATION_UNSUPPORTED or
CREDENTIAL_UNREADABLE). Each binding reports its own role: a password the
IMAP server accepts and the SMTP server refuses marks only the sending
binding. next_retry_at is when Traummail can next check the credential by
itself; it is absent while a check runs, when only a reconnect can repair
it, and once the check is due: it then runs with the binding's next work
(the next synchronization, or the next message you send). Until then Traummail tries the credential at most once at a time, never
once per queued message, and at most six times for a password (eight for
OAuth) before it waits for you; starting synchronizations adds no attempts,
each is answered with the refusal at once. (One exception: if an account's
receiving and sending bindings use different passwords, a check for a
message whose delivery is uncertain can add one attempt per such message.)
A binding with no stored
credential carries credentials_not_found, also with RECONNECT_ACCOUNT.
These five codes are the only ones paired with RECONNECT_ACCOUNT in a
binding issue or an operation failure; act on the action, whatever the
code. Terminal synchronizations, and their operations, are retained for
seven days.
Accounts/list and Accounts/get
Both methods require accounts.read and return only Accounts selected by the
caller’s grant. The Gateway joins the owner presentation after authorization;
admin and app-owner roster views do not expose it.
POST /rpc
Content-Type: application/json
Authorization: Bearer <access_token>
{
"jsonrpc": "2.0",
"id": 1,
"method": "Accounts/list",
"params": {}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"accounts": [{
"id": "account_01H9X…",
"lifecycle": "ACCOUNT_LIFECYCLE_ACTIVE",
"mine": true,
"presentation": {
"personal_label": "Family mail",
"revision": "3",
"updated_at": "2026-08-21T10:15:00Z"
},
"service_bindings": [{
"id": "binding_01H9Y…",
"account_id": "account_01H9X…",
"role": "SERVICE_ROLE_MAIL_READ",
"provider": "gmail",
"adapter": "gmail",
"lifecycle": "SERVICE_BINDING_LIFECYCLE_ACTIVE",
"generation": "1",
"working_capabilities": ["CAPABILITY_MAIL_READ"]
}]
}]
}
}Use Accounts/get with { "account_id": "account_…" } for one selected
Account. An unknown or foreign Account is not disclosed.
Create or attach service bindings
There is no Accounts/create compatibility method. Every flow uses the current
prepare/start contract:
AccountConnections/preparepreallocates a pending owner edge for a new Account or proves ownership of an attach target. Preparation performs no provider request and receives no credential.AccountConnections/startrepeats the prepared target and supplies explicit authorization-input groups plus role-qualified service selections.- OAuth flows continue through the provider callback. Classical flows call
AccountConnections/complete_classicalonce with the selected IMAP origin and, whenMAIL_SENDwas selected, explicit SMTP coordinates. Gateway never derives an SMTP hostname from the IMAP hostname. AccountConnections/getreturns independent per-role results. A binding ID is always paired with a positive generation.
Creating a new Account requires a global accounts.create grant. Attaching,
reconnecting, disconnecting, or purging bindings requires the corresponding
bindings.update or bindings.delete grant. Reuse of an authority is explicit
and same-account only; matching addresses or usernames never authorize reuse.
{
"jsonrpc": "2.0",
"id": 1,
"method": "AccountConnections/prepare",
"params": {
"idempotency_key": "7d770bf5-012d-4698-a608-02cdef931df8",
"create_account": { "personal_label": "Family mail" }
}
}{
"jsonrpc": "2.0",
"id": 2,
"method": "AccountConnections/start",
"params": {
"connection_id": "connection_01H9Z…",
"idempotency_key": "7d770bf5-012d-4698-a608-02cdef931df8",
"target": { "create_account_id": "account_01H9X…" },
"authorization_inputs": [{
"id": "mail-auth",
"new_authorization": { "provider_or_realm": "classical" }
}],
"services": [
{
"authorization_input_id": "mail-auth",
"role": "SERVICE_ROLE_MAIL_READ",
"adapter": "imap",
"requested_capabilities": ["CAPABILITY_MAIL_READ", "CAPABILITY_MAIL_MODIFY"]
},
{
"authorization_input_id": "mail-auth",
"role": "SERVICE_ROLE_MAIL_SEND",
"adapter": "smtp",
"requested_capabilities": ["CAPABILITY_MAIL_SEND"]
}
]
}
}{
"jsonrpc": "2.0",
"id": 3,
"method": "AccountConnections/complete_classical",
"params": {
"connection_id": "connection_01H9Z…",
"authorization_input_id": "mail-auth",
"endpoint": {
"host": "imap.fastmail.com",
"port": 993,
"username": "me@example.com",
"password": "<app-password>",
"security": "TRANSPORT_SECURITY_TLS"
},
"smtp_endpoint": {
"host": "smtp.fastmail.com",
"port": 465,
"security": "TRANSPORT_SECURITY_TLS"
}
}
}The secret transits the completion request once, is forwarded to Sync’s sealed
staging/vault path, and is never persisted by Gateway. Supplying SMTP
coordinates without a MAIL_SEND selection, or selecting MAIL_SEND without
explicit SMTP coordinates, is rejected.
A proved external service resource remains reserved through disconnect and is
released only after purge closure. Cross-account conflicts return
SERVICE_RESOURCE_CONFLICT without another owner’s
Account ID or metadata.
Personal presentation
Accounts/get_presentation requires selector-qualified accounts.read.
Accounts/update requires both selector-qualified accounts.read and
accounts.update; it is a Gateway-local compare-and-set and makes no Sync or
provider call.
{
"jsonrpc": "2.0",
"id": 4,
"method": "Accounts/update",
"params": {
"account_id": "account_01H9X…",
"expected_presentation_revision": "3",
"set_personal_label": "Family mail"
}
}Use clear_personal_label: {} to clear it. Repeating the already-current
desired value is idempotent. A stale revision returns the distinct concurrent
write conflict. Labels are presentation only: they never affect routing,
authorization, provider identity, ordering, or search.
Disconnect and purge
Accounts/disconnect stops all bindings while preserving retained history and
external-resource reservations. Accounts/purge is the erasure operation: it
fences the Account, scrubs the personal label at purge admission, destroys
Supermail-owned service state, and releases reservations only after closure.
These are separate operations; there is no predecessor purge flag.
Repair of a disconnected binding uses a new current service-connection flow. It proves the retained external identity and cannot fall forward to another provider resource.
AI agent access
Some providers require every request an AI agent makes to use a separate OAuth
client, which the mailbox owner authorizes once per mailbox. Gmail is one: an
agent request (see Declaring the actor)
reaches Gmail only over the owner's agent authorization, never over the
mailbox's own connection. Account.provider_agent_optin says where an account
stands:
| Value | Meaning |
|---|---|
not_required | The provider needs no separate authorization; agent requests use the mailbox's connection. |
active | The owner authorized agent access and it is current. |
authorization_required | Not authorized yet. Agent requests that reach the provider fail. |
reauthorization_required | The provider revoked or refused the authorization, or it no longer covers the mailbox's permissions. |
unavailable | This deployment offers no agent client for the provider (or the provider refuses it). |
An agent request to a mailbox that is not active (or not_required) fails
before any provider contact, with SyncError.CredentialsNotFound
(unavailable: SyncError.NotSupported); it never falls back to the
mailbox's own connection. Reads that reach the provider (on-demand message
bodies, mailbox lists) are covered like changes; background synchronization is
the owner's own work and is unaffected.
Accounts/start_provider_agent_optin
Requires accounts.read and accounts.update and a user actor: an agent
never changes its own authority.
{ "account_id": "account_01H9X…" }{
"authorize_url": "https://accounts.google.com/o/oauth2/v2/auth?…",
"expires_at": "2026-10-14T18:10:00Z"
}Send the owner's browser to authorize_url. The consent asks for the same
permissions as the mailbox's connection and must be given by the same provider
account. The provider returns to the Gateway, which returns the browser to the
Gateway account page (or, for the paired Inbox, to the Inbox) with
provider_agent_optin_result: active, consent_denied, expired,
rejected (another provider account, no offline access or too few
permissions) or failed. The consent is valid for ten minutes and resolves
once.
Accounts/remove_provider_agent_optin
Same authorization as start. Forgets the agent authorization and returns the
account's new provider_agent_optin. The provider's revoke endpoint is
deliberately not called: one revocation can end every grant under the
deployment's shared consent, including the mailbox's own connection. Owners can
also revoke the agent client in their provider account settings.