Swagger UI
Interactive docs exposed at /swagger or /api/v1/docs. Generated from backend/swagger.go.
Swagger UI
Interactive docs exposed at /swagger or /api/v1/docs. Generated from backend/swagger.go.
Handlers
All routes defined in backend/routes.go and implemented under backend/handlers/.
Base URL: /api/v1. All mutating endpoints require a bearer JWT issued by POST /auth/login and a valid CSRF token.
POST /api/v1/auth/loginContent-Type: application/json{"email": "alice@example.com","password": "correct horse battery staple"}POST /api/v1/auth/mfa/verify{"session": "temp_id","code": "123456"}GET /api/v1/auth/mfa/backup-codes # Requires JWTAuthorization: Bearer <jwt>GET /notes → list decrypted note metadata (handler: handlers/notes.go:GetNotes)POST /notes → create note; payload must include encrypted title/content stringsGET /notes/:id/versions → fetch version history, stored in note_versionsPOST /notes/:id/versions/:version → restore a historic versionPOST /api/v1/notesAuthorization: Bearer <jwt>Content-Type: application/json{"workspace_id": "c76c0c3d-0c2d-4ac3-a71c-1b1837a8cb6d","title_encrypted": "BASE64...","content_encrypted": "BASE64...","content_hash": "BASE64..."}Tags
CRUD handlers in handlers/tags.go. Deterministic hashes stored in tags.name_hash.
Folders
Endpoints live in handlers/folders.go. Moving a note triggers a folder_id update with auditing.
Templates
Handled by handlers/templates.go. POST /templates/:id/use increments usage_count.
Example assign tag request:
POST /api/v1/notes/b9fa1ed4-9d03-4d56-94f1-8b2b50f5b9de/tags{"tag_id": "11c8a517-11e2-43dd-96df-49ca8e5d0080"}handlers/collaboration.go.handlers/share_links.go with Redis caching.POST /api/v1/notes/:id/share{"email": "teammate@example.com","permission": "write"}POST /api/v1/notes/:id/share-links{"permission": "read","expires_at": "2024-12-31T23:59:00Z"}Attachments are stored encrypted in PostgreSQL (attachments.content_encrypted). Uploads stream via multipart/form-data.
POST /api/v1/notes/:noteId/attachmentsAuthorization: Bearer <jwt>Content-Type: multipart/form-data
file=@contract.pdfname=Encrypted titleHandler: handlers/attachments.go:UploadAttachment enforces size limits and updates users.storage_used.
POST /notes/import and POST /notes/bulk-import accept encrypted JSON exports produced by the frontend.POST /notes/:id/export returns decrypted data for the caller.POST /search queries the encrypted keyword index (see /architecture/database-schema).All above implemented in handlers/import_export.go and handlers/search.go. Rate limiting tier: ImportExportLimiter or SearchLimiter.
GET /health/live → confirms process runningGET /health/ready → verifies database, Redis, and template/admin readinessGET /health → returns service status plus user countsHandlers live directly in backend/routes.go. These endpoints are unauthenticated and safe for platform probes.
Standard error envelope:
HTTP/1.1 429 Too Many RequestsContent-Type: application/json
{ "error": "Too many requests. Please try again later." }fields array from utils.ValidationError.401 with "error": "Unauthorized".403 with "error": "invalid csrf token".middleware.JWTMiddleware and the global CORS middleware guard every route:
config.AllowedOrigins (CORS_ORIGINS env).AllowHeaders includes X-CSRF-Token.csrf_token; send it back as the X-CSRF-Token header on mutating requests.Implementation references:
backend/routes.go → CORS + CSRF setupfrontend/src/services/SecureAPI.ts → automatically forwards JWT + CSRF headersAuth
backend/handlers/auth.go, middleware/jwt.go, services/mfa.go
Notes
backend/handlers/notes.go, handlers/templates.go, handlers/attachments.go
Collaboration
handlers/collaboration.go, websocket/hub.go
Search & Export
handlers/search.go, handlers/import_export.go