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
object
Whether there are more pages
Items per page
Current page number (1-indexed)
Total number of items
422
Validation Error