Base URL: http://localhost:9000
All protected endpoints require authentication when authentication.enabled: true in config.yaml.
GET /loginReturns the HTML login page.
POST /login
Content-Type: application/json
{
"password": "your_password"
}Authenticates the user session.
Response:
{
"success": true,
"message": "Login successful"
}POST /logoutLogs out the current user session.
GET /api/notesReturns all notes with their metadata and folder structure.
Response:
{
"notes": [
{
"path": "folder/note.md",
"name": "note",
"folder": "folder",
"title": "Note Title",
"created_at": "2025-11-26T10:30:00Z",
"updated_at": "2025-11-26T11:00:00Z"
}
]
}GET /api/notes/{note_path}Retrieve the content of a specific note.
Example:
curl http://localhost:9000/api/notes/folder/mynote.mdResponse:
{
"success": true,
"path": "folder/mynote.md",
"content": "# My Note\nContent here..."
}POST /api/notes/{note_path}
Content-Type: application/json
{
"content": "# My Note\nNote content here..."
}Response:
{
"success": true,
"path": "test.md",
"message": "Note created/updated successfully",
"content": "# My Note\nNote content here..."
}Linux/Mac:
curl -X POST http://localhost:9000/api/notes/test.md \
-H "Content-Type: application/json" \
-d '{"content": "# Hello World"}'Windows PowerShell:
curl.exe -X POST http://localhost:9000/api/notes/test.md -H "Content-Type: application/json" -d "{\"content\": \"# Hello World\"}"DELETE /api/notes/{note_path}Example:
curl -X DELETE http://localhost:9000/api/notes/test.mdPOST /api/notes/move
Content-Type: application/json
{
"oldPath": "note.md",
"newPath": "folder/note.md"
}Response:
{
"success": true,
"message": "Note moved successfully"
}GET /api/media/{media_path}Retrieve a media file (image, audio, video, PDF) with authentication protection.
Example:
curl http://localhost:9000/api/media/folder/_attachments/image-20240417093343.pngSecurity Note: This endpoint requires authentication and validates that:
- The media path is within the notes directory (prevents directory traversal)
- The file exists and is a valid media format
- The requesting user is authenticated (if auth is enabled)
POST /api/upload-media
Content-Type: multipart/form-data
file: <media file>
note_path: <path of note to attach to>Upload a media file to the _attachments directory. Files are automatically organized per-folder and named with timestamps to prevent conflicts.
Supported formats & size limits:
| Type | Formats | Max Size |
|---|---|---|
| Images | JPG, PNG, GIF, WebP | 10 MB |
| Audio | MP3, WAV, OGG, M4A | 50 MB |
| Video | MP4, WebM, MOV, AVI | 100 MB |
| Documents | 20 MB |
Response:
{
"success": true,
"path": "folder/_attachments/media-20240417093343.png",
"filename": "media-20240417093343.png",
"message": "Media uploaded successfully"
}Example (using curl):
curl -X POST http://localhost:9000/api/upload-media \
-F "file=@/path/to/file.mp3" \
-F "note_path=folder/mynote.md"Windows PowerShell:
curl.exe -X POST http://localhost:9000/api/upload-media -F "file=@C:\path\to\video.mp4" -F "note_path=folder/mynote.md"POST /api/media/move
Content-Type: application/json
{
"oldPath": "_attachments/image.png",
"newPath": "folder/_attachments/image.png"
}Move a media file to a different location. Supports drag & drop in the UI.
Response:
{
"success": true,
"message": "Media moved successfully",
"newPath": "folder/_attachments/image.png"
}GET /api/media/orphanedLists media files that are not referenced by any notes (dangling attachments).
Response:
{
"success": true,
"files": [
{
"path": "folder/_attachments/old-image.png",
"size": 102400,
"modified": "2025-11-25T10:30:00Z"
}
],
"count": 1
}DELETE /api/media/orphanedDeletes all orphaned media files. Returns summary of deleted files.
Response:
{
"success": true,
"deleted_count": 5,
"freed_space_bytes": 1048576,
"message": "Cleaned up 5 orphaned files"
}Rate Limit: 30 requests per 60 seconds
POST /api/folders
Content-Type: application/json
{
"path": "Projects/2025"
}Response:
{
"success": true,
"message": "Folder created successfully"
}Rate Limit: 30 requests per 60 seconds
DELETE /api/folders/{folder_path}Deletes a folder and all its contents.
Example:
curl -X DELETE http://localhost:9000/api/folders/Projects/ArchiveRate Limit: 20 requests per 60 seconds
POST /api/folders/move
Content-Type: application/json
{
"oldPath": "OldFolder",
"newPath": "NewFolder"
}Response:
{
"success": true,
"message": "Folder moved successfully"
}Rate Limit: 20 requests per 60 seconds
POST /api/folders/rename
Content-Type: application/json
{
"oldPath": "Projects",
"newName": "Work"
}Response:
{
"success": true,
"message": "Folder renamed successfully"
}Rate Limit: 30 requests per 60 seconds
GET /api/search?q={query}Optional query parameters:
q- Search query (required)page- Page number (default: 1)per_page- Results per page (default: 50)
Example:
curl "http://localhost:9000/api/search?q=hello&page=1&per_page=20"Response:
{
"query": "hello",
"results": [
{
"path": "folder/note.md",
"title": "Hello World",
"snippet": "...<mark>hello</mark> world...",
"score": 1.234
}
],
"total": 10,
"page": 1,
"per_page": 20,
"pages": 1
}Rate Limit: 60 requests per 60 seconds
GET /api/tagsReturns all tags found in notes with their usage counts.
Response:
{
"tags": {
"python": 5,
"tutorial": 3,
"backend": 2
}
}GET /api/tags/{tag_name}Returns all notes that have a specific tag.
Response:
{
"tag": "python",
"notes": [
{
"path": "tutorials/python-basics.md",
"name": "python-basics",
"folder": "tutorials",
"tags": ["python", "tutorial"]
}
]
}GET /api/templatesReturns all available note templates from the _templates folder.
Rate Limit: 120 requests per 60 seconds
Response:
{
"templates": [
{
"name": "meeting-notes",
"path": "_templates/meeting-notes.md",
"modified": "2025-11-26T10:30:00"
},
{
"name": "daily-journal",
"path": "_templates/daily-journal.md",
"modified": "2025-11-26T10:25:00"
}
]
}GET /api/templates/{template_name}Returns the content of a specific template.
Parameters:
template_name- Template name (without .md extension)
Response:
{
"name": "meeting-notes",
"content": "# Meeting Notes\n\nDate: {{date}}\nAttendees: {{attendees}}\n..."
}Rate Limit: 120 requests per 60 seconds
POST /api/templates/create-note
Content-Type: application/json
{
"templateName": "meeting-notes",
"notePath": "meetings/weekly-sync.md"
}Creates a new note from a template with placeholder replacement.
Supported placeholders:
{{date}}- Current date (YYYY-MM-DD){{time}}- Current time (HH:MM:SS){{datetime}}- Current datetime{{timestamp}}- Unix timestamp{{year}}- Current year (YYYY){{month}}- Current month (MM){{day}}- Current day (DD){{title}}- Note name without extension{{folder}}- Parent folder name
Response:
{
"success": true,
"path": "meetings/weekly-sync.md",
"message": "Note created from template successfully",
"content": "# Meeting Notes\n\nDate: 2025-11-26\nAttendees: \n..."
}Rate Limit: 60 requests per 60 seconds
Share notes publicly without requiring authentication.
POST /api/share/{note_path}
Content-Type: application/json
{
"theme": "dracula"
}Creates a share token for the note. The theme is optional (defaults to "light").
Response:
{
"success": true,
"token": "LRFEo86oSVeJ3Gju",
"url": "http://localhost:9000/share/LRFEo86oSVeJ3Gju",
"note_path": "folder/note.md"
}Rate Limit: 30 requests per 60 seconds
GET /api/share/{note_path}Check if a note is currently shared.
Response:
{
"shared": true,
"token": "LRFEo86oSVeJ3Gju",
"url": "http://localhost:9000/share/LRFEo86oSVeJ3Gju",
"theme": "dracula",
"created": "2026-01-15T10:30:00+00:00"
}Rate Limit: 120 requests per 60 seconds
DELETE /api/share/{note_path}Removes public access to the note.
Response:
{
"success": true,
"message": "Share revoked"
}Rate Limit: 30 requests per 60 seconds
GET /api/shared-notesReturns paths of all currently shared notes.
Response:
{
"paths": ["folder/note.md", "another.md"]
}Rate Limit: 60 requests per 60 seconds
GET /share/{token}Public endpoint - no authentication required. Returns the note as a standalone HTML page with the theme set when sharing was created. Optionally generate QR code for mobile access.
Example:
curl http://localhost:9000/share/LRFEo86oSVeJ3GjuFind all notes that link to a specific note using wikilink syntax [[link]] and markdown links.
GET /api/backlinks/{note_path}Returns source notes and where they link to the target note.
Response:
{
"success": true,
"backlinks": [
{
"source_path": "other-note.md",
"link_texts": ["note-name", "folder/note"],
"links": [
{
"line": 10,
"context": "See also [[note-name]]",
"type": "wikilink"
},
{
"line": 15,
"context": "Check [this](folder/note) out",
"type": "markdown"
}
]
}
],
"count": 1
}Rate Limit: 60 requests per 60 seconds
Get comprehensive statistics for a note including word count, reading time, and structure analysis.
GET /api/stats/{note_path}Response:
{
"success": true,
"data": {
"words": 150,
"sentences": 12,
"characters": 800,
"total_characters": 1000,
"reading_time_minutes": 1,
"lines": 50,
"paragraphs": 10,
"list_items": 5,
"tables": 1,
"links": 8,
"internal_links": 6,
"external_links": 2,
"wikilinks": 3,
"code_blocks": 2,
"inline_code": 5,
"headings": {
"h1": 1,
"h2": 3,
"h3": 2
},
"tasks": {
"total": 4,
"completed": 2,
"pending": 2
},
"images": 1,
"blockquotes": 2
}
}Rate Limit: 60 requests per 60 seconds
GET /api/themesReturns all available themes from the themes directory.
GET /api/themes/{theme_id}Returns the CSS content for the specified theme.
Example:
curl http://localhost:9000/api/themes/darkGET /api/localesReturns all available locale (translation) files.
GET /api/locales/{language_code}Returns locale content for the specified language (e.g., en-US, zh-CN).
GET /wsWebSocket endpoint for receiving real-time notifications. When authentication is enabled, requires valid session.
Events:
notes_updated- Broadcast when background note scan completes (new/updated/deleted notes detected)
Example message:
{
"event": "notes_updated",
"timestamp": "2025-11-26T11:00:00Z"
}GET /api/configReturns application configuration (may filter sensitive fields like secret_key).
Response:
{
"app": {
"name": "GoNote",
"version": "0.25.0"
},
"server": {
"host": "0.0.0.0",
"port": 9000,
"debug": false
},
"authentication": {
"enabled": false
},
"search": {
"enabled": true
}
}Rate Limit: 120 requests per 60 seconds
GET /healthReturns system health status.
Response:
{
"status": "healthy",
"timestamp": "2025-11-26T11:00:00Z",
"version": "0.25.0"
}GoNote includes the following security middleware:
- CSRF Protection: Double Submit Cookie pattern applied to state-changing operations
- Rate Limiting: Per-endpoint rate limits to prevent abuse
- CORS: Configurable allowed origins
- Secure Cookies: Automatically enabled when HTTPS detected
CSRF Token: The CSRF token is set in a cookie named csrf_. For state-changing requests, include the token in the X-CSRF-Token header. The token is readable by JavaScript (HTTPOnly=false) to facilitate this.
All endpoints return JSON responses:
Success (generic):
{
"success": true,
"data": { ... }
}Success (specific): Some endpoints use custom response structures tailored to their data (see individual endpoint docs).
Error:
{
"detail": "Error message"
}A global rate limiter is applied to all requests. The default configuration (when RATE_LIMIT_ENABLED=true) is:
- 30 requests per 1 second window
Configure via:
rate_limit:
enabled: true
max_requests: 30
window_seconds: 1Or environment variables:
RATE_LIMIT_ENABLED=true
RATE_LIMIT_MAX=30
RATE_LIMIT_WINDOW=1
Some endpoints have custom limits:
| Endpoint | Limit | Window |
|---|---|---|
POST /login |
10 | 60s |
POST /api/notes/move |
30 | 60s |
DELETE /api/notes/* |
30 | 60s |
POST /api/folders |
30 | 60s |
POST /api/folders/rename |
30 | 60s |
POST /api/share/* |
30 | 60s |
DELETE /api/share/* |
30 | 60s |
POST /api/media/move |
30 | 60s |
POST /api/upload-media |
20 | 60s |
POST /api/templates/create-note |
60 | 60s |
GET /api/backlinks/* |
60 | 60s |
GET /api/stats/* |
60 | 60s |
GET /api/shared-notes |
60 | 60s |
GET /api/templates |
120 | 60s |
GET /api/templates/* |
120 | 60s |
GET /api/share/* |
120 | 60s |
GET /api/config |
120 | 60s |
When rate limit is exceeded, server returns 429 Too Many Requests.
- Use
/api/configto discover current runtime configuration (except secrets) - All responses are case-sensitive; use
snake_casefor field names - For drag & drop media uploads, the
note_pathparameter indicates which note the media is attached to - All paths are URL-encoded and case-sensitive
- When authentication is enabled, include session cookies in requests (browser handles this automatically; for API calls, ensure you're logged in)
Tip: For password-protected deployments, log in first via POST /login to obtain session cookies, then all /api/* endpoints will be accessible.