Skip to main content

Tags, Categories, Queries, MRQL & More

This page covers Tags, Categories, Resource Categories, Queries, MRQL, Search, Logs, and Download Queue endpoints.


Tags API

Tags are labels that can be applied to resources, notes, and groups for organization.

List Tags

GET /v1/tags

Query Parameters

ParameterTypeDescription
pageintegerPage number (default: 1)
NamestringFilter by name (partial match)
DescriptionstringFilter by description
CreatedBeforestringFilter by creation date
CreatedAfterstringFilter by creation date
SortBystring[]Sort order

Example

curl http://localhost:8181/v1/tags

# Search for tags
curl "http://localhost:8181/v1/tags?Name=project"

Response

[
{
"ID": 1,
"Name": "important",
"Description": "High priority items",
"CreatedAt": "2024-01-01T00:00:00Z"
}
]

Create or Update Tag

POST /v1/tag

Parameters

ParameterTypeDescription
IDintegerTag ID (include to update)
NamestringTag name
DescriptionstringDescription

Example

# Create
curl -X POST http://localhost:8181/v1/tag \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"Name": "urgent", "Description": "Requires immediate attention"}'

# Update
curl -X POST http://localhost:8181/v1/tag \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"ID": 1, "Name": "critical", "Description": "Updated description"}'

Delete Tag

POST /v1/tag/delete?Id={id}

Bulk Delete Tags

Delete multiple tags at once.

POST /v1/tags/delete

Parameters

ParameterTypeDescription
IDinteger[]Tag IDs to delete

Merge Tags

Merge multiple tags into one, transferring all associations.

POST /v1/tags/merge

Parameters

ParameterTypeDescription
WinnerintegerTag ID to keep
Losersinteger[]Tag IDs to merge and delete

Inline Editing

POST /v1/tag/editName?id={id}
POST /v1/tag/editDescription?id={id}

Timeline

GET /v1/tags/timeline

See Timeline Endpoints below for details.


Categories API

Categories define types for groups with optional display customizations.

List Categories

GET /v1/categories

Query Parameters

ParameterTypeDescription
pageintegerPage number (default: 1)
NamestringFilter by name
DescriptionstringFilter by description

Example

curl http://localhost:8181/v1/categories

Response

[
{
"ID": 1,
"Name": "Person",
"Description": "Individual people",
"CustomHeader": "...",
"CustomSidebar": "...",
"CustomSummary": "...",
"CustomAvatar": "...",
"MetaSchema": "..."
}
]

Create or Update Category

POST /v1/category

Parameters

ParameterTypeDescription
IDintegerCategory ID (include to update)
NamestringCategory name
DescriptionstringDescription
CustomHeaderstringCustom header template
CustomSidebarstringCustom sidebar template
CustomSummarystringCustom summary template
CustomAvatarstringCustom avatar template
MetaSchemastringJSON schema for metadata validation

Example

curl -X POST http://localhost:8181/v1/category \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"Name": "Company",
"Description": "Business organizations"
}'

Delete Category

POST /v1/category/delete?Id={id}

Inline Editing

POST /v1/category/editName?id={id}
POST /v1/category/editDescription?id={id}

Resource Categories API

Resource categories define types for resources with optional display customizations and metadata schemas.

List Resource Categories

GET /v1/resourceCategories

Query Parameters

ParameterTypeDescription
pageintegerPage number (default: 1)
NamestringFilter by name
DescriptionstringFilter by description

Example

curl http://localhost:8181/v1/resourceCategories

Response

[
{
"ID": 1,
"Name": "Photo",
"Description": "Photograph files",
"CustomHeader": "...",
"CustomSidebar": "...",
"CustomSummary": "...",
"CustomAvatar": "...",
"MetaSchema": "..."
}
]

Create or Update Resource Category

POST /v1/resourceCategory

Parameters

ParameterTypeDescription
IDintegerResource category ID (include to update)
NamestringResource category name
DescriptionstringDescription
CustomHeaderstringCustom header template
CustomSidebarstringCustom sidebar template
CustomSummarystringCustom summary template
CustomAvatarstringCustom avatar template
MetaSchemastringJSON schema for metadata validation

Example

curl -X POST http://localhost:8181/v1/resourceCategory \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"Name": "Photo",
"Description": "Photograph files"
}'

