- Fix addDocumentTags to use POST /documents/:id/tags (was using PUT) - Add getDocumentsByTag(tag) -> GET /tags/:tag - Add getLibraryTree(id) -> GET /libraries/:id/tree - Add getLibraryDocuments(id) -> GET /libraries/:id/documents - Add getProjectDocuments(id) -> GET /projects/:id/documents - Add getFolder(id) -> GET /folders/:id - Add getFolderDocuments(id) -> GET /folders/:id/documents - Add getFolderTree(id) -> GET /folders/:id/tree - Add api-endpoints.md with full API documentation - Remove duplicate/unused dead code blocks
6.7 KiB
SimpleNote Web - API Endpoints
Base URL: /api/v1
Auth: All endpoints require Authorization: Bearer <token> header unless noted.
Auth
POST /auth/token
Generate a new API token (admin only).
Request body:
{ "label": "my-token-label" }
Response 201:
{ "token": "snk_...", "label": "my-token-label", "createdAt": "2026-03-28T..." }
GET /auth/verify
Verify a token is valid.
Response 200:
{ "valid": true, "token": "snk_..." }
Projects
GET /projects
List all projects.
Response 200:
{ "projects": [...] }
POST /projects
Create a project.
Request body:
{ "name": "Project Name", "description": "optional" }
Response 201:
{ "id": "...", "name": "...", "description": "...", "createdAt": "...", "updatedAt": "..." }
GET /projects/:id
Get a single project.
Response 200:
{ "id": "...", "name": "...", "description": "...", "createdAt": "...", "updatedAt": "..." }
Response 404:
{ "error": "Project not found", "code": "NOT_FOUND" }
PUT /projects/:id
Update a project.
Request body:
{ "name": "New Name", "description": "New desc" }
Response 200:
{ "id": "...", "name": "...", "description": "...", "createdAt": "...", "updatedAt": "..." }
DELETE /projects/:id
Delete a project.
Response 200:
{ "deleted": true, "id": "..." }
GET /projects/:id/tree
Get full project tree (folder hierarchy + documents).
Response 200:
{
"project": { "id": "...", "name": "..." },
"documents": [...],
"folders": [...],
"totalDocuments": 5
}
GET /projects/:id/documents
List documents directly in a project (not in sub-folders).
Response 200:
{ "documents": [...], "total": 3 }
Folders
GET /folders?projectId=X&parentId=Y
List folders in a project. projectId is required.
Query params:
projectId(required): Project IDparentId(optional): Parent folder ID. Omit for root folders.
Response 200:
{ "folders": [...] }
Response 400:
{ "error": "projectId query parameter is required", "code": "VALIDATION_ERROR" }
POST /folders
Create a folder.
Request body:
{ "name": "Folder Name", "projectId": "...", "parentId": "..." }
Response 201:
{ "id": "...", "name": "...", "projectId": "...", "parentId": null, "createdAt": "...", "updatedAt": "..." }
GET /folders/:id
Get a single folder.
Response 200:
{ "id": "...", "name": "...", "projectId": "...", "parentId": null, "createdAt": "...", "updatedAt": "..." }
PUT /folders/:id
Update a folder (rename).
Request body:
{ "name": "New Folder Name" }
Response 200:
{ "id": "...", "name": "New Folder Name", ... }
DELETE /folders/:id
Delete a folder.
Response 200:
{ "deleted": true, "id": "..." }
GET /folders/:id/documents
List documents directly in a folder.
Response 200:
{ "documents": [...], "total": 2 }
GET /folders/:id/tree
Get full folder tree (sub-folders + documents recursively).
Response 200:
{
"folder": { "id": "...", "name": "..." },
"documents": [...],
"subFolders": [...],
"totalDocuments": 4
}
Documents
GET /documents
List documents with optional filters.
Query params:
tag- Filter by taglibrary- Filter by library IDproject- Filter by project IDfolder- Filter by folder IDtype- 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:
{ "documents": [...], "total": 10, "limit": 50, "offset": 0 }
POST /documents
Create a document.
Request body:
{
"title": "Doc Title",
"libraryId": "...",
"projectId": "...",
"folderId": "...",
"content": "# Markdown content",
"tags": ["tag1", "tag2"],
"type": "requirement",
"priority": "high",
"status": "draft"
}
Response 201:
{ "id": "...", "title": "...", "content": "...", "tags": [...], "type": "...", "priority": "...", "status": "...", "createdAt": "...", "updatedAt": "..." }
GET /documents/:id
Get a single document.
Response 200:
{ "id": "...", "title": "...", "content": "...", "tags": [...], "type": "...", "priority": "...", "status": "...", "createdAt": "...", "updatedAt": "..." }
PUT /documents/:id
Update a document.
Request body:
{ "title": "New Title", "content": "...", "tags": [...], "type": "...", "priority": "...", "status": "...", "folderId": "..." }
Response 200: Document object.
DELETE /documents/:id
Delete a document.
Response 200:
{ "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:
{ "tags": ["new-tag", "another"] }
Response 200: Updated document object.
Tags
GET /tags
List all tags with counts.
Response 200:
{ "tags": [{ "name": "backend", "count": 5 }, ...], "total": 12 }
GET /tags/:tag
Get all documents with a specific tag.
Response 200:
{ "tag": "backend", "documents": [...], "count": 5 }
Libraries
GET /libraries
List root libraries.
Response 200:
{ "libraries": [...] }
POST /libraries
Create a library.
Request body:
{ "name": "Library Name", "parentId": "..." }
Response 201:
{ "id": "...", "name": "...", "parentId": null, "createdAt": "...", "updatedAt": "..." }
GET /libraries/:id
Get library contents.
Response 200:
{ "id": "...", "name": "...", "parentId": null, ... }
GET /libraries/:id/tree
Get full library tree.
Response 200:
{
"library": { ... },
"documents": [...],
"subLibraries": [...],
"totalDocuments": 3
}
GET /libraries/:id/documents
List documents in a library.
Response 200:
{ "documents": [...], "total": 3 }
DELETE /libraries/:id
Delete a library.
Response 200:
{ "deleted": true, "id": "..." }
Error Format
All errors follow this structure:
{ "error": "Human-readable message", "code": "ERROR_CODE" }
Common codes: VALIDATION_ERROR, NOT_FOUND, UNAUTHORIZED, INTERNAL_ERROR