API reference
All endpoints live on the same Worker that serves the SPA. Authentication is session-cookie based; the cookie is set by /api/auth/callback after the Prism OAuth round-trip and verified on every request. Admin endpoints check role ∈ { owner, co-owner } on the configured team.
Auth
| Method | Path | Description |
|---|---|---|
| GET | /api/auth/config | Public OAuth config used by the SPA |
| GET | /api/auth/me | Current user, or { user: null } |
| POST | /api/auth/callback | Exchange OAuth code for a session |
| POST | /api/auth/logout | Invalidate session |
Init
| Method | Path | Description |
|---|---|---|
| GET | /api/init/status | { configured, configStaged } |
| GET | /api/init/branding | site_name, site_logo_url |
| GET | /api/init/config | Full app config — open while pre-init, owner-only after |
| PUT | /api/init/config | Patch config — open while pre-init, owner-only after |
| POST | /api/init/setup | Phase 1: persist config + run schema, await owner login |
POST /api/init/setup refuses if the system is already configured OR if a config is already staged (an owner-login is the only way to advance from a staged state).
Mailbox
| Method | Path | Description |
|---|---|---|
| GET | /api/folders | All folders + counts |
| POST | /api/folders | Create a custom folder |
| PATCH | /api/folders/:id | Rename a folder |
| DELETE | /api/folders/:id | Delete folder, move messages to inbox |
| GET | /api/messages?folder=&cursor=&q= | Paginated message list |
| GET | /api/messages/:id | Full message body + attachments |
| PATCH | /api/messages/:id | Mark read/unread/starred, move folder |
| DELETE | /api/messages/:id | Soft-delete (trash) or hard-delete |
| POST | /api/messages/bulk | Same patches but on { ids[] } (≤ 500) |
| POST | /api/messages/trash/empty | Hard-delete every trashed message |
| GET | /api/threads/:threadId | All messages in a thread |
| GET | /api/messages/:id/attachments/:attId | Stream an attachment |
| GET | /api/messages/:id/raw | Stream the original .eml |
Send & drafts
| Method | Path | Description |
|---|---|---|
| POST | /api/send | Send a message. From address must be assigned to caller. |
| POST | /api/drafts | Upsert a draft (creates a folder='drafts' row) |
Member-facing addresses
| Method | Path | Description |
|---|---|---|
| GET | /api/addresses | Addresses assigned to current user |
Members cannot add or remove their own addresses.
Admin (owner / co-owner only)
| Method | Path | Description |
|---|---|---|
| GET | /api/admin/members | All Lumen-known team members + their storage caps |
| GET | /api/admin/addresses | All address assignments |
| POST | /api/admin/addresses | Assign { userId, address, displayName? } |
| PUT | /api/admin/addresses/:id | Update display name |
| DELETE | /api/admin/addresses/:id | Remove assignment |
| PUT | /api/admin/addresses/:id/primary | Make this address the user's primary |
| PUT | /api/admin/members/:userId/limits | Set/clear per-user maxStorageBytes and maxMessageBytes (null to inherit, 0 for unlimited) |
| GET | /api/admin/stats/storage | Site-wide totals: bytes used, message count |
A user becomes "Lumen-known" the first time they complete a Prism login.
Realtime
| Method | Path | Description |
|---|---|---|
| GET | /api/ws | WebSocket. Pushes LumenEvent messages to the user. |
LumenEvent:
ts
type LumenEvent =
| { kind: "incoming"; folder: string; messageId: string }
| { kind: "changed"; folder: string; messageId: string }
| { kind: "deleted"; folder: string; messageId: string }
| { kind: "folders" };Image proxy
| Method | Path | Description |
|---|---|---|
| GET | /api/proxy/image?url=… | Server-side image fetch with SSRF guard |
Required for image rendering when the site default (or per-user override) has the proxy enabled. See Storage & limits.
Contacts & settings
| Method | Path | Description |
|---|---|---|
| GET | /api/contacts?q= | Frecency-ranked contacts |
| GET | /api/settings | Per-user settings + site defaults |
| PUT | /api/settings | Patch user settings |