Delete Resource Category

POST /v1/resourceCategory/delete?Id={id}

Inline Editing

POST /v1/resourceCategory/editName?id={id}
POST /v1/resourceCategory/editDescription?id={id}

Queries API

Queries are saved SQL queries that can be executed to generate custom reports. For database-level write protection, configure DB_READONLY_DSN as a truly read-only connection; otherwise query execution is not database-enforced read-only.

List Queries

GET /v1/queries

Query Parameters

ParameterTypeDescription
pageintegerPage number (default: 1)
NamestringFilter by name
TextstringFilter by query text

Example

curl http://localhost:8181/v1/queries

Get Single Query

GET /v1/query?id={id}

Create or Update Query

POST /v1/query

Parameters

ParameterTypeDescription
IDintegerQuery ID (include to update)
NamestringQuery name
TextstringSQL query text
TemplatestringDisplay template

Example

curl -X POST http://localhost:8181/v1/query \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"Name": "Recent Resources",
"Text": "SELECT * FROM resources ORDER BY created_at DESC LIMIT 10"
}'

Delete Query

POST /v1/query/delete?Id={id}

Run Query

Execute a saved query and get results.

POST /v1/query/run

Query Parameters

ParameterTypeDescription
idintegerQuery ID to run
namestringQuery name to run (alternative to id)

Example

# Run by ID
curl -X POST "http://localhost:8181/v1/query/run?id=1" \
-H "Accept: application/json"

# Run by name
curl -X POST "http://localhost:8181/v1/query/run?name=Recent%20Resources" \
-H "Accept: application/json"

Get Database Schema

Return the database table and column schema. Useful for writing saved queries.

GET /v1/query/schema

Example

curl http://localhost:8181/v1/query/schema

The response is cached for 5 minutes.

Inline Editing

POST /v1/query/editName?id={id}
POST /v1/query/editDescription?id={id}

Global Search API

Search across entity types: resources, notes, groups, tags, categories, queries, relation types, note types, and resource categories.

GET /v1/search

Query Parameters

ParameterTypeDescription
qstringRequired. Search query
limitintegerMaximum results (default: 20, max: 200)
typesstringEntity types to search (comma-separated: resource, note, group, tag, category, query, relationType, noteType, resourceCategory)

Example

# Search everything
curl "http://localhost:8181/v1/search?q=project"

# Search with limit
curl "http://localhost:8181/v1/search?q=project&limit=100"

# Search specific types
curl "http://localhost:8181/v1/search?q=project&types=resource,note"

Response

{
"query": "project",
"total": 15,
"results": [
{
"id": 1,
"type": "group",
"name": "Project Alpha",
"description": "Main project group",
"score": 100,
"url": "/group?id=1",
"extra": {"category": "Project"}
},
{
"id": 5,
"type": "resource",
"name": "project-plan.pdf",
"description": "Project planning document",
"score": 85,
"url": "/resource?id=5"
}
]
}

Logs API

Query the audit log of system events and entity changes.

List Log Entries

GET /v1/logs

Query Parameters

ParameterTypeDescription
pageintegerPage number (default: 1)
LevelstringFilter by level (info, warning, error)
ActionstringFilter by action (create, update, delete, system)
EntityTypestringFilter by entity type
EntityIDintegerFilter by entity ID
MessagestringSearch in message (partial match)
RequestPathstringSearch in request path
CreatedBeforestringFilter by date
CreatedAfterstringFilter by date
SortBystring[]Sort order

Example

# List recent logs
curl http://localhost:8181/v1/logs

# Filter by action
curl "http://localhost:8181/v1/logs?Action=create"

# Filter by entity type
curl "http://localhost:8181/v1/logs?EntityType=resource"

# Filter errors only
curl "http://localhost:8181/v1/logs?Level=error"

Response

{
"logs": [
{
"id": 100,
"createdAt": "2024-01-15T10:30:00Z",
"level": "info",
"action": "create",
"entityType": "resource",
"entityId": 456,
"entityName": "photo.jpg",
"message": "Resource created: photo.jpg",
"requestPath": "/v1/resource"
}
],
"totalCount": 1500,
"page": 1,
"perPage": 50
}

Get Single Log Entry

GET /v1/log?id={id}

Get Entity History

Get all log entries for a specific entity.

GET /v1/logs/entity

