Connect by JBRH Open Connect

Mailboxes

A mailbox is one connected email identity plus the settings that govern it: a role, a signature, its own autonomy rule, its own health verdicts, and the transport and credentials its provider adapter is built from. A workspace can run several. Adding one adds a source of mail and a possible sending identity; it does not change how any existing mailbox behaves.

Status
Available What this means
Audience
both
Channels
email
In the app
#/mailboxes
Last verified
Product version
6.3.2

What the row owns#

FieldWhat it decidesWhere you change it
transportWhich provider adapter providers.build() constructsFixed when you connect; change it by reconnecting
configThe credentials and settings that adapter needsThe connection form, or refreshed automatically by OAuth
roleHow the mailbox is used — see Mailbox rolesThe mailbox row
signatureWhat is appended to a reply sent from hereThe mailbox row
autonomyWhat Connect may do without asking, for this address onlyThe mailbox row, or What Connect may do
Health verdictsWhat the row reports about itself — see Mailbox healthNot editable; derived from what actually happened

config is a real setter rather than a computed property, and that is not an implementation detail you can ignore. oauth.store_tokens rebuilds the whole dictionary and assigns it back after a token refresh; against a read-only property every refreshed access token would be silently dropped, and the mailbox would work until the current token expired and then stop for reasons no screen could explain.

Several in one workspace#

There is no single-inbox assumption anywhere in the channel. A workspace commonly runs a general address, a sales address and a support address at once, and each carries its own settings. Two of them can sit at different autonomy modes on the same day: sales replies going out on their own while everything arriving at the support address waits for a person.

What is shared is the workspace: one set of contacts, one thread list, one memory, one knowledge bank, one audit trail. A person who writes to two of your addresses is one relationship, not two, because identity resolution happens after canonicalisation and does not care which mailbox the message came through.

  1. Open #/mailboxes and add a mailbox. Both audiences answer the same question here — which provider — because add-a-mailbox is one flow.

    Result You are sent into that provider's connection path: Google's consent screen, the Microsoft flow, or the IMAP and SMTP form.

  2. Give the new row a role, and a signature if it needs its own.

    Result The role is what later decides which mailbox a reply is sent from, so it is worth setting deliberately rather than leaving at the default.

  3. Set its autonomy, or leave it to inherit.

    Result An unset mailbox falls through to the channel's rule, then the workspace's. Nothing is enabled by adding a mailbox that was not already enabled for the channel.

  4. Let the first sync run.

    Result History is read in, threads appear in Conversations, and the row starts reporting health. Syncing an inbox covers what the first pass does differently from every later one.

Two tables, one policy#

Customer mailboxes live in tenant_mailboxes and the Owner's in mailboxes. They are not identical rows: tenant_mailboxes has real foreign keys to workspaces and platform_users, which mailboxes does not. What is identical is the policy — mailbox_console runs over mailboxes.py for both audiences, so a rule about roles, health or autonomy is written once and cannot drift between them.

Every by-id action on a mailbox goes through mailbox_console.row_for, which resolves the row inside the caller's workspace. Four routes once did not — the connection form, sync, and the OAuth start and disconnect each took a mailbox id straight from a browser and looked it up with a bare db.get, which the workspace kernel's query filter can miss entirely when the row is already in the identity map. They answer only for their own workspace's rows now. One inbound webhook route still resolves its mailbox before any workspace is known, because it has nothing else to resolve it by; that is the documented exception rather than an oversight.

The mailbox that was invisible rather than missing#

A mailbox row stamped with an empty workspace_id matches no scope and no row-level security policy. It is not deleted and it is not broken: every by-id action on it still works, while every list of mailboxes omits it. Two connected Gmail identities can disappear from the Mailboxes screen this way, with no duplicate warning and no error anywhere.

That query is deliberately read-only. Stamping the row from a request is refused by the policy's WITH CHECK, which would turn a missing row into a 500 — the repair belongs to migrate.backfill_canonical_workspace, which runs as the schema owner on every boot. If a mailbox you know you connected is not on the screen, this is the first thing to suspect, and A row with no workspace stamp is invisible, not missing is the longer write-up.

Questions#

How many mailboxes can one workspace have?

The model does not fix a number — a mailbox is a row, and a workspace may hold several. What bounds you in practice is the daily allowance on a customer plan, which counts processed mail across the workspace rather than per address.

If I connect the same address twice, do I get two of everything?

You should not: the uniqueness check refuses a second row for an address already connected in that workspace. That check was the thing an empty workspace stamp defeated, which is why the visibility fix and the duplicate fix are the same fix.

Does deleting a mailbox delete its conversations?

No. Threads, messages and contacts are canonical workspace records and outlive the connection that produced them. Disconnecting a mailbox sets out exactly what stops and what is kept.