API Overview
All features are accessible via a REST API. Most write endpoints accept either JSON or form-encoded requests, and most /v1 endpoints return JSON.
Out of the box the API has no authentication or authorization, so it is designed for private, trusted networks only. Never expose the server directly to the public internet. Opt-in Authentication & RBAC (-auth) adds per-user accounts, roles, and API tokens (Authorization: Bearer <token>); even then, front the server with a reverse proxy for internet-facing deployments.
Base Path
All API endpoints are prefixed with /v1:
http://localhost:8181/v1/resources
http://localhost:8181/v1/notes
http://localhost:8181/v1/groups
Response Formats
Most API endpoints under /v1/ return JSON, with a few important exceptions:
GET /v1/resource/viewreturns a302redirect to the stored fileGET /v1/resource/previewreturns image bytes, or a307redirect to/public/placeholders/file.jpgwhen no thumbnail can be producedGET /v1/resource/version/filereturns the version's fileGET|POST /v1/mrql/exportreturns a CSV or JSON attachmentGET /v1/exports/{jobId}/downloadreturns a tar archiveGET /v1/download/eventsandGET /v1/jobs/eventsreturn Server-Sent Events streamsGET /v1/plugins/{pluginName}/block/renderreturns an HTML fragmentPOST /v1/plugins/{pluginName}/display/renderreturns an HTML fragment
Template routes (without the /v1/ prefix) return HTML by default and support two suffixes:
| Suffix | Effect |
|---|---|
.json | Returns JSON instead of HTML |
.body | Returns the HTML body without the layout wrapper |
A template route also returns JSON when the request carries Accept: application/json, so the .json suffix is a convenience rather than the only mechanism.
# API route: always JSON
curl http://localhost:8181/v1/resources
# Template route: HTML by default
curl http://localhost:8181/resources
# Template route: JSON via suffix
curl http://localhost:8181/resources.json
# Template route: JSON via the Accept header
curl -H "Accept: application/json" http://localhost:8181/resources
# Template route: body only (useful for HTMX-style updates)
curl http://localhost:8181/resources.body
The .json and .body suffixes do not work on /v1/ API routes. Use /v1/ paths directly for JSON responses.
Redirects on Write Endpoints
/v1 write endpoints answer 303 See Other when the request's Accept header includes text/html. A ?redirect=<relative path> query parameter overrides the destination and applies to every client regardless of Accept; only same-origin relative paths are accepted. Send Accept: application/json and omit redirect to get the JSON body.
Request Content Types
Many write endpoints accept requests in two formats:
- JSON:
Content-Type: application/json - Form data:
Content-Type: application/x-www-form-urlencodedormultipart/form-data
# JSON request
curl -X POST http://localhost:8181/v1/tag \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"Name": "example-tag"}'
# Form request
curl -X POST http://localhost:8181/v1/tag \
-H "Accept: application/json" \
-d "Name=example-tag"
Pagination
List endpoints support pagination using the page query parameter:
# Get page 1 (default)
curl http://localhost:8181/v1/resources
# Get page 2
curl http://localhost:8181/v1/resources?page=2
# Get page 3
curl http://localhost:8181/v1/resources?page=3
The default page size is 50 items. The MaxResults parameter on Resource queries can override the page size. List responses carry the X-Page and X-Per-Page headers.
Common Query Parameters
Most list endpoints support these common filtering parameters:
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number for pagination (default: 1) |
Name | string | Filter by name (partial match) |
Description | string | Filter by description (partial match) |
CreatedBefore | string | Filter items created before this date (ISO 8601) |
CreatedAfter | string | Filter items created after this date (ISO 8601) |
SortBy | string[] | Sort order (e.g., SortBy=name, SortBy=created_at desc) |
Sorting
Use the SortBy parameter to control result ordering. Append asc or desc (space-separated) for direction. Default is ascending.
# Sort by name ascending
curl "http://localhost:8181/v1/resources?SortBy=name"
# Sort by creation date descending
curl -G http://localhost:8181/v1/resources \
--data-urlencode "SortBy=created_at desc"
# Multiple sort fields
curl -G http://localhost:8181/v1/resources \
--data-urlencode "SortBy=name" \
--data-urlencode "SortBy=created_at desc"
# Sort by metadata field
curl -G http://localhost:8181/v1/resources \
--data-urlencode "SortBy=meta->>'priority' desc"
Sort columns are validated against: ^(meta->>?'[a-z_]+'|[a-z_]+)(\s(desc|asc))?$
Error Responses
When an error occurs, the API returns an appropriate HTTP status code with an error message:
{
"error": "resource not found"
}
Common HTTP status codes:
| Status | Description |
|---|---|
| 200 | Success |
| 201 | Created (block creation) |
| 202 | Accepted (async plugin actions, download submissions) |
| 204 | No Content (block deletion, reorder, rebalance) |
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Missing or invalid credential under -auth |
| 403 | Forbidden - The acting role lacks the capability, for example relation and taxonomy writes |
| 404 | Not Found - Entity does not exist |
| 409 | Conflict - Duplicate file on a multipart POST /v1/resource upload (JSON body includes details[].existingResourceId). A duplicate URL download via POST /v1/resource/remote returns 400, and POST /v1/resource/local is idempotent (200 with the existing resource). Also returned when deleting a group that is assigned as a user's scope group, and when deleting a resource's current or last version |
| 415 | Unsupported Media Type - The resource's stored bytes are not a decodable image |
| 500 | Internal Server Error |
| 503 | Service Unavailable - ffmpeg is not configured, so video thumbnails and trimming are unavailable |
ID Parameters
For endpoints that operate on a single entity, pass the ID as a query parameter:
# Get a specific resource
curl http://localhost:8181/v1/resource?id=123
# Delete a specific tag
curl -X POST http://localhost:8181/v1/tag/delete?Id=456
Some endpoints use id (lowercase) and others use Id (capitalized). This is a known inconsistency in the API. Check the specific endpoint documentation for the correct casing.
OpenAPI Specification
An OpenAPI 3.0 specification can be generated from the routes registered with the OpenAPI generator:
# Generate YAML spec (default)
go run ./cmd/openapi-gen
# Generate with custom output path
go run ./cmd/openapi-gen -output api-spec.yaml
# Generate JSON format
go run ./cmd/openapi-gen -output api-spec.json -format json
Validate a Spec
Validate a generated OpenAPI spec against the OpenAPI 3.0 schema:
go run ./cmd/openapi-gen/validate.go openapi.yaml
If validation succeeds, the command prints "Valid OpenAPI 3.0 spec" and exits with code 0. On failure, it prints the validation error and exits with code 1.
The generated spec works with Swagger UI, Postman, or code generators.
The generated spec currently focuses on the core documented API surface. Some newer aliases or convenience endpoints may exist in the server before they are added to the generated spec.
API Endpoint Categories
The API is organized into these categories:
- Resources - File management (upload, download, metadata)
- Notes - Text content and note types
- Groups - Hierarchical organization and relations
- Plugins - Plugin management, actions, and job monitoring
- Tags, Categories, Queries & More - Tags, Categories, Queries, MRQL, Series, Search, Logs, Download Queue, Admin Stats, Timeline