Query Parameters

ParameterTypeDescription
entityTypestringRequired. Entity type (tag, note, resource, group)
entityIdintegerRequired. Entity ID
pageintegerPage number (default: 1)

Example

# Get history for a specific resource
curl "http://localhost:8181/v1/logs/entity?entityType=resource&entityId=123"

Download Queue API

Queue background downloads for remote resources. The queue holds up to 100 jobs and runs up to 3 concurrently. Completed jobs are retained for 1 hour before eviction.

The canonical route family is /v1/jobs/*. Legacy /v1/download/* aliases remain available for compatibility.

Submit Download

Add URLs to the download queue.

POST /v1/jobs/download/submit

Parameters

Same as POST /v1/resource/remote, but always queues for background download.

Submit multiple URLs by separating them with newlines in the URL field. Each URL becomes a separate job.

Example

curl -X POST http://localhost:8181/v1/jobs/download/submit \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"URL": "https://example.com/file1.zip\nhttps://example.com/file2.zip",
"OwnerId": 5
}'

Legacy alias: POST /v1/download/submit

Get Download Queue

Get all download jobs.

GET /v1/jobs/queue

Example

curl http://localhost:8181/v1/jobs/queue

Legacy alias: GET /v1/download/queue

Response

{
"jobs": [
{
"id": "job-123",
"url": "https://example.com/file.zip",
"status": "downloading",
"progress": 45,
"totalSize": 1048576,
"progressPercent": 4.29,
"createdAt": "2024-01-15T10:00:00Z",
"source": "download"
}
]
}

Job Operations

EndpointDescription
POST /v1/jobs/cancel?id={job_id}Cancel a pending or in-progress download
POST /v1/jobs/pause?id={job_id}Pause a download job
POST /v1/jobs/resume?id={job_id}Resume a paused download (restarts from the beginning)
POST /v1/jobs/retry?id={job_id}Retry a failed or cancelled download

Downloads can fail due to network errors, connection timeouts (default 30s), idle timeouts (default 60s), or exceeding the overall timeout (default 30m). Configure these with the -remote-connect-timeout, -remote-idle-timeout, and -remote-overall-timeout flags.

Legacy aliases remain available under /v1/download/cancel, /v1/download/pause, /v1/download/resume, and /v1/download/retry.

Download Events (SSE)

Stream real-time download status updates via Server-Sent Events.

GET /v1/jobs/events

Example

The server emits named events, so you must use addEventListener (not onmessage):

EventDescription
initFull initial state with all current jobs ({ jobs: [...], actionJobs: [...] })
addedA new download job was queued
updatedA download job changed status or progress
removedA download job was removed from the queue
action_addedA plugin action job was created
action_updatedA plugin action job changed status
action_removedA plugin action job was removed
const eventSource = new EventSource('http://localhost:8181/v1/jobs/events');

// Receive full initial state (all current jobs)
eventSource.addEventListener('init', (event) => {
const { jobs, actionJobs } = JSON.parse(event.data);
console.log('Initial download jobs:', jobs);
console.log('Initial action jobs:', actionJobs);
});

// Download job updates
eventSource.addEventListener('added', (event) => {
const { type, job } = JSON.parse(event.data);
console.log('New download job:', job);
});

eventSource.addEventListener('updated', (event) => {
const { type, job } = JSON.parse(event.data);
console.log('Download job updated:', job.id, job.status, job.progressPercent + '%');
});

eventSource.addEventListener('removed', (event) => {
const { type, job } = JSON.parse(event.data);
console.log('Download job removed:', job.id);
});

// Plugin action job updates (prefixed with "action_")
eventSource.addEventListener('action_updated', (event) => {
const { job } = JSON.parse(event.data);
console.log('Action job updated:', job);
});

Legacy alias: GET /v1/download/events


Series API

A series groups related resources into an ordered sequence (e.g., pages of a scanned document, frames of an animation).

List Series

GET /v1/seriesList

Query Parameters

ParameterTypeDescription
pageintegerPage number for pagination
NamestringFilter by name (partial match)
SlugstringFilter by slug
CreatedBeforestringFilter by creation date (ISO 8601)
CreatedAfterstringFilter by creation date (ISO 8601)
SortBystring[]Sort order

Get Single Series

GET /v1/series?id={id}

Create Series

POST /v1/series/create

Parameters

ParameterTypeDescription
NamestringRequired. Series name

Update Series

POST /v1/series

Parameters

ParameterTypeDescription
IDintegerRequired. Series ID
NamestringNew name
MetastringJSON metadata

Delete Series

POST /v1/series/delete?Id={id}

Inline Editing

POST /v1/series/editName?id={id}

Remove Resource from Series

POST /v1/resource/removeSeries?id={resourceId}

Removes a resource from its series without deleting the series itself.


Meta Keys Endpoints

Get all unique metadata keys used across entities.

EntityEndpoint
ResourcesGET /v1/resources/meta/keys
NotesGET /v1/notes/meta/keys
GroupsGET /v1/groups/meta/keys

Each returns an array of strings representing all metadata keys in use:

["author", "source", "date_created", "location"]

MRQL API

Execute, validate, and manage MRQL queries. See the MRQL feature page for the full query language reference.

Execute Query

POST /v1/mrql

Request Body

FieldTypeDescription
querystringMRQL query expression (required)
limitintegerMax results (flat) or items per bucket (grouped)
pageintegerPage number
bucketsintegerBuckets per page (grouped mode only)
offsetintegerDirect offset for cursor-based bucket paging

Query Parameters

ParameterDescription
render=1Process CustomMRQLResult templates server-side and populate renderedHTML on each entity

Example

curl -X POST http://localhost:8181/v1/mrql \
-H "Content-Type: application/json" \
-d '{"query": "type = resource AND tags = \"photo\" ORDER BY created DESC", "limit": 20}'

Returns an MRQLResult for flat queries (with resources, notes, groups arrays) or an MRQLGroupedResult for GROUP BY queries (with mode, buckets, and rows fields).

Validate Query

POST /v1/mrql/validate

Request Body

FieldTypeDescription
querystringMRQL query expression

Response

{
"valid": true,
"errors": []
}

When invalid, errors contains objects with position and message details.

Autocomplete

POST /v1/mrql/complete

Request Body

FieldTypeDescription
querystringPartial MRQL query
cursorintegerCursor position in the query string

Returns context-aware completion suggestions for the cursor position.

List Saved Queries

GET /v1/mrql/saved
GET /v1/mrql/saved?id=N

Without id, returns a paginated list of all saved MRQL queries. With id, returns a single saved query.

ParameterDescription
idSaved query ID (returns single query)
all=1Return all saved queries without pagination
pagePage number for paginated listing

Create Saved Query

POST /v1/mrql/saved
FieldTypeRequiredDescription
namestringYesQuery name
querystringYesMRQL query expression
descriptionstringNoQuery description

Returns 201 with the created saved query object.

Update Saved Query

PUT /v1/mrql/saved?id=N

Same body fields as create. Returns the updated saved query.

Delete Saved Query

POST /v1/mrql/saved/delete?id=N

Returns {"id": N} on success.

Run Saved Query

POST /v1/mrql/saved/run
ParameterDescription
idSaved query ID
nameSaved query name (fallback if id not found)
limitMax results
pagePage number
bucketsBuckets per page (grouped mode)
offsetDirect offset
render=1Process CustomMRQLResult templates server-side

Looks up the saved query by ID first, then by name. Revalidates the saved query before execution (schema changes may have invalidated it since save time). Returns the same response format as the execute endpoint.


Admin Stats API

Server and data statistics for monitoring.

Server Stats

GET /v1/admin/server-stats

Returns server runtime information (memory usage, goroutines, uptime, etc.).

Data Stats

GET /v1/admin/data-stats

Returns entity counts and storage statistics.

Expensive Data Stats

GET /v1/admin/data-stats/expensive

Returns statistics that require heavier queries (e.g., orphan counts, duplicate detection). Separated from the main stats endpoint to avoid blocking.


Timeline Endpoints

Each major entity type has a timeline endpoint that returns entities grouped by time period, suitable for timeline/calendar views.

EntityEndpoint
ResourcesGET /v1/resources/timeline
NotesGET /v1/notes/timeline
GroupsGET /v1/groups/timeline
TagsGET /v1/tags/timeline
CategoriesGET /v1/categories/timeline
QueriesGET /v1/queries/timeline

Each accepts the same query parameters as the corresponding list endpoint, plus timeline-specific parameters for date range and granularity.