0.9.0 - Added Trilium ETAPI integration.
Lyra can now: Search trilium notes and create new notes. with proper ETAPI auth.
This commit is contained in:
730
docs/TRILLIUM_API.md
Normal file
730
docs/TRILLIUM_API.md
Normal file
@@ -0,0 +1,730 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user