# SimpleNote Web - API Endpoints **Base URL:** `/api/v1` **Auth:** All endpoints require `Authorization: Bearer ` header unless noted. --- ## Auth ### `POST /auth/token` Generate a new API token (admin only). **Request body:** ```json { "label": "my-token-label" } ``` **Response `201`:** ```json { "token": "snk_...", "label": "my-token-label", "createdAt": "2026-03-28T..." } ``` --- ### `GET /auth/verify` Verify a token is valid. **Response `200`:** ```json { "valid": true, "token": "snk_..." } ``` --- ## Projects ### `GET /projects` List all projects. **Response `200`:** ```json { "projects": [...] } ``` --- ### `POST /projects` Create a project. **Request body:** ```json { "name": "Project Name", "description": "optional" } ``` **Response `201`:** ```json { "id": "...", "name": "...", "description": "...", "createdAt": "...", "updatedAt": "..." } ``` --- ### `GET /projects/:id` Get a single project. **Response `200`:** ```json { "id": "...", "name": "...", "description": "...", "createdAt": "...", "updatedAt": "..." } ``` **Response `404`:** ```json { "error": "Project not found", "code": "NOT_FOUND" } ``` --- ### `PUT /projects/:id` Update a project. **Request body:** ```json { "name": "New Name", "description": "New desc" } ``` **Response `200`:** ```json { "id": "...", "name": "...", "description": "...", "createdAt": "...", "updatedAt": "..." } ``` --- ### `DELETE /projects/:id` Delete a project. **Response `200`:** ```json { "deleted": true, "id": "..." } ``` --- ### `GET /projects/:id/tree` Get full project tree (folder hierarchy + documents). **Response `200`:** ```json { "project": { "id": "...", "name": "..." }, "documents": [...], "folders": [...], "totalDocuments": 5 } ``` --- ### `GET /projects/:id/documents` List documents directly in a project (not in sub-folders). **Response `200`:** ```json { "documents": [...], "total": 3 } ``` --- ## Folders ### `GET /folders?projectId=X&parentId=Y` List folders in a project. `projectId` is required. **Query params:** - `projectId` (required): Project ID - `parentId` (optional): Parent folder ID. Omit for root folders. **Response `200`:** ```json { "folders": [...] } ``` **Response `400`:** ```json { "error": "projectId query parameter is required", "code": "VALIDATION_ERROR" } ``` --- ### `POST /folders` Create a folder. **Request body:** ```json { "name": "Folder Name", "projectId": "...", "parentId": "..." } ``` **Response `201`:** ```json { "id": "...", "name": "...", "projectId": "...", "parentId": null, "createdAt": "...", "updatedAt": "..." } ``` --- ### `GET /folders/:id` Get a single folder. **Response `200`:** ```json { "id": "...", "name": "...", "projectId": "...", "parentId": null, "createdAt": "...", "updatedAt": "..." } ``` --- ### `PUT /folders/:id` Update a folder (rename). **Request body:** ```json { "name": "New Folder Name" } ``` **Response `200`:** ```json { "id": "...", "name": "New Folder Name", ... } ``` --- ### `DELETE /folders/:id` Delete a folder. **Response `200`:** ```json { "deleted": true, "id": "..." } ``` --- ### `GET /folders/:id/documents` List documents directly in a folder. **Response `200`:** ```json { "documents": [...], "total": 2 } ``` --- ### `GET /folders/:id/tree` Get full folder tree (sub-folders + documents recursively). **Response `200`:** ```json { "folder": { "id": "...", "name": "..." }, "documents": [...], "subFolders": [...], "totalDocuments": 4 } ``` --- ## Documents ### `GET /documents` List documents with optional filters. **Query params:** - `tag` - Filter by tag - `library` - Filter by library ID - `project` - Filter by project ID - `folder` - Filter by folder ID - `type` - Filter by type (e.g., `requirement`, `general`) - `status` - Filter by status (e.g., `draft`, `approved`) - `limit` - Max results (default 50) - `offset` - Pagination offset (default 0) **Response `200`:** ```json { "documents": [...], "total": 10, "limit": 50, "offset": 0 } ``` --- ### `POST /documents` Create a document. **Request body:** ```json { "title": "Doc Title", "libraryId": "...", "projectId": "...", "folderId": "...", "content": "# Markdown content", "tags": ["tag1", "tag2"], "type": "requirement", "priority": "high", "status": "draft" } ``` **Response `201`:** ```json { "id": "...", "title": "...", "content": "...", "tags": [...], "type": "...", "priority": "...", "status": "...", "createdAt": "...", "updatedAt": "..." } ``` --- ### `GET /documents/:id` Get a single document. **Response `200`:** ```json { "id": "...", "title": "...", "content": "...", "tags": [...], "type": "...", "priority": "...", "status": "...", "createdAt": "...", "updatedAt": "..." } ``` --- ### `PUT /documents/:id` Update a document. **Request body:** ```json { "title": "New Title", "content": "...", "tags": [...], "type": "...", "priority": "...", "status": "...", "folderId": "..." } ``` **Response `200`:** Document object. --- ### `DELETE /documents/:id` Delete a document. **Response `200`:** ```json { "deleted": true, "id": "..." } ``` --- ### `GET /documents/:id/export` Export document as markdown. **Response `200`:** Raw text/markdown (Content-Type: `text/markdown`). --- ### `POST /documents/:id/tags` Add tags to a document. **Request body:** ```json { "tags": ["new-tag", "another"] } ``` **Response `200`:** Updated document object. --- ## Tags ### `GET /tags` List all tags with counts. **Response `200`:** ```json { "tags": [{ "name": "backend", "count": 5 }, ...], "total": 12 } ``` --- ### `GET /tags/:tag` Get all documents with a specific tag. **Response `200`:** ```json { "tag": "backend", "documents": [...], "count": 5 } ``` --- ## Libraries ### `GET /libraries` List root libraries. **Response `200`:** ```json { "libraries": [...] } ``` --- ### `POST /libraries` Create a library. **Request body:** ```json { "name": "Library Name", "parentId": "..." } ``` **Response `201`:** ```json { "id": "...", "name": "...", "parentId": null, "createdAt": "...", "updatedAt": "..." } ``` --- ### `GET /libraries/:id` Get library contents. **Response `200`:** ```json { "id": "...", "name": "...", "parentId": null, ... } ``` --- ### `GET /libraries/:id/tree` Get full library tree. **Response `200`:** ```json { "library": { ... }, "documents": [...], "subLibraries": [...], "totalDocuments": 3 } ``` --- ### `GET /libraries/:id/documents` List documents in a library. **Response `200`:** ```json { "documents": [...], "total": 3 } ``` --- ### `DELETE /libraries/:id` Delete a library. **Response `200`:** ```json { "deleted": true, "id": "..." } ``` --- ## Error Format All errors follow this structure: ```json { "error": "Human-readable message", "code": "ERROR_CODE" } ``` Common codes: `VALIDATION_ERROR`, `NOT_FOUND`, `UNAUTHORIZED`, `INTERNAL_ERROR`