# API Reference Base URL: `http://localhost:3000/api`. Auth: `Authorization: Bearer `. ## Auth ### POST /auth/register ```json { "email": "a@example.com", "name": "Ada", "password": "secret123" } ``` -> `201 { user, token }`. Errors: `400` validation, `409` email taken. ### POST /auth/login ```json { "email": "a@example.com", "password": "secret123" } ``` -> `200 { user, token }`. Errors: `401` invalid credentials. ### GET /auth/me -> `200 { user }`. ```bash curl -X POST localhost:3000/api/auth/register \ -H 'Content-Type: application/json' \ -d '{"email":"a@example.com","name":"Ada","password":"secret123"}' ``` ## Workspaces | Method | Path | Role | Description | |--------|------|------|-------------| | GET | `/workspaces` | member | List my workspaces | | POST | `/workspaces` | any logged-in | Create workspace (caller becomes owner) | | GET | `/workspaces/:id` | viewer+ | Get workspace | | PATCH | `/workspaces/:id` | admin+ | Rename | | DELETE | `/workspaces/:id` | owner | Delete | | GET | `/workspaces/:id/members` | viewer+ | List members | | POST | `/workspaces/:id/members` | admin+ | Invite `{email, role}` (role: admin/member/viewer) | | PATCH | `/workspaces/:id/members/:userId` | admin+ | Change role | | DELETE | `/workspaces/:id/members/:userId` | admin+ | Remove member | Roles rank: `viewer < member < admin < owner`. Viewers read only. ## Pages | Method | Path | Description | |--------|------|-------------| | GET | `/pages/by-workspace/:workspaceId` | List pages (flat, client builds tree) | | POST | `/pages` | Create `{workspace_id, parent_id?, title?}` (member+) | | GET | `/pages/:id` | Get page + comments | | PATCH | `/pages/:id` | Update title/icon/cover/content/position/parent/archive (member+) | | DELETE | `/pages/:id` | Delete incl. children via FK cascade | | GET | `/pages/search/query?q=&workspace_id=` | Title search | | POST | `/pages/:id/comments` | Add comment `{body}` | | DELETE | `/pages/:pageId/comments/:commentId` | Delete (author or admin+) | Content fields: `content_json` (TipTap JSON), `content_html` (rendered HTML). ## Users - `GET /users/search?email=` — lookup for invites (auth required). - `GET /users/discover?q=` — fuzzy discovery (min 2 chars, max 10 hits, no hashes). - `PATCH /users/me` — profile/appearance (`name, email, avatar, locale, theme, bg_image, bg_blur`). - `PATCH /users/me/password` — `{ currentPassword, newPassword }`. ## Books - `GET /books/by-workspace/:workspaceId`, `POST /books {workspace_id,title?,icon?}` - `PATCH /books/:id`, `DELETE /books/:id` (pages inside cascade). ## Tasks - `GET /tasks/by-workspace/:workspaceId?status=&from=&to=` (calendar range on `due_at`) - `POST /tasks {workspace_id, book_id?, page_id?, title, status?, priority?, due_at?}` - `PATCH /tasks/:id`, `DELETE /tasks/:id` ## Time tracking - `GET /time/by-workspace/:workspaceId` → `{ entries, open }` - `POST /time/start {workspace_id, book_id?, page_id?, description?}` (409 if running) - `POST /time/stop {workspace_id}` → sets `ended_at` + `duration_seconds` - `PATCH /time/:id`, `DELETE /time/:id` (own or admin+) ## Admin (requires `is_admin`) - `GET /admin/overview` → counts + recent users - `GET /admin/users`, `POST /admin/users {name,email,password,is_admin?}` (admins create accounts; public registration is disabled once installed), `PATCH /admin/users/:id`, `DELETE /admin/users/:id` ## Invites & notifications - `POST /workspaces/:id/members {email,role}` → pending invite + notification (workspace admin+ or `workspace.invite` permission) - `GET /invites/mine`, `POST /invites/:id/accept`, `POST /invites/:id/decline` - `GET /workspaces/:id/permissions`, `PATCH /workspaces/:id/permissions` (per-user allow/deny/inherit overrides, workspace admin+) - `GET /notifications/mine?unread=1` → `{ notifications, unread }` - `PATCH /notifications/:id/read`, `POST /notifications/read-all` ## Chat / teams / groups / upload - `GET /chat/:workspaceId`, `POST /chat/:workspaceId {body,attachment?}`, `PATCH /chat/message/:id`, `DELETE /chat/message/:id`, `GET /chat/:workspaceId/search?q=` - `GET /dm`, `POST /dm {user_id}`, `GET /dm/:id/messages`, `POST /dm/:id/messages`, `PATCH/DELETE /dm/message/:mid`, `GET /dm/:id/search?q=` - `POST /reactions/toggle {target_type,target_id,emoji}` (chat + DM messages) - Socket: `chat:join`, `dm:join`, `chat:typing`, `dm:typing`, `presence:online`, `chat:message/edited/deleted`, `dm:message/edited/deleted`, `reaction:updated` - `GET /teams/by-workspace/:id`, `POST /teams`, `PATCH/DELETE /teams/:id`, team members CRUD, `POST /teams/:id/workspace {name}` (team workspace + badge) - `GET /groups/by-workspace/:id` (seeds `Team Creators`), groups CRUD + members - `POST /upload` (multipart image ≤5MB) → `{ url: /uploads/... }` ## Health - `GET /health` -> `{ok:true, db:'up'}` or `503 {ok:false, db:'down'}`. ## Real-time (Socket.IO) Connect: `io(url, { auth: { token } })`. - `page:join { pageId }` — join room; server checks workspace membership. - `page:edit { pageId, content_json, content_html }` — persist + broadcast to others. Viewers are ignored server-side. - `typing { pageId, name }` — broadcast typing indicator. - Server events: `page:updated { page, from? }`, `page:deleted { id }`, `comment:added { comment }`, `presence:update { members: [{userId,email,name}] }`, `error:message { error }`.