# Lumvise Account & Project API
Base: `https://app.lumvise.com` (web) — the same endpoints serve the desktop app.
## Authentication
Single sign-on via AWS Cognito (OIDC, Authorization Code + PKCE, public client —
no client secret). One app client serves both the website and the desktop app.
- **Web**: redirect to `https://eu-north-1lvplz48hz.auth.eu-north-1.amazoncognito.com/oauth2/authorize` with
`response_type=code`, `scope=openid email profile`, S256 code challenge.
Redirect target: `https://app.lumvise.com/auth/callback` (the app subdomain, per the "Allowed callback URLs" below).
- **Desktop**: same flow with a loopback redirect (`http://127.0.0.1:<port>/callback`);
exchange the code at `https://eu-north-1lvplz48hz.auth.eu-north-1.amazoncognito.com/oauth2/token` with the PKCE verifier.
- **Renewal**: use the Cognito refresh token at `/oauth2/token`
(`grant_type=refresh_token`). Fully local work needs no session at all.
Every API call sends the **ID token** as `Authorization: Bearer <id_token>`.
The server verifies it against the pool JWKS (issuer, signature, expiry,
audience) and maps `sub` to the user's profile. Clients never receive database
or graph-database credentials.
## Concepts
- **Personal space**: every user automatically has a private space; it is just
the set of projects they own plus projects shared with them.
- **Project**: a knowledge graph plus relational content. Owned by exactly one
user; shareable with others as `viewer` or `editor`.
- **Centralized project**: a project whose graph lives in FalkorDB
(`lumvise_p_<projectId>`). Local-only projects stay fully supported.
- **Publication**: an optional public page for a project (`/p/<slug>`).
## Endpoints (RPC, JSON over POST)
All endpoints are TanStack server functions; the desktop app calls them as
`POST /_serverFn/<id>` with a JSON body `{ "data": ... }` and the bearer token.
| Function | Input | Auth | Description |
|---|---|---|---|
| `getAccount` | — | user | Profile + owned projects + projects shared with the caller |
| `createProject` | `{ name, description? }` | user | Creates a project in the caller's space |
| `renameProject` | `{ projectId, name }` | owner | Renames a project |
| `deleteProject` | `{ projectId }` | owner | Deletes project, memberships, publication (graph drop: see contract) |
| `shareProject` | `{ projectId, email, role }` | owner | Shares with an existing account (`viewer`/`editor`) |
| `unshareProject` | `{ projectId, memberId }` | owner | Removes a member |
| `listProjectMembers` | `{ projectId }` | owner | Lists members |
| `publishProject` | `{ projectId, title, summary? }` | owner | Creates/updates the public page, returns `slug` |
| `unpublishProject` | `{ projectId }` | owner | Removes the public page |
| `listPublications` | — | public | Latest public projects |
| `centralizeProject` | `{ projectId }` | owner | Provisions the FalkorDB graph for a local project |
Errors: `401` missing/invalid token, `403` not permitted, `404` not found,
`400` invalid input (zod-validated).
## Graph API
Node/edge read/write endpoints are **not yet implemented** — they follow the
graph-storage contract in `docs/graph-contract.md`, which we agree on before
the desktop adapter is built.
## Graph gateway
`/api/public/v1/projects/{projectId}/graph/{op}`. Send `Authorization: Bearer <Cognito id or access token>`. You can leave the token out only to read published projects.
The gateway handles each request in this order: check the token, look up the caller's role on the project in PostgreSQL, check that role against the operation, then run it on that project's FalkorDB graph only. A private or unknown project returns `404`, so outsiders can't tell whether it exists.
| op | Method | Minimum role | Body |
|---|---|---|---|
| `query` | POST | public | `{ cypher, params? }`, read-only (`GRAPH.RO_QUERY`), 5 s timeout |
| `export` | GET | public | |
| `putNodes` | POST | editor | `{ nodes: Node[] }` (≤500); `rev` must match the stored value, otherwise `409` lists the conflicts |
| `deleteNodes` | POST | editor | `{ ids }` (≤1000) |
| `putEdges` | POST | editor | `{ edges: {id,type,from,to,created_at?}[] }` (≤2000) |
| `deleteEdges` | POST | editor | `{ ids }` |
| `import` | POST | owner | `{ mode: "replace"\|"merge", nodes, edges }` (≤10k / 50k) |
`kind` and `type` must be lowercase identifiers. Requests are limited to 8 MB and `body` to 256 KB per node. The project must be centralized first; otherwise the gateway returns `409`.
## Desktop sign-in
The desktop app signs in through the browser using authorization code + PKCE. Its callback is `http://localhost:8787/callback`. It then sends the resulting tokens to the gateway and renews them with the refresh token.