List Mailboxes
GET /mailboxes
List sending mailboxes the authenticated user can access in this org.
Backs the MCP “pick a mailbox before launching a campaign” flow.
Sources from the canonical mailboxes table (NOT the
ai_inbox_email_cron_jobs table the FE picker previously used — that
table is the AI-inbox sync state, not the source of truth for which
mailboxes exist). See .claude/agent-context/mailbox-picker-sources-cron-table.md.
Tenant scope: mailboxes lives in the per-org schema (NOT public —
the earlier version of this comment was wrong; see
select to_regclass('public.mailboxes') returning null vs. the
per-org row count). Binding the session to schema=<org> IS the
tenant boundary; no further org-filter predicate is needed.
Per-row RBAC (ENFORCE_MAILBOX_PERMISSIONS soak flag) is NOT enforced
here yet — Phase 3b on issue #7180 — keeping symmetry with the FE
picker. When that flag flips on, this endpoint will adopt the scope
filter alongside the cdp/mailboxes/fetch surface.
Historical bug (fixed 2026-05-22): the previous implementation appended
(t.org_id = :org_id OR m.created_by = :user_id) to the WHERE. Both
halves silently matched zero rows in prod — mailboxes.team_id is
NULL on every row (no team pinning) and mailboxes.created_by stores
email strings, not propelauth UUIDs. Result: the endpoint returned an
empty list for every caller. Dropping the predicate restores the
correct per-org listing; tenant isolation comes from the schema bind.
Authorizations
Parameters
Query Parameters
Header Parameters
Responses
200
Successful Response
object
object
422
Validation Error