Files
project-lyra/docs/TRILLIUM_API.md
serversdwn 794baf2a96 0.9.0 - Added Trilium ETAPI integration.
Lyra can now: Search trilium notes and create new notes. with proper ETAPI auth.
2025-12-29 01:58:20 -05:00

731 lines
19 KiB
Markdown

# TriliumNext ETAPI Complete API Reference
## Overview
ETAPI is TriliumNext's public/external REST API available since Trilium v0.50.
**Base URLs:**
- `http://localhost:37740/etapi`
- `http://localhost:8080/etapi`
**API Version:** 1.0.0
**License:** Apache 2.0
## Authentication
All operations require authentication using one of these methods:
### 1. ETAPI Token Authentication (Recommended)
```http
GET /etapi/app-info
Authorization: <ETAPI_TOKEN>
```
OR (since v0.93.0):
```http
GET /etapi/app-info
Authorization: Bearer <ETAPI_TOKEN>
```
### 2. Basic Authentication (since v0.56)
```http
GET /etapi/app-info
Authorization: Basic <BASE64(username:password)>
```
**Note:** Password must be the ETAPI token (NOT your Trilium password).
### 3. Get Token via API
```http
POST /etapi/auth/login
Content-Type: application/json
{
"password": "your_trilium_password"
}
```
**Response:**
```json
{
"authToken": "Bc4bFn0Ffiok_4NpbVCDnFz7B2WU+pdhW8B5Ne3DiR5wXrEyqdjgRIsk="
}
```
---
## Complete API Endpoints
### Authentication
#### Login
- **POST** `/auth/login`
- **Description:** Get an ETAPI token based on password
- **Security:** None (public endpoint)
- **Request Body:**
```json
{
"password": "string"
}
```
- **Responses:**
- `201`: Auth token created
- `429`: Client IP blacklisted (too many failed attempts)
---
### Application Information
#### Get App Info
- **GET** `/app-info`
- **Description:** Get application information
- **Response:**
```json
{
"appVersion": "0.91.0",
"dbVersion": 231,
"syncVersion": 25,
"buildDate": "2022-02-09T22:52:36+01:00",
"buildRevision": "23daaa2387a0655685377f0a541d154aeec2aae8",
"dataDirectory": "/home/user/data",
"clipperProtocolVersion": "1.0",
"utcDateTime": "2022-03-07T21:54:25.277Z"
}
```
#### Get Metrics
- **GET** `/etapi/metrics`
- **Description:** Get Prometheus-format metrics for monitoring
- **Query Parameters:**
- `format`: `json` or `prometheus` (default: prometheus)
- **Response:** Metrics data including note counts, db stats, etc.
---
### Notes Management
#### Create Note
- **POST** `/create-note`
- **Description:** Create a note and place it into the note tree
- **Request Body:**
```json
{
"parentNoteId": "root",
"title": "My Note",
"type": "text",
"mime": "text/html",
"content": "<p>Hello World</p>",
"notePosition": 10,
"prefix": "",
"isExpanded": false,
"noteId": "customId123",
"branchId": "customBranchId",
"utcDateCreated": "2021-12-31 19:18:11.930Z",
"utcDateModified": "2021-12-31 19:18:11.930Z"
}
```
- **Required Fields:** `parentNoteId`, `title`, `type`, `content`
- **Optional Fields:** `notePosition`, `prefix`, `isExpanded`, `noteId`, `branchId`, timestamps
- **Note Types:**
- `text` - Rich text notes
- `code` - Code notes (requires `mime`)
- `file` - File attachments (requires `mime`)
- `image` - Image notes (requires `mime`)
- `search` - Saved search
- `book` - Book/container note
- `relationMap` - Relation map
- `render` - Render note
- `noteMap` - Note map
- `mermaid` - Mermaid diagrams
- `webView` - Web view
- `shortcut` - Shortcut
- `doc` - Document
- `contentWidget` - Content widget
- `launcher` - Launcher
- `canvas` - Canvas note
- **Response:** `201` with `NoteWithBranch` object
#### Search Notes
- **GET** `/notes`
- **Description:** Search notes using query syntax
- **Query Parameters:**
- `search` (required): Search query string
- `ancestorNoteId`: Search in subtree only
- `fastSearch`: Boolean for fast search mode
- `includeArchivedNotes`: Include archived notes (default: false)
- `orderBy`: Field to order by (e.g., `title`, `dateModified`)
- `orderDirection`: `asc` or `desc`
- `limit`: Maximum results (default: 10)
- `debug`: Enable debug info
- **Response:** Array of note objects
#### Get Note
- **GET** `/notes/{noteId}`
- **Description:** Get note metadata by ID
- **Path Parameters:**
- `noteId`: Note ID
- **Response:** Note object with metadata
#### Get Note Content
- **GET** `/notes/{noteId}/content`
- **Description:** Get note content (HTML/text for text notes, binary for files/images)
- **Path Parameters:**
- `noteId`: Note ID
- **Response:** Note content (content-type varies by note type)
#### Update Note Content
- **PUT** `/notes/{noteId}/content`
- **Description:** Update note content
- **Path Parameters:**
- `noteId`: Note ID
- **Request Body:** Raw content (HTML for text notes, binary for files)
- **Response:** `204` No Content
#### Update Note Metadata
- **PATCH** `/notes/{noteId}`
- **Description:** Update note metadata (title, type, mime, etc.)
- **Path Parameters:**
- `noteId`: Note ID
- **Request Body:**
```json
{
"title": "Updated Title",
"type": "text",
"mime": "text/html"
}
```
- **Response:** `200` with updated note object
#### Delete Note
- **DELETE** `/notes/{noteId}`
- **Description:** Delete note and all its branches
- **Path Parameters:**
- `noteId`: Note ID
- **Response:** `204` No Content
- **Note:** Deletes all clones/branches of the note
#### Export Note
- **GET** `/notes/{noteId}/export`
- **Description:** Export note as ZIP file (with optional subtree)
- **Path Parameters:**
- `noteId`: Note ID (use "root" to export entire tree)
- **Query Parameters:**
- `format`: `html` or `markdown`/`md`
- **Response:** ZIP file download
---
### Branches Management
Branches represent note clones/placements in the tree. A single note can exist in multiple locations via different branches.
#### Create Branch
- **POST** `/branches`
- **Description:** Create a branch (clone a note to another location)
- **Request Body:**
```json
{
"noteId": "existingNoteId",
"parentNoteId": "targetParentId",
"prefix": "Branch Prefix",
"notePosition": 10,
"isExpanded": false,
"branchId": "customBranchId"
}
```
- **Required Fields:** `noteId`, `parentNoteId`
- **Response:** `201` with Branch object
#### Get Branch
- **GET** `/branches/{branchId}`
- **Description:** Get branch by ID
- **Path Parameters:**
- `branchId`: Branch ID
- **Response:** Branch object
#### Update Branch
- **PATCH** `/branches/{branchId}`
- **Description:** Update branch (prefix, notePosition)
- **Path Parameters:**
- `branchId`: Branch ID
- **Request Body:**
```json
{
"prefix": "New Prefix",
"notePosition": 20,
"isExpanded": true
}
```
- **Response:** `200` with updated branch
- **Note:** Only `prefix`, `notePosition`, and `isExpanded` can be updated. For other properties, delete and recreate.
#### Set Branch Prefix
- **PATCH** `/branches/{branchId}/set-prefix`
- **Description:** Set branch prefix
- **Path Parameters:**
- `branchId`: Branch ID
- **Request Body:**
```json
{
"prefix": "New Prefix"
}
```
#### Move Branch to Parent
- **POST** `/branches/{branchId}/set-note-to-parent`
- **Description:** Move branch to a different parent
- **Path Parameters:**
- `branchId`: Branch ID
- **Request Body:**
```json
{
"parentNoteId": "newParentId"
}
```
#### Delete Branch
- **DELETE** `/branches/{branchId}`
- **Description:** Delete branch (removes note from this tree location)
- **Path Parameters:**
- `branchId`: Branch ID
- **Response:** `204` No Content
- **Note:** If this is the last branch of the note, the note itself is deleted
#### Refresh Note Ordering
- **PATCH** `/refresh-note-ordering/{parentNoteId}`
- **Description:** Push notePosition changes to connected clients
- **Path Parameters:**
- `parentNoteId`: Parent note ID
- **Note:** Call this after updating branch notePositions to sync changes to clients
---
### Attributes Management
Attributes include labels (key-value metadata) and relations (links between notes).
#### Create Attribute
- **POST** `/attributes`
- **Description:** Create an attribute
- **Request Body:**
```json
{
"noteId": "targetNoteId",
"type": "label",
"name": "priority",
"value": "high",
"position": 10,
"isInheritable": false,
"attributeId": "customAttributeId"
}
```
- **Attribute Types:**
- `label`: Key-value metadata
- `relation`: Link to another note (value is target noteId)
- **Required Fields:** `noteId`, `type`, `name`
- **Optional Fields:** `value`, `position`, `isInheritable`, `attributeId`
- **Response:** `201` with Attribute object
#### Create Attribute for Note
- **POST** `/notes/{noteId}/attributes`
- **Description:** Create attribute for specific note
- **Path Parameters:**
- `noteId`: Note ID
- **Request Body:** Same as Create Attribute (noteId not required)
#### Get Attribute
- **GET** `/attributes/{attributeId}`
- **Description:** Get attribute by ID
- **Path Parameters:**
- `attributeId`: Attribute ID
- **Response:** Attribute object
#### Get Note Attributes
- **GET** `/notes/{noteId}/attributes`
- **Description:** Get all attributes for a note
- **Path Parameters:**
- `noteId`: Note ID
- **Response:** Array of attribute objects
#### Update Attribute
- **PATCH** `/attributes/{attributeId}`
- **Description:** Update attribute (name, value, position)
- **Path Parameters:**
- `attributeId`: Attribute ID
- **Request Body:**
```json
{
"name": "newName",
"value": "newValue",
"position": 20,
"isInheritable": true
}
```
- **Response:** `200` with updated attribute
#### Delete Attribute
- **DELETE** `/attributes/{attributeId}`
- **Description:** Delete attribute
- **Path Parameters:**
- `attributeId`: Attribute ID
- **Response:** `204` No Content
---
### Attachments Management
#### Create Attachment
- **POST** `/attachments`
- **Description:** Create attachment for a note
- **Request Body:** Multipart form data with file
```json
{
"ownerId": "noteId",
"role": "image",
"mime": "image/png",
"title": "Screenshot",
"position": 10,
"attachmentId": "customAttachmentId"
}
```
- **Required Fields:** `ownerId`, file data
- **Optional Fields:** `role`, `mime`, `title`, `position`, `attachmentId`
- **Response:** `201` with Attachment object
#### Create Attachment for Note
- **POST** `/notes/{noteId}/attachments`
- **Description:** Create attachment (alternative endpoint)
- **Path Parameters:**
- `noteId`: Note ID
- **Request Body:** Same as Create Attachment (ownerId not required)
#### Get Attachment
- **GET** `/attachments/{attachmentId}`
- **Description:** Get attachment metadata
- **Path Parameters:**
- `attachmentId`: Attachment ID
- **Response:** Attachment object
#### Get Attachment Content
- **GET** `/attachments/{attachmentId}/content`
- **Description:** Get attachment binary content
- **Path Parameters:**
- `attachmentId`: Attachment ID
- **Response:** Binary content with appropriate MIME type
#### Get Note Attachments
- **GET** `/notes/{noteId}/attachments`
- **Description:** Get all attachments for a note
- **Path Parameters:**
- `noteId`: Note ID
- **Response:** Array of attachment objects
#### Update Attachment Content
- **PUT** `/attachments/{attachmentId}/content`
- **Description:** Update attachment binary content
- **Path Parameters:**
- `attachmentId`: Attachment ID
- **Request Body:** Binary file data
- **Response:** `204` No Content
#### Update Attachment Metadata
- **PATCH** `/attachments/{attachmentId}`
- **Description:** Update attachment metadata
- **Path Parameters:**
- `attachmentId`: Attachment ID
- **Request Body:**
```json
{
"title": "New Title",
"role": "image",
"mime": "image/jpeg",
"position": 20
}
```
- **Response:** `200` with updated attachment
#### Delete Attachment
- **DELETE** `/attachments/{attachmentId}`
- **Description:** Delete attachment
- **Path Parameters:**
- `attachmentId`: Attachment ID
- **Response:** `204` No Content
---
### Special Purpose Endpoints
#### Get Inbox Note
- **GET** `/inbox/{date}`
- **Description:** Get or create inbox note for specific date
- **Path Parameters:**
- `date`: Date in format `YYYY-MM-DD`
- **Response:** Note object
- **Behavior:**
- Returns fixed inbox note (marked with `#inbox` label) if configured
- Otherwise returns/creates day note in journal for the specified date
#### Get Day Note
- **GET** `/calendar/days/{date}`
- **Description:** Get or create day note
- **Path Parameters:**
- `date`: Date in format `YYYY-MM-DD` (e.g., `2022-12-31`)
- **Response:** Note object
- **Note:** Creates note if it doesn't exist
#### Get Month Note
- **GET** `/calendar/months/{month}`
- **Description:** Get or create month note
- **Path Parameters:**
- `month`: Month in format `YYYY-MM` (e.g., `2022-12`)
- **Response:** Note object
- **Note:** Creates note if it doesn't exist
#### Get Year Note
- **GET** `/calendar/years/{year}`
- **Description:** Get or create year note
- **Path Parameters:**
- `year`: Year in format `YYYY` (e.g., `2022`)
- **Response:** Note object
- **Note:** Creates note if it doesn't exist
---
### Backup
#### Create Backup
- **PUT** `/backup/{backupName}`
- **Description:** Create a database backup
- **Path Parameters:**
- `backupName`: Backup filename (without extension)
- **Example:** `PUT /backup/now` creates `backup-now.db`
- **Response:** `204` No Content
---
## Data Types and Schemas
### Common Field Types
- **EntityId**: 12-character alphanumeric string (e.g., `evnnmvHTCgIn`)
- **LocalDateTime**: `YYYY-MM-DD HH:mm:ss.SSS±ZZZZ` (e.g., `2021-12-31 20:18:11.930+0100`)
- **UtcDateTime**: `YYYY-MM-DD HH:mm:ss.SSSZ` (e.g., `2021-12-31 19:18:11.930Z`)
### Note Position
- Normal ordering: 10, 20, 30, 40...
- First position: use value < 10 (e.g., 5)
- Last position: use large value (e.g., 1000000)
- Between existing: use value between their positions
### Branch Prefix
Branch-specific title prefix displayed in the tree. Useful when same note appears in multiple locations with slightly different context.
---
## Error Responses
All endpoints may return these error responses:
### Standard Error Object
```json
{
"status": 400,
"code": "NOTE_IS_PROTECTED",
"message": "Note 'evnnmvHTCgIn' is protected and cannot be modified through ETAPI"
}
```
### Common HTTP Status Codes
- `200`: Success
- `201`: Resource created
- `204`: Success (no content)
- `400`: Bad request (validation error)
- `401`: Unauthorized (invalid token)
- `404`: Not found
- `429`: Too many requests (rate limited/blacklisted)
- `500`: Internal server error
### Common Error Codes
- `NOTE_IS_PROTECTED`: Protected note cannot be modified
- `INVALID_TOKEN`: Invalid or expired ETAPI token
- `VALIDATION_ERROR`: Request validation failed
- `NOT_FOUND`: Resource not found
- `RATE_LIMITED`: Too many requests
---
## Search Query Syntax
The `/notes` search endpoint supports Trilium's query language:
### Basic Search
```
python # Search in title and content
#todo # Find notes with label "todo"
~project # Find notes with relation "project"
```
### Advanced Operators
```
note.title =* "meeting" # Title contains "meeting"
note.title %= ".*2022.*" # Regex in title
#priority = "high" # Label with specific value
~template = "someNoteId" # Relation to specific note
#created >= MONTH-1 # Created in last month
note.dateModified >= "2022-01-01" # Modified after date
```
### Combining Queries
```
#todo AND #urgent # Both labels
#work OR #personal # Either label
#project AND note.title =* "Q1" # Label AND title condition
```
### Hierarchical Queries
```
note.parents.title = "Work" # Direct parent title
note.ancestors.title = "Archive" # Any ancestor title
note.children.title =* "Chapter" # Direct children
```
See Trilium Search Documentation for complete syntax.
---
## Rate Limiting
- Failed authentication attempts can result in IP blacklist
- Blacklisted IPs receive `429` response
- Wait period required before retry
- Use valid tokens to avoid blacklisting
---
## Configuration Notes
### Upload Size Limits
- Default: 250MB
- Disable limit: Set `TRILIUM_NO_UPLOAD_LIMIT=true`
- Custom limit: Set `MAX_ALLOWED_FILE_SIZE_MB=<size>`
### Network Configuration
ETAPI accessible through:
- Local interface: `http://localhost:8080/etapi`
- Network interface: Configure reverse proxy (nginx/Apache)
- SSL/TLS: Recommended for production use
---
## Best Practices
1. **Always use ETAPI tokens** (not passwords) for authentication
2. **Store tokens securely** - they provide full access to your Trilium instance
3. **Use notePosition strategically** - leave gaps (10, 20, 30) for easy insertion
4. **Handle branches carefully** - deleting last branch deletes the note
5. **Check for protected notes** - they cannot be modified via ETAPI
6. **Implement rate limiting** in your client to avoid blacklisting
7. **Use search efficiently** - leverage fastSearch for better performance
8. **Call refresh-note-ordering** after bulk branch position updates
9. **Validate data before submission** - reduce error responses
10. **Handle errors gracefully** - check status codes and error messages
---
## Example Workflows
### Create a Note with Attributes
```bash
# 1. Create note
NOTE_RESPONSE=$(curl -X POST "$SERVER/etapi/create-note" \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"parentNoteId": "root",
"title": "Project TODO",
"type": "text",
"content": "<p>Task list</p>"
}')
NOTE_ID=$(echo $NOTE_RESPONSE | jq -r '.note.noteId')
# 2. Add label
curl -X POST "$SERVER/etapi/attributes" \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"noteId\": \"$NOTE_ID\",
\"type\": \"label\",
\"name\": \"priority\",
\"value\": \"high\"
}"
```
### Clone Note to Multiple Locations
```bash
# Clone note to another parent
curl -X POST "$SERVER/etapi/branches" \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"noteId": "existingNoteId",
"parentNoteId": "anotherParentId",
"prefix": "Reference: "
}'
```
### Daily Journal Entry
```bash
# Get or create today's note
TODAY=$(date +%Y-%m-%d)
curl "$SERVER/etapi/calendar/days/$TODAY" \
-H "Authorization: $TOKEN"
```
---
## Client Libraries
### Python
- **trilium-py**: Full-featured client with extended functionality
- **PyTrilium**: Lightweight wrapper matching OpenAPI spec
- **trilium-alchemy**: SQLAlchemy-style SDK with CLI toolkit
### Node.js
- **trilium-etapi**: TypeScript wrapper with type safety
### Other Tools
- **trilium-mcp-server**: Model Context Protocol server for LLMs
- **openapi-mcp-generator**: Generate MCP servers from OpenAPI specs
---
## Version Compatibility
- ETAPI introduced: Trilium v0.50
- Basic Auth support: v0.56
- Bearer token format: v0.93.0
- TriliumNext fork: Compatible with Trilium API, ongoing development
Check `/app-info` endpoint for version details of your instance.
---
## Additional Resources
- **Official Documentation**: https://docs.triliumnotes.org/
- **GitHub Repository**: https://github.com/TriliumNext/Trilium
- **Search Syntax Guide**: https://github.com/zadam/trilium/wiki/Search
- **Community Resources**: https://github.com/Nriver/awesome-trilium
---
**License:** Apache 2.0
**Maintainer:** TriliumNext Community
**Contact:** https://github.com/TriliumNext/Trilium/discussions