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

19 KiB

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:

GET /etapi/app-info
Authorization: <ETAPI_TOKEN>

OR (since v0.93.0):

GET /etapi/app-info
Authorization: Bearer <ETAPI_TOKEN>

2. Basic Authentication (since v0.56)

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

POST /etapi/auth/login
Content-Type: application/json

{
  "password": "your_trilium_password"
}

Response:

{
  "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:
    {
      "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:
    {
      "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:
    {
      "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:
    {
      "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:
    {
      "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:
    {
      "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:
    {
      "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:
    {
      "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:
    {
      "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:
    {
      "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
    {
      "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:
    {
      "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

{
  "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:

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

# 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

# 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

# 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


License: Apache 2.0
Maintainer: TriliumNext Community
Contact: https://github.com/TriliumNext/Trilium/discussions