Grimoire API Documentation
August 3, 2026 ยท View on GitHub
Auto-generated from
daemon/src/api/contract.ts. Do not edit by hand.
Contract
- Base URL:
http://127.0.0.1:3210 - Machine-readable contract:
docs/api-contract.json - OpenAPI output:
docs/openapi.json - Regenerate:
npm run docs:api - Drift check:
npm run docs:api:check - OpenAPI-only commands:
npm run docs:openapiandnpm run docs:openapi:check
Authentication
The daemon is intended to bind to localhost. The first-party loopback browser app uses the local origin boundary and does not need to send an API token.
Local integration surfaces such as /mcp and /capture require a managed bearer token. Create one with POST /integration-tokens, store the returned token immediately, and send it as Authorization: Bearer <token>. Regular REST routes remain tokenless for the first-party app, but if a client presents an Authorization header it must be a valid integration token. List responses only include redacted token prefixes. Missing required tokens, invalid tokens, rotated tokens, or revoked tokens return 401 with application/problem+json and a WWW-Authenticate bearer challenge.
Browser Origin And CORS
Grimoire keeps browser access loopback-only. The daemon trusts the first-party app from http://127.0.0.1:3210, http://localhost:3210, and configured loopback development origins. Requests without an Origin header are treated as non-browser local client traffic and do not receive CORS headers.
Configure additional local browser clients with CORS_ORIGINS as a comma-separated list of loopback origins:
CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173,http://localhost:4321 littleimpd
Only full http or https origins are accepted, including scheme, host, and any port used by the client. Non-loopback origins are ignored even when configured, unsafe browser writes from rejected origins return 403, and rejected preflight requests return 403 without reflecting Access-Control-Allow-Origin. Protected local capture clients must use loopback origins and a managed integration bearer token.
Response Conventions
- Most JSON endpoints return
{ "data": ... }envelopes. - Paginated endpoints include a
paginationobject withtotal,limit,offset, andhas_more. - Newer route validation errors use
application/problem+json; some backup/export routes still return{ "error": string }.
OpenAPI Output
docs/openapi.json is generated from the same daemon-owned contract for local client tooling that expects an OpenAPI 3.0 document.
Limitations:
- Hono
ALLroutes are represented by the primary client methodPOSTwithx-grimoire-source-method: "ALL". - Mixed content responses such as JSON/CSV exports share the closest generated schema; CSV, media, and SSE clients should still use the documented content type.
- First-party REST routes remain local-origin routes. The OpenAPI bearer scheme documents managed local integration tokens, and MCP marks that scheme as required.
Endpoints
System
GET /health
Return daemon health, version, uptime, and queue size.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | HealthResponse | Daemon health |
GET /diagnostics
Return a redacted local diagnostics bundle for support.
Diagnostics are generated locally and omit API keys, URL credentials, query strings, PIN hashes, S3 credentials, and backup passwords.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | DiagnosticsResponse | Redacted diagnostics |
Examples:
Generate diagnostics
Request:
curl http://127.0.0.1:3210/diagnostics
Updates
GET /updates/check
Check a GitHub Releases-compatible source for a newer Grimoire release.
Query parameters:
| Field | Type | Required | Description |
|---|---|---|---|
channel | "stable" | "beta" | no | Update channel to check; defaults from the current package version |
source | string | no | Public GitHub Releases-compatible JSON endpoint; private and loopback hosts are rejected |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | UpdateCheckResponse | Update check result |
422 | application/problem+json | ProblemDetails | Invalid channel or source URL |
502 | application/problem+json | ProblemDetails | Update source could not be read or returned an invalid response |
Examples:
Check for updates
Request:
curl 'http://127.0.0.1:3210/updates/check?channel=stable'
Bookmarks
POST /bookmarks
Save a URL and enqueue the ingestion pipeline.
Request body:
- Content type:
application/json - Schema:
BookmarkCreateRequest
| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | HTTP or HTTPS URL to save |
title | string | no | Optional title override |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BookmarkResponse | Existing active bookmark returned idempotently |
201 | application/json | BookmarkResponse | Bookmark created |
400 | application/problem+json | ProblemDetails | Malformed JSON |
409 | application/problem+json | ProblemDetails | URL already exists in trash or archive |
422 | application/problem+json | ProblemDetails | Invalid URL or missing url field |
Examples:
Save a bookmark
Request:
curl -X POST http://127.0.0.1:3210/bookmarks \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/rag-vector-search","title":"RAG Vector Search Notes"}'
Response:
HTTP/1.1 201 Created
Content-Type: application/json
{
"data": {
"id": "bm_123",
"url": "https://example.com/rag-vector-search",
"domain": "example.com",
"title": "RAG Vector Search Notes",
"description": null,
"status": "saved",
"category_id": null,
"favicon_url": null,
"screenshot_url": null,
"is_pinned": 0,
"is_archived": 0,
"is_trashed": 0,
"trashed_at": null,
"read_later": 0,
"read_at": null,
"opened_count": 0,
"last_opened_at": null,
"notes": null,
"created_at": "2026-06-01T09:30:00.000Z",
"updated_at": "2026-06-01T09:30:00.000Z",
"tags": []
}
}
Reject an invalid bookmark URL
Request:
curl -X POST http://127.0.0.1:3210/bookmarks \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1/private"}'
Response:
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json
{
"type": "https://littleimp.app/problems/unprocessable-entity",
"title": "Unprocessable Entity",
"status": 422,
"detail": "Invalid URL - must be http or https"
}
GET /bookmarks
List active or archived bookmarks with filters and pagination.
Query parameters:
| Field | Type | Required | Description |
|---|---|---|---|
tag | string | no | Filter by tag name |
domain | string | no | Filter by exact domain |
category_id | string | no | Filter by exact category ID; takes precedence over category |
category | string | no | Filter by category name |
date_from | string | no | Inclusive ISO date or date-time lower bound |
date_to | string | no | Inclusive ISO date or date-time upper bound |
read_later | "true" | "false" | "1" | "0" | no | Filter by read-later state; accepts boolean strings or numeric flags |
read_state | "read" | "unread" | no | Filter by read state |
is_pinned | "true" | "false" | "1" | "0" | no | Filter by pinned/starred state; accepts boolean strings or numeric flags |
opened_count_min | integer | no | Filter to bookmarks opened at least this many times |
opened_count_max | integer | no | Filter to bookmarks opened no more than this many times |
last_opened_from | string | no | Inclusive ISO date or date-time lower bound for last opened time |
last_opened_to | string | no | Inclusive ISO date or date-time upper bound for last opened time |
sort | "created_at" | "updated_at" | "title" | "domain" | "opened_count" | "last_opened_at" | no | Sort key applied before pagination |
direction | "asc" | "desc" | no | Sort direction; requires sort and defaults to desc when omitted |
limit | integer | no | Maximum number of results to return |
offset | integer | no | Number of results to skip |
archived | "true" | "false" | no | When true, return archived bookmarks |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BookmarkListResponse | Bookmark page |
Examples:
List filtered bookmarks
Request:
curl "http://127.0.0.1:3210/bookmarks?tag=rag&read_state=unread&is_pinned=true&opened_count_min=1&sort=opened_count&direction=desc&limit=10&offset=0"
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{
"id": "bm_123",
"url": "https://example.com/rag-vector-search",
"domain": "example.com",
"title": "RAG Vector Search Notes",
"description": "Practical notes about vector search for retrieval augmented generation.",
"status": "indexed",
"category_id": "cat_ai",
"favicon_url": "/media/bookmarks/bm_123/favicon",
"screenshot_url": "/media/bookmarks/bm_123/screenshot",
"is_pinned": 0,
"is_archived": 0,
"is_trashed": 0,
"trashed_at": null,
"read_later": 1,
"read_at": null,
"opened_count": 2,
"last_opened_at": "2026-06-01T08:45:00.000Z",
"notes": "Compare chunking guidance with local notes.",
"created_at": "2026-06-01T09:30:00.000Z",
"updated_at": "2026-06-01T09:30:00.000Z",
"tags": [
"rag",
"search"
]
}
],
"pagination": {
"total": 1,
"limit": 10,
"offset": 0,
"has_more": false
}
}
GET /bookmarks/aggregates
Return page-independent active-library aggregate counts.
Counts categories, tags, domains, read state, pinned/starred state, and read-later state for active bookmarks under the same approved library filter context as bookmark listing. Pagination and sorting are intentionally ignored.
Query parameters:
| Field | Type | Required | Description |
|---|---|---|---|
tag | string | no | Filter by tag name |
domain | string | no | Filter by exact domain |
category_id | string | no | Filter by exact category ID; takes precedence over category |
category | string | no | Filter by category name |
date_from | string | no | Inclusive ISO date or date-time lower bound |
date_to | string | no | Inclusive ISO date or date-time upper bound |
read_later | "true" | "false" | "1" | "0" | no | Filter by read-later state; accepts boolean strings or numeric flags |
read_state | "read" | "unread" | no | Filter by read state |
is_pinned | "true" | "false" | "1" | "0" | no | Filter by pinned/starred state; accepts boolean strings or numeric flags |
opened_count_min | integer | no | Filter to bookmarks opened at least this many times |
opened_count_max | integer | no | Filter to bookmarks opened no more than this many times |
last_opened_from | string | no | Inclusive ISO date or date-time lower bound for last opened time |
last_opened_to | string | no | Inclusive ISO date or date-time upper bound for last opened time |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BookmarkAggregatesResponse | Bookmark aggregate counts |
422 | application/problem+json | ProblemDetails | Invalid aggregate filter |
Examples:
Read library aggregate counts
Request:
curl "http://127.0.0.1:3210/bookmarks/aggregates?read_later=true&is_pinned=false"
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"total": 12,
"categories": [
{
"id": "cat_ai",
"name": "AI Research",
"count": 7
},
{
"id": "cat_docs",
"name": "Documentation",
"count": 5
}
],
"tags": [
{
"name": "rag",
"count": 6
},
{
"name": "typescript",
"count": 4
}
],
"domains": [
{
"domain": "example.com",
"count": 8
},
{
"domain": "docs.example.com",
"count": 4
}
],
"read": {
"read": 3,
"unread": 9
},
"pinned": {
"pinned": 2,
"unpinned": 10
},
"read_later": {
"yes": 5,
"no": 7
}
}
}
GET /bookmarks/:id
Get one bookmark with extracted content.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BookmarkDetailResponse | Bookmark detail |
404 | application/problem+json | ProblemDetails | Bookmark not found |
Examples:
Read bookmark detail
Request:
curl http://127.0.0.1:3210/bookmarks/bm_123
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"id": "bm_123",
"url": "https://example.com/rag-vector-search",
"domain": "example.com",
"title": "RAG Vector Search Notes",
"description": "Practical notes about vector search for retrieval augmented generation.",
"status": "indexed",
"category_id": "cat_ai",
"favicon_url": "/media/bookmarks/bm_123/favicon",
"screenshot_url": "/media/bookmarks/bm_123/screenshot",
"is_pinned": 0,
"is_archived": 0,
"is_trashed": 0,
"trashed_at": null,
"read_later": 1,
"read_at": null,
"opened_count": 2,
"last_opened_at": "2026-06-01T08:45:00.000Z",
"notes": "Compare chunking guidance with local notes.",
"created_at": "2026-06-01T09:30:00.000Z",
"updated_at": "2026-06-01T09:30:00.000Z",
"tags": [
"rag",
"search"
],
"content": {
"bookmark_id": "bm_123",
"raw_html": null,
"markdown": "## RAG Vector Search\n\nUse hybrid retrieval when exact terms matter.",
"summary": "A practical walkthrough of hybrid retrieval for RAG systems.",
"author": "Example Author",
"published_at": "2026-05-28T12:00:00.000Z",
"word_count": 1240,
"language": "en",
"extracted_at": "2026-06-01T09:30:00.000Z"
},
"media": {
"favicon": null,
"screenshot": null,
"images": []
}
}
}
GET /media/bookmarks/:bookmarkId/:mediaId
Serve one cached local bookmark media file.
Media files are served from the local cache only. Missing files, trashed bookmarks, and unknown media IDs return 404.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
bookmarkId | string | yes | Bookmark ID |
mediaId | string | yes | Media cache record ID |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | image/* | - | Cached non-SVG image media file |
404 | application/problem+json | ProblemDetails | Media not found |
PUT /bookmarks/:id
Patch bookmark fields, tags, archive state, read state, and notes.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Request body:
- Content type:
application/json - Schema:
BookmarkUpdateRequest
| Field | Type | Required | Description |
|---|---|---|---|
title | string | null | no | New title, or null to clear |
category_id | string | null | no | Category ID, or null to clear |
tags | array | no | Replacement tag names |
is_pinned | integer | no | Pinned flag, 0 or 1; maps Grimoire starred/favorite state |
read_later | integer | no | Read-later flag, 0 or 1 |
is_archived | integer | no | Archived flag, 0 or 1 |
read_at | string | null | no | ISO 8601 date-time, or null to mark unread |
notes | string | null | no | Personal notes, or null to clear |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BookmarkResponse | Updated bookmark |
400 | application/problem+json | ProblemDetails | Malformed JSON |
404 | application/problem+json | ProblemDetails | Bookmark not found |
422 | application/problem+json | ProblemDetails | Invalid patch field |
Examples:
Update bookmark metadata
Request:
curl -X PUT http://127.0.0.1:3210/bookmarks/bm_123 \
-H "Content-Type: application/json" \
-d '{"tags":["rag","retrieval"],"read_later":1,"notes":"Compare with local chunking notes."}'
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"id": "bm_123",
"url": "https://example.com/rag-vector-search",
"domain": "example.com",
"title": "RAG Vector Search Notes",
"description": "Practical notes about vector search for retrieval augmented generation.",
"status": "indexed",
"category_id": "cat_ai",
"favicon_url": "/media/bookmarks/bm_123/favicon",
"screenshot_url": "/media/bookmarks/bm_123/screenshot",
"is_pinned": 0,
"is_archived": 0,
"is_trashed": 0,
"trashed_at": null,
"read_later": 1,
"read_at": null,
"opened_count": 2,
"last_opened_at": "2026-06-01T08:45:00.000Z",
"notes": "Compare with local chunking notes.",
"created_at": "2026-06-01T09:30:00.000Z",
"updated_at": "2026-06-01T09:30:00.000Z",
"tags": [
"rag",
"retrieval"
]
}
}
POST /bookmarks/:id/open
Record a user-triggered external open for a bookmark.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BookmarkResponse | Updated bookmark open metrics |
404 | application/problem+json | ProblemDetails | Bookmark not found |
DELETE /bookmarks/:id
Soft-delete a bookmark by moving it to trash.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
204 | - | - | Bookmark moved to trash |
404 | application/problem+json | ProblemDetails | Bookmark not found |
POST /bookmarks/:id/restore
Restore a trashed bookmark.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BookmarkResponse | Restored bookmark |
404 | application/problem+json | ProblemDetails | Bookmark not found or not in trash |
500 | application/problem+json | ProblemDetails | Restore succeeded but bookmark could not be fetched |
DELETE /bookmarks/:id/permanent
Permanently delete a trashed bookmark.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
204 | - | - | Bookmark permanently deleted |
404 | application/problem+json | ProblemDetails | Bookmark not found or not in trash |
GET /trash
List trashed bookmarks.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BookmarkArrayResponse | Trashed bookmarks |
GET /bookmarks/:id/related
List semantically related bookmarks.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Query parameters:
| Field | Type | Required | Description |
|---|---|---|---|
limit | integer | no | Maximum related bookmarks |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | RelatedBookmarksResponse | Related bookmarks |
404 | application/problem+json | ProblemDetails | Bookmark not found |
422 | application/problem+json | ProblemDetails | Embedding provider is not configured |
Examples:
List related bookmarks
Request:
curl "http://127.0.0.1:3210/bookmarks/bm_123/related?limit=5"
GET /bookmarks/:id/status
Get latest pipeline job and failure status for a bookmark.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BookmarkPipelineStatusResponse | Bookmark pipeline status |
404 | application/problem+json | ProblemDetails | Bookmark not found |
POST /bookmarks/:id/failure/dismiss
Dismiss the current non-blocking pipeline failure for a bookmark.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
204 | - | - | Pipeline failure dismissed |
404 | application/problem+json | ProblemDetails | Bookmark not found |
409 | application/problem+json | ProblemDetails | Blocking pipeline failure cannot be dismissed |
Reprocess
POST /bookmarks/:id/retry
Retry pipeline work for one bookmark.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
202 | application/json | ReprocessBatchResponse | Selected bookmark retry accepted |
404 | application/problem+json | ProblemDetails | Bookmark not found |
POST /bookmarks/reprocess
Enqueue durable reprocess or re-embed jobs for existing bookmarks.
Request body:
- Content type:
application/json - Schema:
ReprocessRequest
| Field | Type | Required | Description |
|---|---|---|---|
mode | "selected" | "failed_only" | "all" | "embeddings_only" | yes | Reprocess mode |
bookmark_id | string | no | Bookmark ID required when mode is selected |
replace_ai_fields | boolean | no | When true, allow reprocessing to update AI-derived title, category, and tags; manual notes are never overwritten |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
202 | application/json | ReprocessBatchResponse | Reprocess batch accepted |
400 | application/problem+json | ProblemDetails | Malformed JSON |
404 | application/problem+json | ProblemDetails | Selected bookmark not found |
422 | application/problem+json | ProblemDetails | Invalid reprocess request |
Examples:
Retry failed pipeline work
Request:
curl -X POST http://127.0.0.1:3210/bookmarks/reprocess \
-H "Content-Type: application/json" \
-d '{"mode":"failed_only"}'
GET /reprocess/:batchId
Return progress counts for a durable reprocess batch.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
batchId | string | yes | batchId path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | ReprocessBatchStatusResponse | Reprocess batch status |
404 | application/problem+json | ProblemDetails | Reprocess batch not found |
Search
GET /search
Search bookmarks by keyword, semantic, or hybrid mode.
Query parameters:
| Field | Type | Required | Description |
|---|---|---|---|
q | string | no | Search query |
mode | "keyword" | "semantic" | "hybrid" | no | Search mode |
tag | string | no | Filter by tag name |
domain | string | no | Filter by exact domain |
category_id | string | no | Filter by exact category ID; takes precedence over category |
category | string | no | Filter by category name |
date_from | string | no | Inclusive ISO date or date-time lower bound |
date_to | string | no | Inclusive ISO date or date-time upper bound |
read_later | "true" | "false" | "1" | "0" | no | Filter by read-later state; accepts boolean strings or numeric flags |
read_state | "read" | "unread" | no | Filter by read state |
is_pinned | "true" | "false" | "1" | "0" | no | Filter by pinned/starred state; accepts boolean strings or numeric flags |
opened_count_min | integer | no | Filter to bookmarks opened at least this many times |
opened_count_max | integer | no | Filter to bookmarks opened no more than this many times |
last_opened_from | string | no | Inclusive ISO date or date-time lower bound for last opened time |
last_opened_to | string | no | Inclusive ISO date or date-time upper bound for last opened time |
sort | "created_at" | "updated_at" | "title" | "domain" | "opened_count" | "last_opened_at" | no | Sort key applied before pagination |
direction | "asc" | "desc" | no | Sort direction; requires sort and defaults to desc when omitted |
limit | integer | no | Maximum number of results to return |
offset | integer | no | Number of results to skip |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | SearchResponse | Search page |
400 | application/problem+json | ProblemDetails | Invalid FTS query syntax |
422 | application/problem+json | ProblemDetails | Invalid mode or missing embedding configuration |
Examples:
Hybrid search with pagination
Request:
curl "http://127.0.0.1:3210/search?q=vector%20search&mode=hybrid&tag=rag&read_state=read&last_opened_from=2026-06-01&limit=10&offset=0"
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{
"id": "bm_123",
"url": "https://example.com/rag-vector-search",
"domain": "example.com",
"title": "RAG Vector Search Notes",
"description": "Practical notes about vector search for retrieval augmented generation.",
"status": "indexed",
"category_id": "cat_ai",
"favicon_url": "/media/bookmarks/bm_123/favicon",
"screenshot_url": "/media/bookmarks/bm_123/screenshot",
"is_pinned": 0,
"is_archived": 0,
"is_trashed": 0,
"trashed_at": null,
"read_later": 1,
"read_at": null,
"opened_count": 2,
"last_opened_at": "2026-06-01T08:45:00.000Z",
"notes": "Compare chunking guidance with local notes.",
"created_at": "2026-06-01T09:30:00.000Z",
"updated_at": "2026-06-01T09:30:00.000Z",
"tags": [
"rag",
"search"
],
"snippet": "Use hybrid retrieval when exact vector search terms matter.",
"rank": 0.93
}
],
"pagination": {
"total": 1,
"limit": 10,
"offset": 0,
"has_more": false
},
"meta": {
"mode": "hybrid"
}
}
Categories
GET /categories
List categories as a tree with bookmark counts.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | CategoryTreeResponse | Category tree |
Examples:
List category tree
Request:
curl http://127.0.0.1:3210/categories
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{
"id": "cat_ai",
"name": "AI Research",
"parent_id": null,
"color": "#2563eb",
"icon": "brain",
"description": "Papers, implementation notes, and reference material for AI work.",
"slug": "ai-research",
"is_archived": 0,
"is_public": 0,
"created_at": "2026-06-01T09:30:00.000Z",
"updated_at": "2026-06-01T09:30:00.000Z",
"bookmark_count": 1,
"children": []
}
]
}
POST /categories
Create a category.
Request body:
- Content type:
application/json - Schema:
CategoryRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Category name |
parent_id | string | null | no | Parent category ID |
color | string | null | no | Optional category hex color |
icon | string | null | no | Optional lowercase icon token |
description | string | null | no | Optional category description |
slug | string | null | no | Optional category slug |
is_archived | 0 | 1 | no | Archived metadata flag, 0 or 1 |
is_public | 0 | 1 | no | Public visibility metadata flag, 0 or 1; local metadata only and does not expose data |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
201 | application/json | CategoryResponse | Created category |
400 | application/problem+json | ProblemDetails | Malformed JSON |
409 | application/problem+json | ProblemDetails | Duplicate category under parent |
422 | application/problem+json | ProblemDetails | Invalid name or parent |
Examples:
Create a category with metadata
Request:
curl -X POST http://127.0.0.1:3210/categories \
-H "Content-Type: application/json" \
-d '{"name":"AI Research","color":"#2563eb","icon":"brain","slug":"ai-research","description":"Papers, implementation notes, and reference material for AI work.","is_public":0}'
Response:
HTTP/1.1 201 Created
Content-Type: application/json
{
"data": {
"id": "cat_ai",
"name": "AI Research",
"parent_id": null,
"color": "#2563eb",
"icon": "brain",
"description": "Papers, implementation notes, and reference material for AI work.",
"slug": "ai-research",
"is_archived": 0,
"is_public": 0,
"created_at": "2026-06-01T09:30:00.000Z",
"updated_at": "2026-06-01T09:30:00.000Z"
}
}
PUT /categories/:id
Rename or reparent a category.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Request body:
- Content type:
application/json - Schema:
CategoryPatchRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | Category name |
parent_id | string | null | no | Parent category ID |
color | string | null | no | Optional category hex color |
icon | string | null | no | Optional lowercase icon token |
description | string | null | no | Optional category description |
slug | string | null | no | Optional category slug |
is_archived | 0 | 1 | no | Archived metadata flag, 0 or 1 |
is_public | 0 | 1 | no | Public visibility metadata flag, 0 or 1; local metadata only and does not expose data |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | CategoryResponse | Updated category |
400 | application/problem+json | ProblemDetails | Malformed JSON |
404 | application/problem+json | ProblemDetails | Category not found |
409 | application/problem+json | ProblemDetails | Duplicate category under parent |
422 | application/problem+json | ProblemDetails | Invalid patch or parent |
DELETE /categories/:id
Delete a category.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
204 | - | - | Category deleted |
404 | application/problem+json | ProblemDetails | Category not found |
Tags
GET /tags
List tags with bookmark counts.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | TagListResponse | Tags |
Examples:
List tags
Request:
curl http://127.0.0.1:3210/tags
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{
"id": "tag_rag",
"name": "rag",
"created_at": "2026-06-01T09:30:00.000Z",
"bookmark_count": 1
}
]
}
POST /tags
Create a tag, idempotently returning an existing tag when present.
Request body:
- Content type:
application/json - Schema:
TagRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Tag name, normalized to lowercase |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | TagResponse | Existing tag |
201 | application/json | TagResponse | Created tag |
400 | application/problem+json | ProblemDetails | Malformed JSON |
422 | application/problem+json | ProblemDetails | Invalid tag name |
Examples:
Create a tag
Request:
curl -X POST http://127.0.0.1:3210/tags \
-H "Content-Type: application/json" \
-d '{"name":"rag"}'
Response:
HTTP/1.1 201 Created
Content-Type: application/json
{
"data": {
"id": "tag_rag",
"name": "rag",
"created_at": "2026-06-01T09:30:00.000Z"
}
}
PUT /tags/:id
Rename a tag without changing bookmark associations.
Duplicate target tag names are rejected with 409 rather than merged implicitly.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Request body:
- Content type:
application/json - Schema:
TagRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Tag name, normalized to lowercase |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | TagResponse | Renamed tag |
400 | application/problem+json | ProblemDetails | Malformed JSON |
404 | application/problem+json | ProblemDetails | Tag not found |
409 | application/problem+json | ProblemDetails | Duplicate tag name |
422 | application/problem+json | ProblemDetails | Invalid tag name |
DELETE /tags/:id
Delete a tag and detach it from bookmarks.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
204 | - | - | Tag deleted |
404 | application/problem+json | ProblemDetails | Tag not found |
POST /bookmarks/:id/tags
Attach a tag to a bookmark.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Request body:
- Content type:
application/json - Schema:
TagRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Tag name, normalized to lowercase |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
201 | application/json | BookmarkResponse | Bookmark with attached tag |
400 | application/problem+json | ProblemDetails | Malformed JSON |
404 | application/problem+json | ProblemDetails | Bookmark not found |
422 | application/problem+json | ProblemDetails | Invalid tag name |
DELETE /bookmarks/:id/tags/:tagId
Detach a tag from a bookmark.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Bookmark ID |
tagId | string | yes | Tag ID |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
204 | - | - | Tag detached |
404 | application/problem+json | ProblemDetails | Bookmark, tag, or attachment not found |
Domains
GET /domains
List domains with active bookmark counts.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | DomainListResponse | Domains |
500 | application/problem+json | ProblemDetails | Query failed |
Import
POST /import/preview
Preview a Netscape HTML bookmark export without mutating library data.
Request body:
- Content type:
multipart/form-data - Multipart body with a file field plus optional duplicatePolicy JSON and remapping JSON fields.
- Schema:
object
| Field | Type | Required | Description |
|---|---|---|---|
file | string | yes | HTML bookmark export file |
duplicatePolicy | string | no | Optional JSON duplicate policy |
remapping | string | no | Optional JSON ImportRemappingInput. Folder create mappings use sourcePath and targetPath; folder existing mappings use sourcePath and categoryId. Tag existing mappings use sourceTag and tagId; new/renamed mappings use sourceTag and targetName; skipped mappings use only sourceTag. |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | ImportPreviewResponse | Import preview |
400 | application/problem+json | ProblemDetails | Multipart parsing failed |
413 | application/problem+json | ProblemDetails | File exceeds 10 MB |
415 | application/problem+json | ProblemDetails | Request is not multipart/form-data |
422 | application/problem+json | ProblemDetails | Missing file, invalid bookmark export, or invalid duplicate policy |
Examples:
Preview Netscape bookmarks
Request:
curl -X POST http://127.0.0.1:3210/import/preview \
-F file=@bookmarks.html \
-F 'duplicatePolicy={"active":"merge","archived":"restore_merge","trashed":"skip"}' \
-F 'remapping={"folders":[{"sourcePath":["Research"],"action":"existing","categoryId":"cat_research"}],"tags":[{"sourceTag":"sqlite","action":"renamed","targetName":"database"}]}'
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"duplicatePolicy": {
"active": "merge",
"archived": "restore_merge",
"trashed": "skip"
},
"remapping": {
"folders": [
{
"sourcePath": [
"Research"
],
"action": "existing",
"targetCategoryId": "cat_research",
"targetPath": [
"Research"
],
"status": "existing"
}
],
"tags": [
{
"sourceTag": "database",
"action": "existing",
"targetTagId": "tag_database",
"targetName": "database",
"status": "existing"
},
{
"sourceTag": "sqlite",
"action": "renamed",
"targetTagId": "tag_database",
"targetName": "database",
"status": "existing"
}
]
},
"summary": {
"totalRows": 12,
"importableRows": 10,
"new": 7,
"activeDuplicates": 1,
"archivedDuplicates": 1,
"trashedDuplicates": 1,
"invalidUrls": 1,
"privateUrls": 1,
"created": 7,
"merged": 1,
"restored": 1,
"skipped": 3
},
"folders": [
[
"Research"
],
[
"Research",
"Databases"
]
],
"tags": [
"database",
"sqlite"
],
"warnings": [
"Skipped private/internal URL: http://127.0.0.1/admin"
],
"rows": [
{
"classification": "active_duplicate",
"action": "merge",
"url": "https://example.com/rag-vector-search",
"title": "RAG Vector Search Notes",
"notes": null,
"tags": [
"database"
],
"targetTags": [
"database"
],
"folders": [
"Research"
],
"targetCategoryId": "cat_research",
"targetCategoryPath": [
"Research"
],
"existingBookmarkId": "bm_123",
"existingState": "active",
"skipReason": null
}
]
}
}
POST /import
Import a Netscape HTML bookmark export.
Request body:
- Content type:
multipart/form-data - Multipart body with a file field plus optional duplicatePolicy JSON and remapping JSON fields.
- Schema:
object
| Field | Type | Required | Description |
|---|---|---|---|
file | string | yes | HTML bookmark export file |
duplicatePolicy | string | no | Optional JSON duplicate policy |
remapping | string | no | Optional JSON ImportRemappingInput. Folder create mappings use sourcePath and targetPath; folder existing mappings use sourcePath and categoryId. Tag existing mappings use sourceTag and tagId; new/renamed mappings use sourceTag and targetName; skipped mappings use only sourceTag. |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | ImportSummaryResponse | Import accepted |
400 | application/problem+json | ProblemDetails | Multipart parsing failed |
413 | application/problem+json | ProblemDetails | File exceeds 10 MB |
415 | application/problem+json | ProblemDetails | Request is not multipart/form-data |
422 | application/problem+json | ProblemDetails | Missing file or invalid bookmark export |
Examples:
Import Netscape bookmarks
Request:
curl -X POST http://127.0.0.1:3210/import \
-F file=@bookmarks.html \
-F 'remapping={"folders":[{"sourcePath":["Research"],"action":"existing","categoryId":"cat_research"}],"tags":[{"sourceTag":"sqlite","action":"renamed","targetName":"database"}]}'
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"importId": "import_123",
"total": 12,
"folders": 4,
"warnings": 1,
"duplicatePolicy": {
"active": "skip",
"archived": "skip",
"trashed": "skip"
},
"remapping": {
"folders": [
{
"sourcePath": [
"Research"
],
"action": "existing",
"targetCategoryId": "cat_research",
"targetPath": [
"Research"
],
"status": "existing"
}
],
"tags": [
{
"sourceTag": "sqlite",
"action": "renamed",
"targetTagId": null,
"targetName": "database",
"status": "new"
}
]
},
"progressUrl": "/import/import_123/progress"
}
}
GET /import/:importId/progress
Stream import progress over Server-Sent Events.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
importId | string | yes | importId path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | text/event-stream | ImportProgressEvent | SSE stream of progress events |
404 | application/problem+json | ProblemDetails | Import ID not found |
Settings
GET /settings
Read current settings with secrets redacted and runtime capabilities.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | SettingsResponse | Settings |
PUT /settings
Deep-merge a settings patch into persisted settings.
Request body:
- Content type:
application/json - Schema:
SettingsPatch
| Field | Type | Required | Description |
|---|---|---|---|
ai | object | no | |
ai.provider | "openai" | "ollama" | "anthropic" | "openrouter" | "openai_compatible" | "deepseek" | "none" | no | LLM provider |
ai.openai | object | no | |
ai.openai.api_key | string | no | OpenAI API key, empty string clears it |
ai.openai.model | string | no | OpenAI chat model |
ai.ollama | object | no | |
ai.ollama.base_url | string | no | Ollama base URL |
ai.ollama.model | string | no | Ollama model |
ai.anthropic | object | no | |
ai.anthropic.api_key | string | no | Anthropic API key, empty string clears it |
ai.anthropic.base_url | string | no | Anthropic API base URL |
ai.anthropic.model | string | no | Anthropic Messages API model |
ai.openrouter | object | no | |
ai.openrouter.api_key | string | no | OpenRouter API key, empty string clears it |
ai.openrouter.base_url | string | no | OpenRouter OpenAI-compatible base URL |
ai.openrouter.model | string | no | OpenRouter model slug |
ai.openai_compatible | object | no | |
ai.openai_compatible.api_key | string | no | Custom OpenAI-compatible API key, empty string clears it |
ai.openai_compatible.base_url | string | no | Custom OpenAI-compatible chat base URL |
ai.openai_compatible.model | string | no | Custom OpenAI-compatible chat model |
ai.deepseek | object | no | |
ai.deepseek.api_key | string | no | DeepSeek API key, empty string clears it |
ai.deepseek.base_url | string | no | DeepSeek OpenAI-compatible base URL |
ai.deepseek.model | string | no | DeepSeek chat model |
ai.embeddings | object | no | |
ai.embeddings.provider | "openai" | "ollama" | "openai_compatible" | no | Embedding provider |
ai.embeddings.model | string | no | Embedding model |
ai.embeddings.openai_compatible | object | no | |
ai.embeddings.openai_compatible.api_key | string | no | Custom OpenAI-compatible embedding API key, empty string clears it |
ai.embeddings.openai_compatible.base_url | string | no | Custom OpenAI-compatible embeddings base URL |
ai.embeddings.openai_compatible.model | string | no | Custom OpenAI-compatible embedding model |
app | object | no | |
app.autostart | boolean | no | Start daemon automatically |
app.theme | "light" | "dark" | "system" | no | UI theme |
app.lock | object | no | |
app.lock.enabled | boolean | no | Whether app lock is enabled |
app.lock.pin_hash | string | no | PIN hash, empty string clears it |
backup | object | no | |
backup.local | object | no | |
backup.local.destination_path | string | no | Absolute custom backup destination, or empty string for default |
backup.schedule | object | no | |
backup.schedule.enabled | boolean | no | Enable scheduled snapshots |
backup.schedule.cron | string | no | Five-part cron expression |
backup.schedule.retention_count | integer | no | Number of local snapshots to retain |
backup.s3 | object | no | |
backup.s3.endpoint | string | no | S3-compatible endpoint URL, or empty string for AWS |
backup.s3.bucket | string | no | S3 bucket |
backup.s3.access_key | string | no | S3 access key |
backup.s3.secret_key | string | no | S3 secret key |
backup.s3.region | string | no | S3 region |
backup.s3.prefix | string | no | Object key prefix |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | SettingsResponse | Updated settings |
400 | application/problem+json | ProblemDetails | Malformed JSON |
422 | application/problem+json | ProblemDetails | Invalid settings patch |
500 | application/problem+json | ProblemDetails | Settings could not be persisted |
POST /settings/test-ai
Test connectivity to the configured LLM provider.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | ConnectivityTestResponse | Connectivity result |
Backup
POST /backup
Create a local backup snapshot and optionally upload it to S3.
Request body:
- Content type:
application/json - Schema:
BackupCreateRequest
| Field | Type | Required | Description |
|---|---|---|---|
skip_remote | boolean | no | When true, create only the local snapshot and skip S3 upload |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
201 | application/json | BackupResult | Backup created |
400 | application/json | LegacyError | Malformed or non-object JSON body |
409 | application/json | LegacyError | Backup or restore already in progress |
422 | application/json | LegacyError | Invalid backup create request |
500 | application/json | LegacyError | Backup creation failed |
Examples:
Create a local backup
Request:
curl -X POST http://127.0.0.1:3210/backup \
-H "Content-Type: application/json" \
-d '{"skip_remote":true}'
Response:
HTTP/1.1 201 Created
Content-Type: application/json
{
"path": "/Users/me/.local/share/littleimp/backups/2026-06-01T09-30-00-000Z",
"size_bytes": 98304,
"bookmark_count": 42,
"created_at": "2026-06-01T09:30:00.000Z"
}
GET /backup/list
List local backups and optionally merge remote S3 backups.
Query parameters:
| Field | Type | Required | Description |
|---|---|---|---|
include_remote | "true" | "false" | no | When true, include S3 backups |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BackupListResponse | Backups |
422 | application/json | LegacyError | S3 is not configured |
500 | application/json | LegacyError | Remote backup listing failed |
GET /backup/schedule
Read backup schedule settings and the computed next run time.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BackupScheduleResponse | Backup schedule |
PUT /backup/schedule
Patch backup schedule settings.
Request body:
- Content type:
application/json - Schema:
BackupSchedulePatch
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | no | Enable scheduled snapshots |
cron | string | no | Five-part cron expression |
retention_count | integer | no | Number of local snapshots to retain |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BackupScheduleResponse | Updated backup schedule |
400 | application/json | LegacyError | Malformed or non-object JSON body |
422 | application/json | LegacyError | Invalid schedule patch |
500 | application/json | LegacyError | Schedule settings could not be saved |
Examples:
Update backup schedule
Request:
curl -X PUT http://127.0.0.1:3210/backup/schedule \
-H "Content-Type: application/json" \
-d '{"enabled":true,"cron":"0 3 * * *","retention_count":10}'
GET /backup/destination
Read the effective backup directory and writability.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BackupDestinationResponse | Backup destination |
PUT /backup/destination
Set or clear the custom local backup directory.
Request body:
- Content type:
application/json - Schema:
BackupDestinationPatch
| Field | Type | Required | Description |
|---|---|---|---|
path | string | yes | Absolute custom backup path, or empty string to reset |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BackupDestinationResponse | Updated backup destination |
400 | application/json | LegacyError | Malformed or non-object JSON body |
422 | application/json | LegacyError | Invalid or unwritable path |
500 | application/json | LegacyError | Destination settings could not be saved |
Examples:
Set a custom backup destination
Request:
curl -X PUT http://127.0.0.1:3210/backup/destination \
-H "Content-Type: application/json" \
-d '{"path":"/Users/me/Backups/Grimoire"}'
POST /backup/verify
Verify a local backup snapshot without restoring it.
Request body:
- Content type:
application/json - Schema:
BackupVerifyRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Local backup directory name |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | BackupVerificationResult | Backup verification result |
400 | application/json | LegacyError | Malformed JSON or non-object request body |
409 | application/json | LegacyError | Backup or restore already in progress |
422 | application/json | LegacyError | Invalid verify request or backup validation failed |
500 | application/json | LegacyError | Backup verification failed |
Examples:
Verify a local backup
Request:
curl -X POST http://127.0.0.1:3210/backup/verify \
-H "Content-Type: application/json" \
-d '{"name":"2026-05-13T09-30-00-000Z"}'
POST /backup/package
Create an encrypted package file from a local backup snapshot.
Request body:
- Content type:
application/json - Schema:
BackupPackageRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Local backup directory name |
password | string | yes | Password used to encrypt the package |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
201 | application/json | EncryptedBackupPackageResult | Encrypted backup package created |
400 | application/json | LegacyError | Malformed JSON or non-object request body |
409 | application/json | LegacyError | Backup or restore already in progress |
422 | application/json | LegacyError | Invalid package request or backup validation failed |
500 | application/json | LegacyError | Encrypted backup package creation failed |
Examples:
Create an encrypted package
Request:
curl -X POST http://127.0.0.1:3210/backup/package \
-H "Content-Type: application/json" \
-d '{"name":"2026-05-13T09-30-00-000Z","password":"correct horse battery staple"}'
POST /backup/package/verify
Verify an encrypted package file without restoring it.
Request body:
- Content type:
application/json - Schema:
EncryptedBackupPackageRequest
| Field | Type | Required | Description |
|---|---|---|---|
path | string | yes | Absolute path to an encrypted backup package file accessible by the daemon |
password | string | yes | Password used to decrypt the package |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | EncryptedBackupPackageVerificationResult | Encrypted backup package verification result |
400 | application/json | LegacyError | Malformed JSON or non-object request body |
409 | application/json | LegacyError | Backup or restore already in progress |
422 | application/json | LegacyError | Invalid package request, wrong password, or package validation failed |
500 | application/json | LegacyError | Encrypted backup package verification failed |
Examples:
Verify an encrypted package
Request:
curl -X POST http://127.0.0.1:3210/backup/package/verify \
-H "Content-Type: application/json" \
-d '{"path":"/Users/me/Library/Application Support/littleimp/backups/2026-05-13T09-30-00-000Z.littleimp-backup.enc","password":"correct horse battery staple"}'
POST /restore
Restore from a local backup directory, remote S3 snapshot, or encrypted package.
Request body:
- Content type:
application/json - Schema:
RestoreRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | Local backup directory name |
source | "remote" | "encrypted_package" | no | Restore source |
key | string | no | Remote S3 snapshot.db key |
path | string | no | Absolute path to an encrypted backup package file accessible by the daemon |
password | string | no | Password used to decrypt the encrypted package |
allow_unsafe_no_checksum | boolean | no | Allow restoring a backup with no checksum file |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | RestoreResult | Restore completed |
400 | application/json | LegacyError | Malformed JSON |
409 | application/json | LegacyError | Backup or restore already in progress |
422 | application/json | LegacyError | Invalid restore request or backup validation failed |
500 | application/json | LegacyError | Restore failed |
Examples:
Restore a local backup
Request:
curl -X POST http://127.0.0.1:3210/restore \
-H "Content-Type: application/json" \
-d '{"name":"2026-05-13T09-30-00-000Z"}'
Restore a remote backup
Request:
curl -X POST http://127.0.0.1:3210/restore \
-H "Content-Type: application/json" \
-d '{"source":"remote","key":"little-imp/2026-05-13T09-30-00-000Z/snapshot.db"}'
Restore an encrypted package
Request:
curl -X POST http://127.0.0.1:3210/restore \
-H "Content-Type: application/json" \
-d '{"source":"encrypted_package","path":"/Users/me/Library/Application Support/littleimp/backups/2026-05-13T09-30-00-000Z.littleimp-backup.enc","password":"correct horse battery staple"}'
POST /settings/test-s3
Test connectivity to the configured S3 backup destination.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | ConnectivityTestResponse | S3 connectivity succeeded |
422 | application/json | LegacyError | S3 is not configured or connection failed |
Examples:
Test S3 connectivity
Request:
curl -X POST http://127.0.0.1:3210/settings/test-s3
Timeline
GET /timeline
List timeline events with pagination.
Query parameters:
| Field | Type | Required | Description |
|---|---|---|---|
limit | integer | no | Maximum number of results to return |
offset | integer | no | Number of results to skip |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | TimelinePage | Timeline page |
400 | application/problem+json | ProblemDetails | Invalid limit or offset |
Suggestions
GET /suggestions
List pending organization-agent suggestions.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | SuggestionsResponse | Pending suggestions |
POST /suggestions/:id/accept
Accept a suggestion and apply its action.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | object | Accepted suggestion |
404 | application/problem+json | ProblemDetails | Suggestion not found |
422 | application/problem+json | ProblemDetails | Suggestion is no longer pending or action is invalid |
500 | application/problem+json | ProblemDetails | Suggestion action failed |
POST /suggestions/:id/reject
Reject a pending suggestion.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | object | Rejected suggestion |
404 | application/problem+json | ProblemDetails | Suggestion not found |
422 | application/problem+json | ProblemDetails | Suggestion is no longer pending |
500 | application/problem+json | ProblemDetails | Suggestion could not be resolved |
Export
GET /export
Export active bookmarks as JSON or CSV.
Query parameters:
| Field | Type | Required | Description |
|---|---|---|---|
format | "json" | "csv" | no | Export format |
tag | string | no | Filter by tag name |
domain | string | no | Filter by exact domain |
category_id | string | no | Filter by exact category ID; takes precedence over category |
category | string | no | Filter by category name |
date_from | string | no | Inclusive ISO date or date-time lower bound |
date_to | string | no | Inclusive ISO date or date-time upper bound |
read_later | "true" | "false" | "1" | "0" | no | Filter by read-later state; accepts boolean strings or numeric flags |
read_state | "read" | "unread" | no | Filter by read state |
is_pinned | "true" | "false" | "1" | "0" | no | Filter by pinned/starred state; accepts boolean strings or numeric flags |
opened_count_min | integer | no | Filter to bookmarks opened at least this many times |
opened_count_max | integer | no | Filter to bookmarks opened no more than this many times |
last_opened_from | string | no | Inclusive ISO date or date-time lower bound for last opened time |
last_opened_to | string | no | Inclusive ISO date or date-time upper bound for last opened time |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json or text/csv | array<ExportBookmark> | Downloadable JSON or CSV export |
400 | application/json | LegacyError | Invalid format |
422 | application/json | LegacyError | Invalid export filter |
Examples:
Export read-later bookmarks as JSON
Request:
curl "http://127.0.0.1:3210/export?format=json&read_later=true"
Response:
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": "bm_123",
"url": "https://example.com/rag-vector-search",
"title": "RAG Vector Search Notes",
"summary": "A practical walkthrough of hybrid retrieval for RAG systems.",
"tags": [
"rag",
"search"
],
"category": "AI Research",
"domain": "example.com",
"is_pinned": 0,
"read_later": 1,
"opened_count": 2,
"last_opened_at": "2026-06-01T08:45:00.000Z",
"created_at": "2026-06-01T09:30:00.000Z",
"is_archived": 0,
"read_at": null,
"notes": "Compare chunking guidance with local notes."
}
]
Export read pinned bookmarks opened this month
Request:
curl "http://127.0.0.1:3210/export?format=csv&read_state=read&is_pinned=true&opened_count_min=1&last_opened_from=2026-06-01"
Reject an invalid export filter
Request:
curl "http://127.0.0.1:3210/export?format=json&read_state=maybe"
Response:
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": "`read_state` must be read or unread"
}
Integrations
POST /capture
Capture a bookmark from an explicit local integration.
This protected local integration endpoint requires Authorization: Bearer <integration-token>. It creates a normal bookmark, optionally applies tags, notes, category assignment, and capture metadata, then enqueues the standard ingestion pipeline. Existing active URLs are returned idempotently without merging metadata or queueing duplicate work.
Request body:
- Content type:
application/json - Schema:
CaptureRequest
| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | HTTP or HTTPS URL to save |
title | string | no | Optional title override |
tags | array | no | Optional replacement tag names |
category_id | string | null | no | Existing category ID to assign |
category | string | no | Root category name to resolve or create when category_id is omitted |
notes | string | null | no | Personal notes, or null to leave empty |
source | CaptureSource | no | |
source.client | string | null | no | Optional local integration client label |
source.source_url | string | null | no | Optional public HTTP or HTTPS page/context URL |
source.referrer_url | string | null | no | Optional public HTTP or HTTPS referrer URL |
source.selected_text | string | null | no | Optional selected text or short capture context |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | CaptureResponse | Existing active bookmark returned idempotently |
201 | application/json | CaptureResponse | Bookmark captured and ingest queued |
400 | application/problem+json | ProblemDetails | Malformed JSON |
401 | application/problem+json | ProblemDetails | Missing, invalid, rotated, or revoked integration token |
409 | application/problem+json | ProblemDetails | URL already exists in trash or archive |
413 | application/json | LegacyError | Request body exceeds local JSON limit |
422 | application/problem+json | ProblemDetails | Invalid capture request |
Examples:
Capture a bookmark from a local integration
Request:
curl -X POST http://127.0.0.1:3210/capture \
-H "Authorization: Bearer limp_it_example" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/rag-vector-search","title":"RAG Vector Search Notes","tags":["rag","search"],"category":"AI Research","notes":"Compare chunking guidance with local notes.","source":{"client":"local-capture","source_url":"https://example.com/rag-vector-search","referrer_url":"https://example.com/","selected_text":"Hybrid retrieval combines exact and semantic matching."}}'
Response:
HTTP/1.1 201 Created
Content-Type: application/json
{
"data": {
"bookmark": {
"id": "bm_123",
"url": "https://example.com/rag-vector-search",
"domain": "example.com",
"title": "RAG Vector Search Notes",
"description": null,
"status": "saved",
"category_id": "cat_ai",
"favicon_url": null,
"screenshot_url": null,
"is_pinned": 0,
"is_archived": 0,
"is_trashed": 0,
"trashed_at": null,
"read_later": 0,
"read_at": null,
"opened_count": 0,
"last_opened_at": null,
"notes": "Compare chunking guidance with local notes.",
"created_at": "2026-06-01T09:30:00.000Z",
"updated_at": "2026-06-01T09:30:00.000Z",
"tags": [
"rag",
"search"
]
},
"capture": {
"bookmark_id": "bm_123",
"source_client": "local-capture",
"source_url": "https://example.com/rag-vector-search",
"referrer_url": "https://example.com/",
"selected_text": "Hybrid retrieval combines exact and semantic matching.",
"captured_at": "2026-06-01T09:30:00.000Z",
"updated_at": "2026-06-01T09:30:00.000Z"
},
"created": true,
"job_id": "job_123"
}
}
GET /capture/bookmarklet
Bookmarklet capture page (hidden iframe target, no auth header).
The browser bookmarklet uses a hidden iframe pointed at this endpoint to avoid CORS. Authentication is via a query-parameter token. The endpoint returns an HTML page (not JSON) that the iframe renders silently. Designed for the Settings โ Browser Integration bookmarklet flow; not intended for direct use.
Query parameters:
| Field | Type | Required | Description |
|---|---|---|---|
token | string | yes | Integration bearer token (query-param auth) |
url | string | yes | The URL to capture |
title | string | no | Page title |
selection | string | no | User-selected text |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | - | - | Bookmark already exists (not duplicated) |
201 | - | - | Bookmark captured successfully |
400 | application/problem+json | ProblemDetails | Missing token or url |
401 | application/problem+json | ProblemDetails | Invalid or revoked token |
409 | application/problem+json | ProblemDetails | URL exists in trash or archive |
422 | application/problem+json | ProblemDetails | Invalid URL |
GET /integration-tokens
List managed local integration tokens with secret values redacted.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | IntegrationTokenListResponse | Integration tokens |
Examples:
List integration tokens
Request:
curl http://127.0.0.1:3210/integration-tokens
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{
"id": "itok_123",
"name": "Raycast MCP",
"token_prefix": "limp_it_7fd9",
"created_at": "2026-06-01T09:30:00.000Z",
"last_used_at": null,
"revoked_at": null
}
]
}
POST /integration-tokens
Create a managed bearer token for an explicit local integration client.
The full token is returned only once. Store it in the client and use it as an Authorization bearer token. The JSON body is optional; omit it to create a token named Local integration.
Request body:
- Content type:
application/json - Schema:
IntegrationTokenCreateRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | User-visible integration client name |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
201 | application/json | IntegrationTokenCreateResponse | Integration token created |
400 | application/problem+json | ProblemDetails | Malformed JSON |
415 | application/problem+json | ProblemDetails | Request body is not application/json |
422 | application/problem+json | ProblemDetails | Invalid token name |
Examples:
Create an integration token
Request:
curl -X POST http://127.0.0.1:3210/integration-tokens \
-H "Content-Type: application/json" \
-d '{"name":"Raycast MCP"}'
Response:
HTTP/1.1 201 Created
Content-Type: application/json
{
"data": {
"token": "limp_it_example_secret",
"record": {
"id": "itok_123",
"name": "Raycast MCP",
"token_prefix": "limp_it_7fd9",
"created_at": "2026-06-01T09:30:00.000Z",
"last_used_at": null,
"revoked_at": null
}
}
}
POST /integration-tokens/:id/rotate
Rotate an active integration token and return the new bearer token once.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | IntegrationTokenCreateResponse | Integration token rotated |
404 | application/problem+json | ProblemDetails | Active integration token not found |
DELETE /integration-tokens/:id
Revoke an integration token.
Path parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | id path parameter |
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
204 | - | - | Integration token revoked |
404 | application/problem+json | ProblemDetails | Integration token not found |
MCP
ALL /mcp
Handle MCP Streamable HTTP transport requests.
The daemon creates a fresh MCP server and transport for each request. MCP is a local integration surface and requires Authorization: Bearer <integration-token>.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json or text/event-stream | - | MCP transport response |
401 | application/problem+json | ProblemDetails | Missing, invalid, rotated, or revoked integration token |
500 | application/json | McpErrorResponse | MCP request failed |
Examples:
Call the MCP endpoint
Request:
curl -X POST http://127.0.0.1:3210/mcp \
-H "Authorization: Bearer limp_it_example" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.1"}}}'
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"serverInfo": {
"name": "grimoire",
"version": "1.0.1"
}
}
}
Reject a missing integration token
Request:
curl -X POST http://127.0.0.1:3210/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Response:
HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json
WWW-Authenticate: Bearer realm="littleimp-local-integrations"
{
"type": "https://littleimp.app/problems/integration-token-required",
"title": "Unauthorized",
"status": 401,
"detail": "A managed integration bearer token is required for this route"
}
Demo
POST /demo/load
Load demo bookmarks into an empty library for first-run exploration.
Creates a set of 10 demo bookmarks with realistic developer-content URLs, titles, categories, and tags. Only succeeds when the library has no existing bookmarks. Returns the count of created bookmarks and categories.
Responses:
| Status | Content type | Schema | Description |
|---|---|---|---|
200 | application/json | DemoLoadResult | Demo data loaded |
409 | application/json | LegacyError | Library is not empty โ demo can only be loaded on a fresh library |
500 | application/json | LegacyError | Failed to load demo data |
Schemas
ProblemDetails
RFC 7807-style problem response
| Field | Type | Required | Description |
|---|---|---|---|
type | string | yes | Stable problem type URI |
title | string | yes | Short human-readable error title |
status | integer | yes | HTTP status code |
detail | string | null | no | Human-readable explanation |
LegacyError
Legacy JSON error response
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes | Human-readable error message |
details | string | null | no | Optional additional details |
Pagination
| Field | Type | Required | Description |
|---|---|---|---|
total | integer | yes | Total matching records |
limit | integer | yes | Applied page size |
offset | integer | yes | Applied offset |
has_more | boolean | yes | Whether another page exists |
Bookmark
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Bookmark ID |
url | string | yes | Original bookmark URL |
domain | string | yes | URL hostname |
title | string | null | yes | Page title |
description | string | null | yes | Page description |
status | "saved" | "fetched" | "extracted" | "ai_enriched" | "indexed" | yes | Pipeline status |
category_id | string | null | yes | Assigned category ID |
favicon_url | string | null | yes | Cached favicon media path or URL |
screenshot_url | string | null | yes | Cached page preview media path or URL |
is_pinned | 0 | 1 | yes | Pinned flag, 0 or 1; maps Grimoire starred/favorite state |
is_archived | 0 | 1 | yes | Archived flag, 0 or 1 |
is_trashed | 0 | 1 | yes | Trash flag, 0 or 1 |
trashed_at | string | null | yes | Trash timestamp |
read_later | 0 | 1 | yes | Read-later flag, 0 or 1 |
read_at | string | null | yes | Read timestamp |
opened_count | integer | yes | Number of user-triggered opens |
last_opened_at | string | null | yes | Most recent user-triggered open timestamp |
notes | string | null | yes | Personal notes |
created_at | string | yes | Creation timestamp |
updated_at | string | yes | Update timestamp |
tags | array | yes | Tag names attached to the bookmark |
BookmarkContent
| Field | Type | Required | Description |
|---|---|---|---|
bookmark_id | string | yes | Bookmark ID |
raw_html | string | null | yes | Raw HTML |
markdown | string | null | yes | Extracted Markdown |
summary | string | null | yes | Extracted summary |
author | string | null | yes | Author |
published_at | string | null | yes | Published timestamp |
word_count | integer | null | yes | Estimated word count |
language | string | null | yes | Detected language |
extracted_at | string | yes | Extraction timestamp |
BookmarkMedia
Cached local media item
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Media cache record ID |
kind | "favicon" | "screenshot" | "image" | yes | Media kind |
url | string | yes | Local daemon media path |
source_url | string | yes | Original media source URL |
media_type | string | yes | Cached non-SVG image MIME type |
size_bytes | integer | yes | Cached media byte size |
alt | string | null | yes | Image alt text or preview label |
BookmarkMediaSet
Cached local media available for a bookmark
| Field | Type | Required | Description |
|---|---|---|---|
favicon | BookmarkMedia | null | yes | |
favicon.id | string | yes | Media cache record ID |
favicon.kind | "favicon" | "screenshot" | "image" | yes | Media kind |
favicon.url | string | yes | Local daemon media path |
favicon.source_url | string | yes | Original media source URL |
favicon.media_type | string | yes | Cached non-SVG image MIME type |
favicon.size_bytes | integer | yes | Cached media byte size |
favicon.alt | string | null | yes | Image alt text or preview label |
screenshot | BookmarkMedia | null | yes | |
screenshot.id | string | yes | Media cache record ID |
screenshot.kind | "favicon" | "screenshot" | "image" | yes | Media kind |
screenshot.url | string | yes | Local daemon media path |
screenshot.source_url | string | yes | Original media source URL |
screenshot.media_type | string | yes | Cached non-SVG image MIME type |
screenshot.size_bytes | integer | yes | Cached media byte size |
screenshot.alt | string | null | yes | Image alt text or preview label |
images | array | yes | Cached extracted images |
BookmarkDetail
Bookmark with extracted content
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Bookmark ID |
url | string | yes | Original bookmark URL |
domain | string | yes | URL hostname |
title | string | null | yes | Page title |
description | string | null | yes | Page description |
status | "saved" | "fetched" | "extracted" | "ai_enriched" | "indexed" | yes | Pipeline status |
category_id | string | null | yes | Assigned category ID |
favicon_url | string | null | yes | Cached favicon media path or URL |
screenshot_url | string | null | yes | Cached page preview media path or URL |
is_pinned | 0 | 1 | yes | Pinned flag, 0 or 1; maps Grimoire starred/favorite state |
is_archived | 0 | 1 | yes | Archived flag, 0 or 1 |
is_trashed | 0 | 1 | yes | Trash flag, 0 or 1 |
trashed_at | string | null | yes | Trash timestamp |
read_later | 0 | 1 | yes | Read-later flag, 0 or 1 |
read_at | string | null | yes | Read timestamp |
opened_count | integer | yes | Number of user-triggered opens |
last_opened_at | string | null | yes | Most recent user-triggered open timestamp |
notes | string | null | yes | Personal notes |
created_at | string | yes | Creation timestamp |
updated_at | string | yes | Update timestamp |
tags | array | yes | Tag names attached to the bookmark |
content | BookmarkContent | null | yes | |
content.bookmark_id | string | yes | Bookmark ID |
content.raw_html | string | null | yes | Raw HTML |
content.markdown | string | null | yes | Extracted Markdown |
content.summary | string | null | yes | Extracted summary |
content.author | string | null | yes | Author |
content.published_at | string | null | yes | Published timestamp |
content.word_count | integer | null | yes | Estimated word count |
content.language | string | null | yes | Detected language |
content.extracted_at | string | yes | Extraction timestamp |
media | BookmarkMediaSet | yes | |
media.favicon | BookmarkMedia | null | yes | |
media.favicon.id | string | yes | Media cache record ID |
media.favicon.kind | "favicon" | "screenshot" | "image" | yes | Media kind |
media.favicon.url | string | yes | Local daemon media path |
media.favicon.source_url | string | yes | Original media source URL |
media.favicon.media_type | string | yes | Cached non-SVG image MIME type |
media.favicon.size_bytes | integer | yes | Cached media byte size |
media.favicon.alt | string | null | yes | Image alt text or preview label |
media.screenshot | BookmarkMedia | null | yes | |
media.screenshot.id | string | yes | Media cache record ID |
media.screenshot.kind | "favicon" | "screenshot" | "image" | yes | Media kind |
media.screenshot.url | string | yes | Local daemon media path |
media.screenshot.source_url | string | yes | Original media source URL |
media.screenshot.media_type | string | yes | Cached non-SVG image MIME type |
media.screenshot.size_bytes | integer | yes | Cached media byte size |
media.screenshot.alt | string | null | yes | Image alt text or preview label |
media.images | array | yes | Cached extracted images |
BookmarkDetailResponse
Single bookmark response
| Field | Type | Required | Description |
|---|---|---|---|
data | BookmarkDetail | yes | |
data.id | string | yes | Bookmark ID |
data.url | string | yes | Original bookmark URL |
data.domain | string | yes | URL hostname |
data.title | string | null | yes | Page title |
data.description | string | null | yes | Page description |
data.status | "saved" | "fetched" | "extracted" | "ai_enriched" | "indexed" | yes | Pipeline status |
data.category_id | string | null | yes | Assigned category ID |
data.favicon_url | string | null | yes | Cached favicon media path or URL |
data.screenshot_url | string | null | yes | Cached page preview media path or URL |
data.is_pinned | 0 | 1 | yes | Pinned flag, 0 or 1; maps Grimoire starred/favorite state |
data.is_archived | 0 | 1 | yes | Archived flag, 0 or 1 |
data.is_trashed | 0 | 1 | yes | Trash flag, 0 or 1 |
data.trashed_at | string | null | yes | Trash timestamp |
data.read_later | 0 | 1 | yes | Read-later flag, 0 or 1 |
data.read_at | string | null | yes | Read timestamp |
data.opened_count | integer | yes | Number of user-triggered opens |
data.last_opened_at | string | null | yes | Most recent user-triggered open timestamp |
data.notes | string | null | yes | Personal notes |
data.created_at | string | yes | Creation timestamp |
data.updated_at | string | yes | Update timestamp |
data.tags | array | yes | Tag names attached to the bookmark |
data.content | BookmarkContent | null | yes | |
data.content.bookmark_id | string | yes | Bookmark ID |
data.content.raw_html | string | null | yes | Raw HTML |
data.content.markdown | string | null | yes | Extracted Markdown |
data.content.summary | string | null | yes | Extracted summary |
data.content.author | string | null | yes | Author |
data.content.published_at | string | null | yes | Published timestamp |
data.content.word_count | integer | null | yes | Estimated word count |
data.content.language | string | null | yes | Detected language |
data.content.extracted_at | string | yes | Extraction timestamp |
data.media | BookmarkMediaSet | yes | |
data.media.favicon | BookmarkMedia | null | yes | |
data.media.favicon.id | string | yes | Media cache record ID |
data.media.favicon.kind | "favicon" | "screenshot" | "image" | yes | Media kind |
data.media.favicon.url | string | yes | Local daemon media path |
data.media.favicon.source_url | string | yes | Original media source URL |
data.media.favicon.media_type | string | yes | Cached non-SVG image MIME type |
data.media.favicon.size_bytes | integer | yes | Cached media byte size |
data.media.favicon.alt | string | null | yes | Image alt text or preview label |
data.media.screenshot | BookmarkMedia | null | yes | |
data.media.screenshot.id | string | yes | Media cache record ID |
data.media.screenshot.kind | "favicon" | "screenshot" | "image" | yes | Media kind |
data.media.screenshot.url | string | yes | Local daemon media path |
data.media.screenshot.source_url | string | yes | Original media source URL |
data.media.screenshot.media_type | string | yes | Cached non-SVG image MIME type |
data.media.screenshot.size_bytes | integer | yes | Cached media byte size |
data.media.screenshot.alt | string | null | yes | Image alt text or preview label |
data.media.images | array | yes | Cached extracted images |
BookmarkResponse
Single bookmark response
| Field | Type | Required | Description |
|---|---|---|---|
data | Bookmark | yes | |
data.id | string | yes | Bookmark ID |
data.url | string | yes | Original bookmark URL |
data.domain | string | yes | URL hostname |
data.title | string | null | yes | Page title |
data.description | string | null | yes | Page description |
data.status | "saved" | "fetched" | "extracted" | "ai_enriched" | "indexed" | yes | Pipeline status |
data.category_id | string | null | yes | Assigned category ID |
data.favicon_url | string | null | yes | Cached favicon media path or URL |
data.screenshot_url | string | null | yes | Cached page preview media path or URL |
data.is_pinned | 0 | 1 | yes | Pinned flag, 0 or 1; maps Grimoire starred/favorite state |
data.is_archived | 0 | 1 | yes | Archived flag, 0 or 1 |
data.is_trashed | 0 | 1 | yes | Trash flag, 0 or 1 |
data.trashed_at | string | null | yes | Trash timestamp |
data.read_later | 0 | 1 | yes | Read-later flag, 0 or 1 |
data.read_at | string | null | yes | Read timestamp |
data.opened_count | integer | yes | Number of user-triggered opens |
data.last_opened_at | string | null | yes | Most recent user-triggered open timestamp |
data.notes | string | null | yes | Personal notes |
data.created_at | string | yes | Creation timestamp |
data.updated_at | string | yes | Update timestamp |
data.tags | array | yes | Tag names attached to the bookmark |
BookmarkListResponse
Paginated bookmark list
| Field | Type | Required | Description |
|---|---|---|---|
data | array | yes | Page items |
pagination | Pagination | yes | |
pagination.total | integer | yes | Total matching records |
pagination.limit | integer | yes | Applied page size |
pagination.offset | integer | yes | Applied offset |
pagination.has_more | boolean | yes | Whether another page exists |
BookmarkArrayResponse
Bookmark array response
| Field | Type | Required | Description |
|---|---|---|---|
data | array | yes | Bookmarks |
BookmarkAggregateCategory
Category aggregate count under the requested library filter context
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Category ID |
name | string | yes | Category name |
count | integer | yes | Matching active bookmark count |
BookmarkAggregateTag
Tag aggregate count under the requested library filter context
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Tag name |
count | integer | yes | Matching active bookmark count |
BookmarkAggregateDomain
Domain aggregate count under the requested library filter context
| Field | Type | Required | Description |
|---|---|---|---|
domain | string | yes | Domain |
count | integer | yes | Matching active bookmark count |
BookmarkReadAggregate
Read state aggregate counts
| Field | Type | Required | Description |
|---|---|---|---|
read | integer | yes | Matching active bookmarks marked read |
unread | integer | yes | Matching active bookmarks not marked read |
BookmarkPinnedAggregate
Pinned/starred aggregate counts
| Field | Type | Required | Description |
|---|---|---|---|
pinned | integer | yes | Matching active bookmarks pinned/starred |
unpinned | integer | yes | Matching active bookmarks not pinned/starred |
BookmarkReadLaterAggregate
Read-later aggregate counts
| Field | Type | Required | Description |
|---|---|---|---|
yes | integer | yes | Matching active bookmarks marked read-later |
no | integer | yes | Matching active bookmarks not marked read-later |
BookmarkAggregates
Page-independent active-library aggregate counts
| Field | Type | Required | Description |
|---|---|---|---|
total | integer | yes | Total active bookmarks matching the requested library filter context |
categories | array | yes | Category counts |
tags | array | yes | Tag counts |
domains | array | yes | Domain counts |
read | BookmarkReadAggregate | yes | |
read.read | integer | yes | Matching active bookmarks marked read |
read.unread | integer | yes | Matching active bookmarks not marked read |
pinned | BookmarkPinnedAggregate | yes | |
pinned.pinned | integer | yes | Matching active bookmarks pinned/starred |
pinned.unpinned | integer | yes | Matching active bookmarks not pinned/starred |
read_later | BookmarkReadLaterAggregate | yes | |
read_later.yes | integer | yes | Matching active bookmarks marked read-later |
read_later.no | integer | yes | Matching active bookmarks not marked read-later |
BookmarkAggregatesResponse
Bookmark aggregate counts response
| Field | Type | Required | Description |
|---|---|---|---|
data | BookmarkAggregates | yes | |
data.total | integer | yes | Total active bookmarks matching the requested library filter context |
data.categories | array | yes | Category counts |
data.tags | array | yes | Tag counts |
data.domains | array | yes | Domain counts |
data.read | BookmarkReadAggregate | yes | |
data.read.read | integer | yes | Matching active bookmarks marked read |
data.read.unread | integer | yes | Matching active bookmarks not marked read |
data.pinned | BookmarkPinnedAggregate | yes | |
data.pinned.pinned | integer | yes | Matching active bookmarks pinned/starred |
data.pinned.unpinned | integer | yes | Matching active bookmarks not pinned/starred |
data.read_later | BookmarkReadLaterAggregate | yes | |
data.read_later.yes | integer | yes | Matching active bookmarks marked read-later |
data.read_later.no | integer | yes | Matching active bookmarks not marked read-later |
BookmarkCreateRequest
| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | HTTP or HTTPS URL to save |
title | string | no | Optional title override |
CaptureSource
Optional metadata recorded for a local integration capture request
| Field | Type | Required | Description |
|---|---|---|---|
client | string | null | no | Optional local integration client label |
source_url | string | null | no | Optional public HTTP or HTTPS page/context URL |
referrer_url | string | null | no | Optional public HTTP or HTTPS referrer URL |
selected_text | string | null | no | Optional selected text or short capture context |
BookmarkCaptureMetadata
Stored local integration capture metadata
| Field | Type | Required | Description |
|---|---|---|---|
bookmark_id | string | yes | Captured bookmark ID |
source_client | string | null | yes | Local integration client label |
source_url | string | null | yes | Stored source/context URL |
referrer_url | string | null | yes | Stored referrer URL |
selected_text | string | null | yes | Stored selected text or capture context |
captured_at | string | yes | First capture timestamp |
updated_at | string | yes | Most recent metadata update timestamp |
CaptureRequest
Protected one-click capture request for explicit local integrations
| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | HTTP or HTTPS URL to save |
title | string | no | Optional title override |
tags | array | no | Optional replacement tag names |
category_id | string | null | no | Existing category ID to assign |
category | string | no | Root category name to resolve or create when category_id is omitted |
notes | string | null | no | Personal notes, or null to leave empty |
source | CaptureSource | no | |
source.client | string | null | no | Optional local integration client label |
source.source_url | string | null | no | Optional public HTTP or HTTPS page/context URL |
source.referrer_url | string | null | no | Optional public HTTP or HTTPS referrer URL |
source.selected_text | string | null | no | Optional selected text or short capture context |
CaptureResult
One-click capture result
| Field | Type | Required | Description |
|---|---|---|---|
bookmark | Bookmark | yes | |
bookmark.id | string | yes | Bookmark ID |
bookmark.url | string | yes | Original bookmark URL |
bookmark.domain | string | yes | URL hostname |
bookmark.title | string | null | yes | Page title |
bookmark.description | string | null | yes | Page description |
bookmark.status | "saved" | "fetched" | "extracted" | "ai_enriched" | "indexed" | yes | Pipeline status |
bookmark.category_id | string | null | yes | Assigned category ID |
bookmark.favicon_url | string | null | yes | Cached favicon media path or URL |
bookmark.screenshot_url | string | null | yes | Cached page preview media path or URL |
bookmark.is_pinned | 0 | 1 | yes | Pinned flag, 0 or 1; maps Grimoire starred/favorite state |
bookmark.is_archived | 0 | 1 | yes | Archived flag, 0 or 1 |
bookmark.is_trashed | 0 | 1 | yes | Trash flag, 0 or 1 |
bookmark.trashed_at | string | null | yes | Trash timestamp |
bookmark.read_later | 0 | 1 | yes | Read-later flag, 0 or 1 |
bookmark.read_at | string | null | yes | Read timestamp |
bookmark.opened_count | integer | yes | Number of user-triggered opens |
bookmark.last_opened_at | string | null | yes | Most recent user-triggered open timestamp |
bookmark.notes | string | null | yes | Personal notes |
bookmark.created_at | string | yes | Creation timestamp |
bookmark.updated_at | string | yes | Update timestamp |
bookmark.tags | array | yes | Tag names attached to the bookmark |
capture | BookmarkCaptureMetadata | null | yes | |
capture.bookmark_id | string | yes | Captured bookmark ID |
capture.source_client | string | null | yes | Local integration client label |
capture.source_url | string | null | yes | Stored source/context URL |
capture.referrer_url | string | null | yes | Stored referrer URL |
capture.selected_text | string | null | yes | Stored selected text or capture context |
capture.captured_at | string | yes | First capture timestamp |
capture.updated_at | string | yes | Most recent metadata update timestamp |
created | boolean | yes | Whether a new bookmark was created |
job_id | string | null | yes | Queued ingest job ID for new bookmarks |
CaptureResponse
One-click capture response
| Field | Type | Required | Description |
|---|---|---|---|
data | CaptureResult | yes | |
data.bookmark | Bookmark | yes | |
data.bookmark.id | string | yes | Bookmark ID |
data.bookmark.url | string | yes | Original bookmark URL |
data.bookmark.domain | string | yes | URL hostname |
data.bookmark.title | string | null | yes | Page title |
data.bookmark.description | string | null | yes | Page description |
data.bookmark.status | "saved" | "fetched" | "extracted" | "ai_enriched" | "indexed" | yes | Pipeline status |
data.bookmark.category_id | string | null | yes | Assigned category ID |
data.bookmark.favicon_url | string | null | yes | Cached favicon media path or URL |
data.bookmark.screenshot_url | string | null | yes | Cached page preview media path or URL |
data.bookmark.is_pinned | 0 | 1 | yes | Pinned flag, 0 or 1; maps Grimoire starred/favorite state |
data.bookmark.is_archived | 0 | 1 | yes | Archived flag, 0 or 1 |
data.bookmark.is_trashed | 0 | 1 | yes | Trash flag, 0 or 1 |
data.bookmark.trashed_at | string | null | yes | Trash timestamp |
data.bookmark.read_later | 0 | 1 | yes | Read-later flag, 0 or 1 |
data.bookmark.read_at | string | null | yes | Read timestamp |
data.bookmark.opened_count | integer | yes | Number of user-triggered opens |
data.bookmark.last_opened_at | string | null | yes | Most recent user-triggered open timestamp |
data.bookmark.notes | string | null | yes | Personal notes |
data.bookmark.created_at | string | yes | Creation timestamp |
data.bookmark.updated_at | string | yes | Update timestamp |
data.bookmark.tags | array | yes | Tag names attached to the bookmark |
data.capture | BookmarkCaptureMetadata | null | yes | |
data.capture.bookmark_id | string | yes | Captured bookmark ID |
data.capture.source_client | string | null | yes | Local integration client label |
data.capture.source_url | string | null | yes | Stored source/context URL |
data.capture.referrer_url | string | null | yes | Stored referrer URL |
data.capture.selected_text | string | null | yes | Stored selected text or capture context |
data.capture.captured_at | string | yes | First capture timestamp |
data.capture.updated_at | string | yes | Most recent metadata update timestamp |
data.created | boolean | yes | Whether a new bookmark was created |
data.job_id | string | null | yes | Queued ingest job ID for new bookmarks |
BookmarkUpdateRequest
| Field | Type | Required | Description |
|---|---|---|---|
title | string | null | no | New title, or null to clear |
category_id | string | null | no | Category ID, or null to clear |
tags | array | no | Replacement tag names |
is_pinned | integer | no | Pinned flag, 0 or 1; maps Grimoire starred/favorite state |
read_later | integer | no | Read-later flag, 0 or 1 |
is_archived | integer | no | Archived flag, 0 or 1 |
read_at | string | null | no | ISO 8601 date-time, or null to mark unread |
notes | string | null | no | Personal notes, or null to clear |
RelatedBookmarksResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | array | yes | Related bookmarks |
PipelineFailure
Latest actionable pipeline failure for a bookmark
| Field | Type | Required | Description |
|---|---|---|---|
stage | "fetch" | "extract" | "ai_enrich" | "embed" | "index" | yes | Pipeline stage that last reported an actionable failure |
message | string | yes | Failure message safe to show in the local UI |
configuration_related | boolean | yes | Whether the failure likely requires provider settings |
retryable | boolean | yes | Whether retrying the bookmark pipeline is supported |
failed_at | string | yes | Failure timestamp |
dismissed_at | string | null | yes | Dismissal timestamp |
BookmarkPipelineStatus
| Field | Type | Required | Description |
|---|---|---|---|
bookmarkId | string | yes | Bookmark ID |
bookmarkStatus | "saved" | "fetched" | "extracted" | "ai_enriched" | "indexed" | yes | Current bookmark pipeline status |
last_failure | PipelineFailure | null | yes | |
last_failure.stage | "fetch" | "extract" | "ai_enrich" | "embed" | "index" | yes | Pipeline stage that last reported an actionable failure |
last_failure.message | string | yes | Failure message safe to show in the local UI |
last_failure.configuration_related | boolean | yes | Whether the failure likely requires provider settings |
last_failure.retryable | boolean | yes | Whether retrying the bookmark pipeline is supported |
last_failure.failed_at | string | yes | Failure timestamp |
last_failure.dismissed_at | string | null | yes | Dismissal timestamp |
job | object | null | yes | |
job.id | string | yes | Job ID |
job.type | string | yes | Job type |
job.status | "pending" | "running" | "done" | "failed" | yes | Job status |
job.error | string | null | yes | Job error |
job.created_at | string | yes | Job creation timestamp |
job.started_at | string | null | yes | Job start timestamp |
job.finished_at | string | null | yes | Job finish timestamp |
BookmarkPipelineStatusResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | BookmarkPipelineStatus | yes | |
data.bookmarkId | string | yes | Bookmark ID |
data.bookmarkStatus | "saved" | "fetched" | "extracted" | "ai_enriched" | "indexed" | yes | Current bookmark pipeline status |
data.last_failure | PipelineFailure | null | yes | |
data.last_failure.stage | "fetch" | "extract" | "ai_enrich" | "embed" | "index" | yes | Pipeline stage that last reported an actionable failure |
data.last_failure.message | string | yes | Failure message safe to show in the local UI |
data.last_failure.configuration_related | boolean | yes | Whether the failure likely requires provider settings |
data.last_failure.retryable | boolean | yes | Whether retrying the bookmark pipeline is supported |
data.last_failure.failed_at | string | yes | Failure timestamp |
data.last_failure.dismissed_at | string | null | yes | Dismissal timestamp |
data.job | object | null | yes | |
data.job.id | string | yes | Job ID |
data.job.type | string | yes | Job type |
data.job.status | "pending" | "running" | "done" | "failed" | yes | Job status |
data.job.error | string | null | yes | Job error |
data.job.created_at | string | yes | Job creation timestamp |
data.job.started_at | string | null | yes | Job start timestamp |
data.job.finished_at | string | null | yes | Job finish timestamp |
ReprocessRequest
| Field | Type | Required | Description |
|---|---|---|---|
mode | "selected" | "failed_only" | "all" | "embeddings_only" | yes | Reprocess mode |
bookmark_id | string | no | Bookmark ID required when mode is selected |
replace_ai_fields | boolean | no | When true, allow reprocessing to update AI-derived title, category, and tags; manual notes are never overwritten |
ReprocessBatch
| Field | Type | Required | Description |
|---|---|---|---|
batch_id | string | yes | Reprocess batch ID |
mode | "selected" | "failed_only" | "all" | "embeddings_only" | yes | Accepted reprocess mode |
requested | integer | yes | Target bookmarks considered |
enqueued | integer | yes | Jobs enqueued |
skipped | integer | yes | Bookmarks skipped because work is already queued or running |
job_ids | array | yes | Queued job IDs |
status_url | string | null | yes | Batch status URL when jobs were enqueued |
ReprocessBatchResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | ReprocessBatch | yes | |
data.batch_id | string | yes | Reprocess batch ID |
data.mode | "selected" | "failed_only" | "all" | "embeddings_only" | yes | Accepted reprocess mode |
data.requested | integer | yes | Target bookmarks considered |
data.enqueued | integer | yes | Jobs enqueued |
data.skipped | integer | yes | Bookmarks skipped because work is already queued or running |
data.job_ids | array | yes | Queued job IDs |
data.status_url | string | null | yes | Batch status URL when jobs were enqueued |
ReprocessBatchStatus
| Field | Type | Required | Description |
|---|---|---|---|
batch_id | string | yes | Reprocess batch ID |
total | integer | yes | Total jobs in the batch |
pending | integer | yes | Pending jobs |
running | integer | yes | Running jobs |
done | integer | yes | Completed jobs |
failed | integer | yes | Failed jobs |
ReprocessBatchStatusResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | ReprocessBatchStatus | yes | |
data.batch_id | string | yes | Reprocess batch ID |
data.total | integer | yes | Total jobs in the batch |
data.pending | integer | yes | Pending jobs |
data.running | integer | yes | Running jobs |
data.done | integer | yes | Completed jobs |
data.failed | integer | yes | Failed jobs |
SearchResultItem
Bookmark search hit
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Bookmark ID |
url | string | yes | Original bookmark URL |
domain | string | yes | URL hostname |
title | string | null | yes | Page title |
description | string | null | yes | Page description |
status | "saved" | "fetched" | "extracted" | "ai_enriched" | "indexed" | yes | Pipeline status |
category_id | string | null | yes | Assigned category ID |
favicon_url | string | null | yes | Cached favicon media path or URL |
screenshot_url | string | null | yes | Cached page preview media path or URL |
is_pinned | 0 | 1 | yes | Pinned flag, 0 or 1; maps Grimoire starred/favorite state |
is_archived | 0 | 1 | yes | Archived flag, 0 or 1 |
is_trashed | 0 | 1 | yes | Trash flag, 0 or 1 |
trashed_at | string | null | yes | Trash timestamp |
read_later | 0 | 1 | yes | Read-later flag, 0 or 1 |
read_at | string | null | yes | Read timestamp |
opened_count | integer | yes | Number of user-triggered opens |
last_opened_at | string | null | yes | Most recent user-triggered open timestamp |
notes | string | null | yes | Personal notes |
created_at | string | yes | Creation timestamp |
updated_at | string | yes | Update timestamp |
tags | array | yes | Tag names attached to the bookmark |
snippet | string | null | yes | Highlighted search excerpt |
rank | number | null | yes | Search rank or hybrid score |
SearchResponse
| Field | Type | Required | Description |
|---|---|---|---|
data | array | yes | Search hits |
pagination | Pagination | yes | |
pagination.total | integer | yes | Total matching records |
pagination.limit | integer | yes | Applied page size |
pagination.offset | integer | yes | Applied offset |
pagination.has_more | boolean | yes | Whether another page exists |
meta | object | yes | |
meta.mode | "keyword" | "semantic" | "hybrid" | yes | Applied search mode |
CategoryRecord
Category row returned by create and update endpoints
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Category ID |
name | string | yes | Category name |
parent_id | string | null | yes | Parent category ID |
color | string | null | yes | Optional category hex color |
icon | string | null | yes | Optional lowercase icon token |
description | string | null | yes | Optional category description |
slug | string | null | yes | Optional category slug |
is_archived | 0 | 1 | yes | Archived metadata flag, 0 or 1 |
is_public | 0 | 1 | yes | Public visibility metadata flag, 0 or 1; local metadata only and does not expose data |
created_at | string | yes | Creation timestamp |
updated_at | string | yes | Update timestamp |
CategoryWithCount
Category row with active bookmark count returned by category listings
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Category ID |
name | string | yes | Category name |
parent_id | string | null | yes | Parent category ID |
color | string | null | yes | Optional category hex color |
icon | string | null | yes | Optional lowercase icon token |
description | string | null | yes | Optional category description |
slug | string | null | yes | Optional category slug |
is_archived | 0 | 1 | yes | Archived metadata flag, 0 or 1 |
is_public | 0 | 1 | yes | Public visibility metadata flag, 0 or 1; local metadata only and does not expose data |
created_at | string | yes | Creation timestamp |
updated_at | string | yes | Update timestamp |
bookmark_count | integer | yes | Active bookmark count |
CategoryNode
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Category ID |
name | string | yes | Category name |
parent_id | string | null | yes | Parent category ID |
color | string | null | yes | Optional category hex color |
icon | string | null | yes | Optional lowercase icon token |
description | string | null | yes | Optional category description |
slug | string | null | yes | Optional category slug |
is_archived | 0 | 1 | yes | Archived metadata flag, 0 or 1 |
is_public | 0 | 1 | yes | Public visibility metadata flag, 0 or 1; local metadata only and does not expose data |
created_at | string | yes | Creation timestamp |
updated_at | string | yes | Update timestamp |
bookmark_count | integer | yes | Active bookmark count |
children | array | yes | Child categories |
CategoryRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Category name |
parent_id | string | null | no | Parent category ID |
color | string | null | no | Optional category hex color |
icon | string | null | no | Optional lowercase icon token |
description | string | null | no | Optional category description |
slug | string | null | no | Optional category slug |
is_archived | 0 | 1 | no | Archived metadata flag, 0 or 1 |
is_public | 0 | 1 | no | Public visibility metadata flag, 0 or 1; local metadata only and does not expose data |
CategoryPatchRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | Category name |
parent_id | string | null | no | Parent category ID |
color | string | null | no | Optional category hex color |
icon | string | null | no | Optional lowercase icon token |
description | string | null | no | Optional category description |
slug | string | null | no | Optional category slug |
is_archived | 0 | 1 | no | Archived metadata flag, 0 or 1 |
is_public | 0 | 1 | no | Public visibility metadata flag, 0 or 1; local metadata only and does not expose data |
CategoryTreeResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | array | yes | Category tree |
CategoryResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | CategoryRecord | yes | |
data.id | string | yes | Category ID |
data.name | string | yes | Category name |
data.parent_id | string | null | yes | Parent category ID |
data.color | string | null | yes | Optional category hex color |
data.icon | string | null | yes | Optional lowercase icon token |
data.description | string | null | yes | Optional category description |
data.slug | string | null | yes | Optional category slug |
data.is_archived | 0 | 1 | yes | Archived metadata flag, 0 or 1 |
data.is_public | 0 | 1 | yes | Public visibility metadata flag, 0 or 1; local metadata only and does not expose data |
data.created_at | string | yes | Creation timestamp |
data.updated_at | string | yes | Update timestamp |
TagRecord
Tag row returned by create and attach endpoints
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Tag ID |
name | string | yes | Tag name |
created_at | string | yes | Creation timestamp |
TagWithCount
Tag row with active bookmark count returned by tag listings
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Tag ID |
name | string | yes | Tag name |
created_at | string | yes | Creation timestamp |
bookmark_count | integer | yes | Active bookmark count |
TagRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Tag name, normalized to lowercase |
TagListResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | array | yes | Tags |
TagResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | TagRecord | yes | |
data.id | string | yes | Tag ID |
data.name | string | yes | Tag name |
data.created_at | string | yes | Creation timestamp |
Domain
| Field | Type | Required | Description |
|---|---|---|---|
domain | string | yes | Domain |
count | integer | yes | Active bookmark count |
DomainListResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | array | yes | Domains |
ImportDuplicatePolicy
Duplicate handling policy applied to an import preview or commit
| Field | Type | Required | Description |
|---|---|---|---|
active | "skip" | "merge" | yes | Policy for active duplicate URLs |
archived | "skip" | "restore_merge" | yes | Policy for archived duplicate URLs |
trashed | "skip" | "restore_merge" | yes | Policy for trashed duplicate URLs |
ImportFolderRemappingInput
Import folder remapping request entry
| Field | Type | Required | Description |
|---|---|---|---|
sourcePath | array | yes | Folder path from the imported file |
action | "create" | "existing" | yes | Folder remapping action. Use create with targetPath or existing with categoryId. |
categoryId | string | no | Existing category ID; required when action is existing |
targetPath | array | no | Target path for create/reuse mappings. Child folders inherit remapped ancestor paths unless explicitly mapped. |
ImportTagRemappingInput
Import tag remapping request entry
| Field | Type | Required | Description |
|---|---|---|---|
sourceTag | string | yes | Source tag name from the imported file |
action | "new" | "existing" | "renamed" | "skipped" | yes | Tag remapping action. Use tagId for existing, targetName for new or renamed. |
tagId | string | no | Existing tag ID; required when action is existing |
targetName | string | no | Target tag name for new or renamed mappings; lowercase hyphen format, max 50 characters |
ImportRemappingInput
Optional import remapping request JSON. Omitted folders and tags use the daemon's default create/reuse decisions.
| Field | Type | Required | Description |
|---|---|---|---|
folders | array | no | Folder remapping overrides |
tags | array | no | Tag remapping overrides |
ImportFolderMapping
Resolved import folder remapping decision
| Field | Type | Required | Description |
|---|---|---|---|
sourcePath | array | yes | Folder path from the imported file |
action | "create" | "existing" | yes | Folder remapping action |
targetCategoryId | string | null | yes | Existing target category ID when mapped to an existing category |
targetPath | array | yes | Resolved target category path |
status | "new" | "existing" | yes | Whether the target category path already exists or will be created |
ImportTagMapping
Resolved import tag remapping decision
| Field | Type | Required | Description |
|---|---|---|---|
sourceTag | string | yes | Source tag name from the imported file |
action | "new" | "existing" | "renamed" | "skipped" | yes | Tag remapping action |
targetTagId | string | null | yes | Existing target tag ID when reused |
targetName | string | null | yes | Resolved target tag name; null when skipped |
status | "new" | "existing" | "skipped" | yes | Whether the target tag exists, will be created, or is skipped |
ImportRemapping
Resolved category and tag remapping decisions applied to an import preview or commit
| Field | Type | Required | Description |
|---|---|---|---|
folders | array | yes | Resolved folder remapping decisions |
tags | array | yes | Resolved tag remapping decisions |
ImportPreviewSummary
| Field | Type | Required | Description |
|---|---|---|---|
totalRows | integer | yes | Total parsed bookmark rows, including skipped invalid/private rows |
importableRows | integer | yes | Valid public HTTP(S) bookmark rows |
new | integer | yes | Rows that would create new bookmarks |
activeDuplicates | integer | yes | Rows matching active bookmarks |
archivedDuplicates | integer | yes | Rows matching archived bookmarks |
trashedDuplicates | integer | yes | Rows matching trashed bookmarks |
invalidUrls | integer | yes | Rows skipped because the URL is malformed or not HTTP(S) |
privateUrls | integer | yes | Rows skipped because the URL targets a private or loopback host |
created | integer | yes | Estimated rows created under the selected policy |
merged | integer | yes | Estimated active duplicate rows merged under the selected policy |
restored | integer | yes | Estimated archived or trashed duplicate rows restored and merged |
skipped | integer | yes | Estimated rows skipped under the selected policy |
ImportPreviewRow
| Field | Type | Required | Description |
|---|---|---|---|
classification | "new" | "active_duplicate" | "archived_duplicate" | "trashed_duplicate" | "invalid_url" | "private_url" | yes | Import row classification |
action | "create" | "skip" | "merge" | "restore_merge" | yes | Action that the selected policy would apply |
url | string | null | yes | Source bookmark URL |
title | string | yes | Source bookmark title |
notes | string | null | yes | Source note text when the import format provides note-like metadata |
tags | array | yes | Source tag names |
targetTags | array | yes | Target tag names after remapping |
folders | array | yes | Source folder path |
targetCategoryId | string | null | yes | Mapped target category ID when it already exists |
targetCategoryPath | array | yes | Target category path after remapping |
existingBookmarkId | string | null | yes | Matching existing bookmark ID |
existingState | "active" | "archived" | "trashed" | null | yes | Matching existing bookmark state |
skipReason | string | null | yes | Reason the row would be skipped |
ImportPreview
Non-mutating import preview
| Field | Type | Required | Description |
|---|---|---|---|
duplicatePolicy | ImportDuplicatePolicy | yes | |
duplicatePolicy.active | "skip" | "merge" | yes | Policy for active duplicate URLs |
duplicatePolicy.archived | "skip" | "restore_merge" | yes | Policy for archived duplicate URLs |
duplicatePolicy.trashed | "skip" | "restore_merge" | yes | Policy for trashed duplicate URLs |
remapping | ImportRemapping | yes | |
remapping.folders | array | yes | Resolved folder remapping decisions |
remapping.tags | array | yes | Resolved tag remapping decisions |
summary | ImportPreviewSummary | yes | |
summary.totalRows | integer | yes | Total parsed bookmark rows, including skipped invalid/private rows |
summary.importableRows | integer | yes | Valid public HTTP(S) bookmark rows |
summary.new | integer | yes | Rows that would create new bookmarks |
summary.activeDuplicates | integer | yes | Rows matching active bookmarks |
summary.archivedDuplicates | integer | yes | Rows matching archived bookmarks |
summary.trashedDuplicates | integer | yes | Rows matching trashed bookmarks |
summary.invalidUrls | integer | yes | Rows skipped because the URL is malformed or not HTTP(S) |
summary.privateUrls | integer | yes | Rows skipped because the URL targets a private or loopback host |
summary.created | integer | yes | Estimated rows created under the selected policy |
summary.merged | integer | yes | Estimated active duplicate rows merged under the selected policy |
summary.restored | integer | yes | Estimated archived or trashed duplicate rows restored and merged |
summary.skipped | integer | yes | Estimated rows skipped under the selected policy |
folders | array<array | yes | Detected Netscape folder paths |
tags | array | yes | Detected tag names |
warnings | array | yes | Parser warnings |
rows | array | yes | Preview rows |
ImportPreviewResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | ImportPreview | yes | |
data.duplicatePolicy | ImportDuplicatePolicy | yes | |
data.duplicatePolicy.active | "skip" | "merge" | yes | Policy for active duplicate URLs |
data.duplicatePolicy.archived | "skip" | "restore_merge" | yes | Policy for archived duplicate URLs |
data.duplicatePolicy.trashed | "skip" | "restore_merge" | yes | Policy for trashed duplicate URLs |
data.remapping | ImportRemapping | yes | |
data.remapping.folders | array | yes | Resolved folder remapping decisions |
data.remapping.tags | array | yes | Resolved tag remapping decisions |
data.summary | ImportPreviewSummary | yes | |
data.summary.totalRows | integer | yes | Total parsed bookmark rows, including skipped invalid/private rows |
data.summary.importableRows | integer | yes | Valid public HTTP(S) bookmark rows |
data.summary.new | integer | yes | Rows that would create new bookmarks |
data.summary.activeDuplicates | integer | yes | Rows matching active bookmarks |
data.summary.archivedDuplicates | integer | yes | Rows matching archived bookmarks |
data.summary.trashedDuplicates | integer | yes | Rows matching trashed bookmarks |
data.summary.invalidUrls | integer | yes | Rows skipped because the URL is malformed or not HTTP(S) |
data.summary.privateUrls | integer | yes | Rows skipped because the URL targets a private or loopback host |
data.summary.created | integer | yes | Estimated rows created under the selected policy |
data.summary.merged | integer | yes | Estimated active duplicate rows merged under the selected policy |
data.summary.restored | integer | yes | Estimated archived or trashed duplicate rows restored and merged |
data.summary.skipped | integer | yes | Estimated rows skipped under the selected policy |
data.folders | array<array | yes | Detected Netscape folder paths |
data.tags | array | yes | Detected tag names |
data.warnings | array | yes | Parser warnings |
data.rows | array | yes | Preview rows |
ImportResultSummary
Final committed import result counts
| Field | Type | Required | Description |
|---|---|---|---|
totalRows | integer | yes | Total parsed bookmark rows, including skipped invalid/private rows |
importableRows | integer | yes | Valid public HTTP(S) bookmark rows |
created | integer | yes | Bookmarks created by the committed import |
updated | integer | yes | Existing bookmarks updated by merge or restore actions |
merged | integer | yes | Active duplicate bookmarks merged by the committed import |
restored | integer | yes | Archived or trashed duplicate bookmarks restored and merged |
skipped | integer | yes | Rows skipped by validation or duplicate policy |
failed | integer | yes | Rows that failed during commit after import processing started |
warnings | integer | yes | Parser and row-level warnings included in the result report |
categoriesCreated | integer | yes | Categories created from imported folder paths |
categoriesReused | integer | yes | Existing categories reused for imported folder paths |
ImportResultRow
Final committed import row result
| Field | Type | Required | Description |
|---|---|---|---|
status | "created" | "merged" | "restored" | "skipped" | "failed" | yes | Final committed row status |
action | "create" | "skip" | "merge" | "restore_merge" | yes | Requested action selected by the duplicate policy |
classification | "new" | "active_duplicate" | "archived_duplicate" | "trashed_duplicate" | "invalid_url" | "private_url" | yes | Import row classification |
url | string | null | yes | Source bookmark URL |
title | string | yes | Source bookmark title |
notes | string | null | yes | Source note text when the import format provides note-like metadata |
tags | array | yes | Source tag names |
targetTags | array | yes | Target tag names after remapping |
folders | array | yes | Source folder path |
targetCategoryId | string | null | yes | Mapped target category ID when it already exists |
targetCategoryPath | array | yes | Target category path after remapping |
existingBookmarkId | string | null | yes | Matching existing bookmark ID from preview analysis |
bookmarkId | string | null | yes | Bookmark ID created or updated by the committed import row |
skipReason | string | null | yes | Reason the row was skipped |
warning | string | null | yes | User-visible row warning, duplicate note, remapping note, or skipped-row reason |
error | string | null | yes | User-visible row error when commit processing failed for this row |
ImportResultReport
Final import result report available when progress is done
| Field | Type | Required | Description |
|---|---|---|---|
duplicatePolicy | ImportDuplicatePolicy | yes | |
duplicatePolicy.active | "skip" | "merge" | yes | Policy for active duplicate URLs |
duplicatePolicy.archived | "skip" | "restore_merge" | yes | Policy for archived duplicate URLs |
duplicatePolicy.trashed | "skip" | "restore_merge" | yes | Policy for trashed duplicate URLs |
remapping | ImportRemapping | yes | |
remapping.folders | array | yes | Resolved folder remapping decisions |
remapping.tags | array | yes | Resolved tag remapping decisions |
summary | ImportResultSummary | yes | |
summary.totalRows | integer | yes | Total parsed bookmark rows, including skipped invalid/private rows |
summary.importableRows | integer | yes | Valid public HTTP(S) bookmark rows |
summary.created | integer | yes | Bookmarks created by the committed import |
summary.updated | integer | yes | Existing bookmarks updated by merge or restore actions |
summary.merged | integer | yes | Active duplicate bookmarks merged by the committed import |
summary.restored | integer | yes | Archived or trashed duplicate bookmarks restored and merged |
summary.skipped | integer | yes | Rows skipped by validation or duplicate policy |
summary.failed | integer | yes | Rows that failed during commit after import processing started |
summary.warnings | integer | yes | Parser and row-level warnings included in the result report |
summary.categoriesCreated | integer | yes | Categories created from imported folder paths |
summary.categoriesReused | integer | yes | Existing categories reused for imported folder paths |
warnings | array | yes | Parser warnings |
rows | array | yes | Committed row results |
ImportSummary
| Field | Type | Required | Description |
|---|---|---|---|
importId | string | yes | Import ID for progress stream |
total | integer | yes | Parsed bookmark row count |
folders | integer | yes | Parsed Netscape folder count |
warnings | integer | yes | Parser warning count |
duplicatePolicy | ImportDuplicatePolicy | yes | |
duplicatePolicy.active | "skip" | "merge" | yes | Policy for active duplicate URLs |
duplicatePolicy.archived | "skip" | "restore_merge" | yes | Policy for archived duplicate URLs |
duplicatePolicy.trashed | "skip" | "restore_merge" | yes | Policy for trashed duplicate URLs |
remapping | ImportRemapping | yes | |
remapping.folders | array | yes | Resolved folder remapping decisions |
remapping.tags | array | yes | Resolved tag remapping decisions |
progressUrl | string | yes | SSE progress URL |
ImportSummaryResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | ImportSummary | yes | |
data.importId | string | yes | Import ID for progress stream |
data.total | integer | yes | Parsed bookmark row count |
data.folders | integer | yes | Parsed Netscape folder count |
data.warnings | integer | yes | Parser warning count |
data.duplicatePolicy | ImportDuplicatePolicy | yes | |
data.duplicatePolicy.active | "skip" | "merge" | yes | Policy for active duplicate URLs |
data.duplicatePolicy.archived | "skip" | "restore_merge" | yes | Policy for archived duplicate URLs |
data.duplicatePolicy.trashed | "skip" | "restore_merge" | yes | Policy for trashed duplicate URLs |
data.remapping | ImportRemapping | yes | |
data.remapping.folders | array | yes | Resolved folder remapping decisions |
data.remapping.tags | array | yes | Resolved tag remapping decisions |
data.progressUrl | string | yes | SSE progress URL |
ImportProgressEvent
| Field | Type | Required | Description |
|---|---|---|---|
queued | integer | yes | Queued bookmarks |
skipped | integer | yes | Skipped bookmarks |
merged | integer | yes | Existing active bookmarks merged |
restored | integer | yes | Existing archived or trashed bookmarks restored and merged |
failed | integer | yes | Rows that failed during commit after import processing started |
total | integer | yes | Total parsed bookmarks |
folders | integer | yes | Total parsed Netscape folders |
categoriesCreated | integer | yes | Categories created from imported folder paths |
categoriesReused | integer | yes | Existing categories reused for imported folder paths |
done | boolean | yes | Whether import processing is complete |
error | string | null | yes | Background import error |
result | ImportResultReport | null | yes | |
result.duplicatePolicy | ImportDuplicatePolicy | yes | |
result.duplicatePolicy.active | "skip" | "merge" | yes | Policy for active duplicate URLs |
result.duplicatePolicy.archived | "skip" | "restore_merge" | yes | Policy for archived duplicate URLs |
result.duplicatePolicy.trashed | "skip" | "restore_merge" | yes | Policy for trashed duplicate URLs |
result.remapping | ImportRemapping | yes | |
result.remapping.folders | array | yes | Resolved folder remapping decisions |
result.remapping.tags | array | yes | Resolved tag remapping decisions |
result.summary | ImportResultSummary | yes | |
result.summary.totalRows | integer | yes | Total parsed bookmark rows, including skipped invalid/private rows |
result.summary.importableRows | integer | yes | Valid public HTTP(S) bookmark rows |
result.summary.created | integer | yes | Bookmarks created by the committed import |
result.summary.updated | integer | yes | Existing bookmarks updated by merge or restore actions |
result.summary.merged | integer | yes | Active duplicate bookmarks merged by the committed import |
result.summary.restored | integer | yes | Archived or trashed duplicate bookmarks restored and merged |
result.summary.skipped | integer | yes | Rows skipped by validation or duplicate policy |
result.summary.failed | integer | yes | Rows that failed during commit after import processing started |
result.summary.warnings | integer | yes | Parser and row-level warnings included in the result report |
result.summary.categoriesCreated | integer | yes | Categories created from imported folder paths |
result.summary.categoriesReused | integer | yes | Existing categories reused for imported folder paths |
result.warnings | array | yes | Parser warnings |
result.rows | array | yes | Committed row results |
RuntimeLlmCapability
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | yes | Whether this runtime feature is usable |
provider | "openai" | "ollama" | "anthropic" | "openrouter" | "openai_compatible" | "deepseek" | "none" | yes | Resolved provider |
model | string | null | yes | Resolved model |
base_url | string | null | yes | Resolved base URL |
RuntimeEmbeddingCapability
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | yes | Whether this runtime feature is usable |
provider | "openai" | "ollama" | "openai_compatible" | "none" | yes | Resolved embedding provider |
model | string | null | yes | Resolved model |
base_url | string | null | yes | Resolved base URL |
RuntimeCapabilities
| Field | Type | Required | Description |
|---|---|---|---|
llm | RuntimeLlmCapability | yes | |
llm.enabled | boolean | yes | Whether this runtime feature is usable |
llm.provider | "openai" | "ollama" | "anthropic" | "openrouter" | "openai_compatible" | "deepseek" | "none" | yes | Resolved provider |
llm.model | string | null | yes | Resolved model |
llm.base_url | string | null | yes | Resolved base URL |
embeddings | RuntimeEmbeddingCapability | yes | |
embeddings.enabled | boolean | yes | Whether this runtime feature is usable |
embeddings.provider | "openai" | "ollama" | "openai_compatible" | "none" | yes | Resolved embedding provider |
embeddings.model | string | null | yes | Resolved model |
embeddings.base_url | string | null | yes | Resolved base URL |
capabilities | object | yes | |
capabilities.enrichment | boolean | yes | LLM enrichment available |
capabilities.semantic_search | boolean | yes | Semantic search available |
capabilities.related_bookmarks | boolean | yes | Related bookmarks available |
capabilities.organization_agent | boolean | yes | Organization agent available |
SettingsBackupSchedule
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | yes | Enable scheduled snapshots |
cron | string | yes | Five-part cron expression |
retention_count | integer | yes | Number of local snapshots to retain |
Settings
| Field | Type | Required | Description |
|---|---|---|---|
ai | object | yes | |
ai.provider | "openai" | "ollama" | "anthropic" | "openrouter" | "openai_compatible" | "deepseek" | "none" | yes | LLM provider |
ai.openai | object | yes | |
ai.openai.api_key | string | yes | Redacted OpenAI API key. Empty string means unset |
ai.openai.model | string | yes | OpenAI chat model |
ai.ollama | object | yes | |
ai.ollama.base_url | string | yes | Ollama base URL |
ai.ollama.model | string | yes | Ollama model |
ai.anthropic | object | yes | |
ai.anthropic.api_key | string | yes | Redacted Anthropic API key. Empty string means unset |
ai.anthropic.base_url | string | yes | Anthropic API base URL |
ai.anthropic.model | string | yes | Anthropic Messages API model |
ai.openrouter | object | yes | |
ai.openrouter.api_key | string | yes | Redacted OpenRouter API key. Empty string means unset |
ai.openrouter.base_url | string | yes | OpenRouter OpenAI-compatible base URL |
ai.openrouter.model | string | yes | OpenRouter model slug |
ai.openai_compatible | object | yes | |
ai.openai_compatible.api_key | string | yes | Redacted custom OpenAI-compatible API key. Empty string means unset |
ai.openai_compatible.base_url | string | yes | Custom OpenAI-compatible chat base URL |
ai.openai_compatible.model | string | yes | Custom OpenAI-compatible chat model |
ai.deepseek | object | yes | |
ai.deepseek.api_key | string | yes | Redacted DeepSeek API key. Empty string means unset |
ai.deepseek.base_url | string | yes | DeepSeek OpenAI-compatible base URL |
ai.deepseek.model | string | yes | DeepSeek chat model |
ai.embeddings | object | yes | |
ai.embeddings.provider | "openai" | "ollama" | "openai_compatible" | yes | Embedding provider |
ai.embeddings.model | string | yes | Embedding model |
ai.embeddings.openai_compatible | object | yes | |
ai.embeddings.openai_compatible.api_key | string | yes | Redacted custom OpenAI-compatible embedding API key. Empty string means unset |
ai.embeddings.openai_compatible.base_url | string | yes | Custom OpenAI-compatible embeddings base URL |
ai.embeddings.openai_compatible.model | string | yes | Custom OpenAI-compatible embedding model |
app | object | yes | |
app.autostart | boolean | yes | Start daemon automatically |
app.theme | "light" | "dark" | "system" | yes | UI theme |
app.lock | object | yes | |
app.lock.enabled | boolean | yes | Whether app lock is enabled |
app.lock.pin_hash | string | yes | Redacted PIN hash. Empty string means unset |
backup | object | yes | |
backup.local | object | yes | |
backup.local.destination_path | string | yes | Absolute custom backup destination, or empty string for default |
backup.schedule | SettingsBackupSchedule | yes | |
backup.schedule.enabled | boolean | yes | Enable scheduled snapshots |
backup.schedule.cron | string | yes | Five-part cron expression |
backup.schedule.retention_count | integer | yes | Number of local snapshots to retain |
backup.s3 | object | yes | |
backup.s3.endpoint | string | yes | S3-compatible endpoint URL, or empty string for AWS |
backup.s3.bucket | string | yes | S3 bucket |
backup.s3.access_key | string | yes | Redacted S3 access key. Empty string means unset |
backup.s3.secret_key | string | yes | Redacted S3 secret key. Empty string means unset |
backup.s3.region | string | yes | S3 region |
backup.s3.prefix | string | yes | Object key prefix |
runtime | RuntimeCapabilities | yes | |
runtime.llm | RuntimeLlmCapability | yes | |
runtime.llm.enabled | boolean | yes | Whether this runtime feature is usable |
runtime.llm.provider | "openai" | "ollama" | "anthropic" | "openrouter" | "openai_compatible" | "deepseek" | "none" | yes | Resolved provider |
runtime.llm.model | string | null | yes | Resolved model |
runtime.llm.base_url | string | null | yes | Resolved base URL |
runtime.embeddings | RuntimeEmbeddingCapability | yes | |
runtime.embeddings.enabled | boolean | yes | Whether this runtime feature is usable |
runtime.embeddings.provider | "openai" | "ollama" | "openai_compatible" | "none" | yes | Resolved embedding provider |
runtime.embeddings.model | string | null | yes | Resolved model |
runtime.embeddings.base_url | string | null | yes | Resolved base URL |
runtime.capabilities | object | yes | |
runtime.capabilities.enrichment | boolean | yes | LLM enrichment available |
runtime.capabilities.semantic_search | boolean | yes | Semantic search available |
runtime.capabilities.related_bookmarks | boolean | yes | Related bookmarks available |
runtime.capabilities.organization_agent | boolean | yes | Organization agent available |
SettingsPatch
| Field | Type | Required | Description |
|---|---|---|---|
ai | object | no | |
ai.provider | "openai" | "ollama" | "anthropic" | "openrouter" | "openai_compatible" | "deepseek" | "none" | no | LLM provider |
ai.openai | object | no | |
ai.openai.api_key | string | no | OpenAI API key, empty string clears it |
ai.openai.model | string | no | OpenAI chat model |
ai.ollama | object | no | |
ai.ollama.base_url | string | no | Ollama base URL |
ai.ollama.model | string | no | Ollama model |
ai.anthropic | object | no | |
ai.anthropic.api_key | string | no | Anthropic API key, empty string clears it |
ai.anthropic.base_url | string | no | Anthropic API base URL |
ai.anthropic.model | string | no | Anthropic Messages API model |
ai.openrouter | object | no | |
ai.openrouter.api_key | string | no | OpenRouter API key, empty string clears it |
ai.openrouter.base_url | string | no | OpenRouter OpenAI-compatible base URL |
ai.openrouter.model | string | no | OpenRouter model slug |
ai.openai_compatible | object | no | |
ai.openai_compatible.api_key | string | no | Custom OpenAI-compatible API key, empty string clears it |
ai.openai_compatible.base_url | string | no | Custom OpenAI-compatible chat base URL |
ai.openai_compatible.model | string | no | Custom OpenAI-compatible chat model |
ai.deepseek | object | no | |
ai.deepseek.api_key | string | no | DeepSeek API key, empty string clears it |
ai.deepseek.base_url | string | no | DeepSeek OpenAI-compatible base URL |
ai.deepseek.model | string | no | DeepSeek chat model |
ai.embeddings | object | no | |
ai.embeddings.provider | "openai" | "ollama" | "openai_compatible" | no | Embedding provider |
ai.embeddings.model | string | no | Embedding model |
ai.embeddings.openai_compatible | object | no | |
ai.embeddings.openai_compatible.api_key | string | no | Custom OpenAI-compatible embedding API key, empty string clears it |
ai.embeddings.openai_compatible.base_url | string | no | Custom OpenAI-compatible embeddings base URL |
ai.embeddings.openai_compatible.model | string | no | Custom OpenAI-compatible embedding model |
app | object | no | |
app.autostart | boolean | no | Start daemon automatically |
app.theme | "light" | "dark" | "system" | no | UI theme |
app.lock | object | no | |
app.lock.enabled | boolean | no | Whether app lock is enabled |
app.lock.pin_hash | string | no | PIN hash, empty string clears it |
backup | object | no | |
backup.local | object | no | |
backup.local.destination_path | string | no | Absolute custom backup destination, or empty string for default |
backup.schedule | object | no | |
backup.schedule.enabled | boolean | no | Enable scheduled snapshots |
backup.schedule.cron | string | no | Five-part cron expression |
backup.schedule.retention_count | integer | no | Number of local snapshots to retain |
backup.s3 | object | no | |
backup.s3.endpoint | string | no | S3-compatible endpoint URL, or empty string for AWS |
backup.s3.bucket | string | no | S3 bucket |
backup.s3.access_key | string | no | S3 access key |
backup.s3.secret_key | string | no | S3 secret key |
backup.s3.region | string | no | S3 region |
backup.s3.prefix | string | no | Object key prefix |
SettingsResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | Settings | yes | |
data.ai | object | yes | |
data.ai.provider | "openai" | "ollama" | "anthropic" | "openrouter" | "openai_compatible" | "deepseek" | "none" | yes | LLM provider |
data.ai.openai | object | yes | |
data.ai.openai.api_key | string | yes | Redacted OpenAI API key. Empty string means unset |
data.ai.openai.model | string | yes | OpenAI chat model |
data.ai.ollama | object | yes | |
data.ai.ollama.base_url | string | yes | Ollama base URL |
data.ai.ollama.model | string | yes | Ollama model |
data.ai.anthropic | object | yes | |
data.ai.anthropic.api_key | string | yes | Redacted Anthropic API key. Empty string means unset |
data.ai.anthropic.base_url | string | yes | Anthropic API base URL |
data.ai.anthropic.model | string | yes | Anthropic Messages API model |
data.ai.openrouter | object | yes | |
data.ai.openrouter.api_key | string | yes | Redacted OpenRouter API key. Empty string means unset |
data.ai.openrouter.base_url | string | yes | OpenRouter OpenAI-compatible base URL |
data.ai.openrouter.model | string | yes | OpenRouter model slug |
data.ai.openai_compatible | object | yes | |
data.ai.openai_compatible.api_key | string | yes | Redacted custom OpenAI-compatible API key. Empty string means unset |
data.ai.openai_compatible.base_url | string | yes | Custom OpenAI-compatible chat base URL |
data.ai.openai_compatible.model | string | yes | Custom OpenAI-compatible chat model |
data.ai.deepseek | object | yes | |
data.ai.deepseek.api_key | string | yes | Redacted DeepSeek API key. Empty string means unset |
data.ai.deepseek.base_url | string | yes | DeepSeek OpenAI-compatible base URL |
data.ai.deepseek.model | string | yes | DeepSeek chat model |
data.ai.embeddings | object | yes | |
data.ai.embeddings.provider | "openai" | "ollama" | "openai_compatible" | yes | Embedding provider |
data.ai.embeddings.model | string | yes | Embedding model |
data.ai.embeddings.openai_compatible | object | yes | |
data.ai.embeddings.openai_compatible.api_key | string | yes | Redacted custom OpenAI-compatible embedding API key. Empty string means unset |
data.ai.embeddings.openai_compatible.base_url | string | yes | Custom OpenAI-compatible embeddings base URL |
data.ai.embeddings.openai_compatible.model | string | yes | Custom OpenAI-compatible embedding model |
data.app | object | yes | |
data.app.autostart | boolean | yes | Start daemon automatically |
data.app.theme | "light" | "dark" | "system" | yes | UI theme |
data.app.lock | object | yes | |
data.app.lock.enabled | boolean | yes | Whether app lock is enabled |
data.app.lock.pin_hash | string | yes | Redacted PIN hash. Empty string means unset |
data.backup | object | yes | |
data.backup.local | object | yes | |
data.backup.local.destination_path | string | yes | Absolute custom backup destination, or empty string for default |
data.backup.schedule | SettingsBackupSchedule | yes | |
data.backup.schedule.enabled | boolean | yes | Enable scheduled snapshots |
data.backup.schedule.cron | string | yes | Five-part cron expression |
data.backup.schedule.retention_count | integer | yes | Number of local snapshots to retain |
data.backup.s3 | object | yes | |
data.backup.s3.endpoint | string | yes | S3-compatible endpoint URL, or empty string for AWS |
data.backup.s3.bucket | string | yes | S3 bucket |
data.backup.s3.access_key | string | yes | Redacted S3 access key. Empty string means unset |
data.backup.s3.secret_key | string | yes | Redacted S3 secret key. Empty string means unset |
data.backup.s3.region | string | yes | S3 region |
data.backup.s3.prefix | string | yes | Object key prefix |
data.runtime | RuntimeCapabilities | yes | |
data.runtime.llm | RuntimeLlmCapability | yes | |
data.runtime.llm.enabled | boolean | yes | Whether this runtime feature is usable |
data.runtime.llm.provider | "openai" | "ollama" | "anthropic" | "openrouter" | "openai_compatible" | "deepseek" | "none" | yes | Resolved provider |
data.runtime.llm.model | string | null | yes | Resolved model |
data.runtime.llm.base_url | string | null | yes | Resolved base URL |
data.runtime.embeddings | RuntimeEmbeddingCapability | yes | |
data.runtime.embeddings.enabled | boolean | yes | Whether this runtime feature is usable |
data.runtime.embeddings.provider | "openai" | "ollama" | "openai_compatible" | "none" | yes | Resolved embedding provider |
data.runtime.embeddings.model | string | null | yes | Resolved model |
data.runtime.embeddings.base_url | string | null | yes | Resolved base URL |
data.runtime.capabilities | object | yes | |
data.runtime.capabilities.enrichment | boolean | yes | LLM enrichment available |
data.runtime.capabilities.semantic_search | boolean | yes | Semantic search available |
data.runtime.capabilities.related_bookmarks | boolean | yes | Related bookmarks available |
data.runtime.capabilities.organization_agent | boolean | yes | Organization agent available |
ConnectivityTestResponse
| Field | Type | Required | Description |
|---|---|---|---|
ok | boolean | yes | Whether the connectivity check succeeded |
error | string | no | Failure reason |
message | string | no | Success message |
BackupSchedule
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | yes | Enable scheduled snapshots |
cron | string | yes | Five-part cron expression |
retention_count | integer | yes | Number of local snapshots to retain |
next_run_at | string | null | yes | Next scheduled run timestamp |
BackupSchedulePatch
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | no | Enable scheduled snapshots |
cron | string | no | Five-part cron expression |
retention_count | integer | no | Number of local snapshots to retain |
BackupScheduleResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | BackupSchedule | yes | |
data.enabled | boolean | yes | Enable scheduled snapshots |
data.cron | string | yes | Five-part cron expression |
data.retention_count | integer | yes | Number of local snapshots to retain |
data.next_run_at | string | null | yes | Next scheduled run timestamp |
BackupDestination
| Field | Type | Required | Description |
|---|---|---|---|
path | string | yes | Effective backup directory |
is_custom | boolean | yes | Whether a custom destination is active |
writable | boolean | yes | Whether the daemon can write to this directory |
BackupDestinationPatch
| Field | Type | Required | Description |
|---|---|---|---|
path | string | yes | Absolute custom backup path, or empty string to reset |
BackupDestinationResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | BackupDestination | yes | |
data.path | string | yes | Effective backup directory |
data.is_custom | boolean | yes | Whether a custom destination is active |
data.writable | boolean | yes | Whether the daemon can write to this directory |
BackupCreateRequest
| Field | Type | Required | Description |
|---|---|---|---|
skip_remote | boolean | no | When true, create only the local snapshot and skip S3 upload |
BackupResult
| Field | Type | Required | Description |
|---|---|---|---|
path | string | yes | Local backup directory |
size_bytes | integer | yes | Snapshot database size |
bookmark_count | integer | yes | Bookmarks included |
created_at | string | yes | Creation timestamp |
remote_url | string | no | Remote S3 URL when uploaded |
BackupEntry
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Backup name or remote key |
path | string | yes | Local path or s3:// URI |
size_bytes | integer | yes | Snapshot database size |
bookmark_count | integer | yes | Bookmarks included |
created_at | string | yes | Creation timestamp |
source | "local" | "remote" | yes | Backup source |
BackupListResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | array | yes | Backup entries |
BackupVerifyRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Local backup directory name |
BackupPackageRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Local backup directory name |
password | string | yes | Password used to encrypt the package |
EncryptedBackupPackageRequest
| Field | Type | Required | Description |
|---|---|---|---|
path | string | yes | Absolute path to an encrypted backup package file accessible by the daemon |
password | string | yes | Password used to decrypt the package |
BackupVerificationResult
| Field | Type | Required | Description |
|---|---|---|---|
ok | boolean | yes | Whether verification succeeded |
name | string | yes | Local backup directory name |
path | string | yes | Local backup directory |
checksum_verified | boolean | yes | Whether checksum verification succeeded |
verified_files | array | yes | Verified files |
bookmark_count | integer | yes | Bookmarks included |
created_at | string | yes | Backup creation timestamp |
EncryptedBackupPackageResult
| Field | Type | Required | Description |
|---|---|---|---|
path | string | yes | Encrypted package file path |
source_path | string | yes | Source local backup directory |
encrypted | boolean | yes | Whether the package is encrypted |
size_bytes | integer | yes | Encrypted package size |
created_at | string | yes | Package creation timestamp |
EncryptedBackupPackageVerificationResult
| Field | Type | Required | Description |
|---|---|---|---|
ok | boolean | yes | Whether verification succeeded |
path | string | yes | Encrypted package file path |
package_encrypted | boolean | yes | Whether the verified input was encrypted |
checksum_verified | boolean | yes | Whether checksum verification succeeded after decryption |
verified_files | array | yes | Verified files |
bookmark_count | integer | yes | Bookmarks included |
created_at | string | yes | Backup creation timestamp |
RestoreRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | Local backup directory name |
source | "remote" | "encrypted_package" | no | Restore source |
key | string | no | Remote S3 snapshot.db key |
path | string | no | Absolute path to an encrypted backup package file accessible by the daemon |
password | string | no | Password used to decrypt the encrypted package |
allow_unsafe_no_checksum | boolean | no | Allow restoring a backup with no checksum file |
RestoreResult
| Field | Type | Required | Description |
|---|---|---|---|
restored_at | string | yes | Restore timestamp |
bookmark_count | integer | yes | Restored bookmark count |
checksum_verified | boolean | yes | Whether checksum verification succeeded |
rollback_path | string | yes | Rollback copy directory |
restart_required | boolean | yes | Whether daemon restart is required |
restart_command | string | yes | Platform-specific command for restarting littleimpd when detectable |
health_url | string | yes | Local health endpoint to poll after restarting the daemon |
rollback_instructions | array | yes | Manual rollback instructions |
TimelineEvent
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Timeline event ID |
type | "category_created" | "category_merged" | "category_merge_suggested" | "category_renamed" | "category_reparented" | "category_deleted" | "duplicate_removed" | "duplicate_flagged" | "cluster_labeled" | "suggestion_accepted" | "suggestion_rejected" | yes | Timeline event type |
description | string | yes | Human-readable event description |
metadata | object | yes | Event metadata |
source | "agent" | "user" | yes | Event source |
created_at | string | yes | Creation timestamp |
TimelinePage
| Field | Type | Required | Description |
|---|---|---|---|
data | array | yes | Timeline events |
pagination | Pagination | yes | |
pagination.total | integer | yes | Total matching records |
pagination.limit | integer | yes | Applied page size |
pagination.offset | integer | yes | Applied offset |
pagination.has_more | boolean | yes | Whether another page exists |
Suggestion
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Suggestion ID |
bookmarkId | string | null | yes | Related bookmark ID |
type | "new_subcategory" | "merge_categories" | "duplicate_bookmark" | yes | Suggestion type |
value | string | yes | Human-readable suggestion value |
metadata | object | yes | Suggestion metadata |
confidence | number | null | yes | Confidence score |
status | "pending" | "accepted" | "rejected" | yes | Suggestion status |
created_at | string | yes | Creation timestamp |
resolved_at | string | null | yes | Resolution timestamp |
SuggestionsResponse
| Field | Type | Required | Description |
|---|---|---|---|
data | array | yes | Pending suggestions |
meta | object | yes | |
meta.pending | integer | yes | Pending suggestion count |
HealthResponse
| Field | Type | Required | Description |
|---|---|---|---|
status | "ok" | yes | Health status |
version | string | yes | Daemon package version |
uptime | integer | yes | Process uptime in milliseconds |
queueSize | integer | yes | Queued background jobs |
Diagnostics
Redacted local diagnostics payload for user-shared support bundles
| Field | Type | Required | Description |
|---|---|---|---|
generated_at | string | yes | Diagnostics generation timestamp |
version | string | yes | Daemon package version |
platform | object | yes | |
platform.os | string | yes | Operating system platform |
platform.arch | string | yes | CPU architecture |
platform.bun_version | string | yes | Bun runtime version |
platform.node_env | string | yes | Node environment |
platform.host | string | yes | Configured daemon bind host |
platform.port | integer | yes | Configured daemon port |
install | object | yes | |
install.mode | "development" | "native" | "docker" | yes | Detected install mode |
paths | object | yes | |
paths.data_dir | string | yes | Configured data directory |
paths.database_path | string | yes | SQLite database path |
paths.config_file | string | yes | Runtime settings file path |
paths.backup_dir | string | yes | Effective local backup directory |
paths.frontend_dist | string | null | yes | Static frontend directory when served by the daemon |
paths.log_files | array | yes | Known local daemon log files |
daemon | object | yes | |
daemon.status | "ok" | yes | Daemon status |
daemon.uptime_ms | integer | yes | Process uptime in milliseconds |
daemon.queue_size | integer | yes | Pending background jobs |
daemon.queue | object | yes | |
daemon.queue.pending | integer | yes | Pending jobs |
daemon.queue.running | integer | yes | Running jobs |
daemon.queue.done | integer | yes | Completed jobs retained in the queue table |
daemon.queue.failed | integer | yes | Failed jobs retained in the queue table |
providers | object | yes | |
providers.llm | object | yes | |
providers.llm.provider | string | yes | Selected LLM provider |
providers.llm.configured | boolean | yes | Whether LLM enrichment can run with current settings |
providers.llm.model | string | null | yes | Resolved or selected LLM model |
providers.llm.base_url | string | null | yes | Resolved or selected LLM base URL with credentials, query strings, and fragments removed |
providers.embeddings | object | yes | |
providers.embeddings.provider | string | yes | Selected embedding provider |
providers.embeddings.configured | boolean | yes | Whether embedding-backed features can run with current settings |
providers.embeddings.model | string | null | yes | Resolved or selected embedding model |
providers.embeddings.base_url | string | null | yes | Resolved or selected embedding base URL with credentials, query strings, and fragments removed |
backup | object | yes | |
backup.local | object | yes | |
backup.local.path | string | yes | Effective local backup directory |
backup.local.is_custom | boolean | yes | Whether a custom backup destination is active |
backup.local.writable | boolean | yes | Whether the effective local backup directory is writable |
backup.schedule | BackupSchedule | yes | |
backup.schedule.enabled | boolean | yes | Enable scheduled snapshots |
backup.schedule.cron | string | yes | Five-part cron expression |
backup.schedule.retention_count | integer | yes | Number of local snapshots to retain |
backup.schedule.next_run_at | string | null | yes | Next scheduled run timestamp |
backup.s3 | object | yes | |
backup.s3.configured | boolean | yes | Whether enough non-secret S3 fields and stored credentials are present |
backup.s3.endpoint | string | yes | S3-compatible endpoint URL with credentials, query strings, and fragments removed; empty string for AWS |
backup.s3.bucket | string | yes | S3 bucket |
backup.s3.region | string | yes | S3 region |
backup.s3.prefix | string | yes | Object key prefix |
search | object | yes | |
search.keyword | boolean | yes | Whether keyword search is available |
search.semantic | boolean | yes | Whether semantic search is available |
search.hybrid | boolean | yes | Whether hybrid search is available |
omitted_secrets | array | yes | Omitted secret classes |
DiagnosticsResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | Diagnostics | yes | |
data.generated_at | string | yes | Diagnostics generation timestamp |
data.version | string | yes | Daemon package version |
data.platform | object | yes | |
data.platform.os | string | yes | Operating system platform |
data.platform.arch | string | yes | CPU architecture |
data.platform.bun_version | string | yes | Bun runtime version |
data.platform.node_env | string | yes | Node environment |
data.platform.host | string | yes | Configured daemon bind host |
data.platform.port | integer | yes | Configured daemon port |
data.install | object | yes | |
data.install.mode | "development" | "native" | "docker" | yes | Detected install mode |
data.paths | object | yes | |
data.paths.data_dir | string | yes | Configured data directory |
data.paths.database_path | string | yes | SQLite database path |
data.paths.config_file | string | yes | Runtime settings file path |
data.paths.backup_dir | string | yes | Effective local backup directory |
data.paths.frontend_dist | string | null | yes | Static frontend directory when served by the daemon |
data.paths.log_files | array | yes | Known local daemon log files |
data.daemon | object | yes | |
data.daemon.status | "ok" | yes | Daemon status |
data.daemon.uptime_ms | integer | yes | Process uptime in milliseconds |
data.daemon.queue_size | integer | yes | Pending background jobs |
data.daemon.queue | object | yes | |
data.daemon.queue.pending | integer | yes | Pending jobs |
data.daemon.queue.running | integer | yes | Running jobs |
data.daemon.queue.done | integer | yes | Completed jobs retained in the queue table |
data.daemon.queue.failed | integer | yes | Failed jobs retained in the queue table |
data.providers | object | yes | |
data.providers.llm | object | yes | |
data.providers.llm.provider | string | yes | Selected LLM provider |
data.providers.llm.configured | boolean | yes | Whether LLM enrichment can run with current settings |
data.providers.llm.model | string | null | yes | Resolved or selected LLM model |
data.providers.llm.base_url | string | null | yes | Resolved or selected LLM base URL with credentials, query strings, and fragments removed |
data.providers.embeddings | object | yes | |
data.providers.embeddings.provider | string | yes | Selected embedding provider |
data.providers.embeddings.configured | boolean | yes | Whether embedding-backed features can run with current settings |
data.providers.embeddings.model | string | null | yes | Resolved or selected embedding model |
data.providers.embeddings.base_url | string | null | yes | Resolved or selected embedding base URL with credentials, query strings, and fragments removed |
data.backup | object | yes | |
data.backup.local | object | yes | |
data.backup.local.path | string | yes | Effective local backup directory |
data.backup.local.is_custom | boolean | yes | Whether a custom backup destination is active |
data.backup.local.writable | boolean | yes | Whether the effective local backup directory is writable |
data.backup.schedule | BackupSchedule | yes | |
data.backup.schedule.enabled | boolean | yes | Enable scheduled snapshots |
data.backup.schedule.cron | string | yes | Five-part cron expression |
data.backup.schedule.retention_count | integer | yes | Number of local snapshots to retain |
data.backup.schedule.next_run_at | string | null | yes | Next scheduled run timestamp |
data.backup.s3 | object | yes | |
data.backup.s3.configured | boolean | yes | Whether enough non-secret S3 fields and stored credentials are present |
data.backup.s3.endpoint | string | yes | S3-compatible endpoint URL with credentials, query strings, and fragments removed; empty string for AWS |
data.backup.s3.bucket | string | yes | S3 bucket |
data.backup.s3.region | string | yes | S3 region |
data.backup.s3.prefix | string | yes | Object key prefix |
data.search | object | yes | |
data.search.keyword | boolean | yes | Whether keyword search is available |
data.search.semantic | boolean | yes | Whether semantic search is available |
data.search.hybrid | boolean | yes | Whether hybrid search is available |
data.omitted_secrets | array | yes | Omitted secret classes |
UpdateRelease
| Field | Type | Required | Description |
|---|---|---|---|
version | string | yes | Normalized semantic version |
tag | string | yes | Release tag from the update source |
name | string | yes | Release display name |
prerelease | boolean | yes | Whether the release is marked as a prerelease |
published_at | string | yes | Release publication timestamp |
url | string | yes | Human-readable release URL |
UpdateCheckResult
| Field | Type | Required | Description |
|---|---|---|---|
current_version | string | yes | Current packaged Grimoire version |
update_available | boolean | yes | Whether a compatible release is newer than the current version |
source | string | yes | Release source URL used for the check |
channel | "stable" | "beta" | yes | Applied update channel |
latest | UpdateRelease | null | yes | |
latest.version | string | yes | Normalized semantic version |
latest.tag | string | yes | Release tag from the update source |
latest.name | string | yes | Release display name |
latest.prerelease | boolean | yes | Whether the release is marked as a prerelease |
latest.published_at | string | yes | Release publication timestamp |
latest.url | string | yes | Human-readable release URL |
UpdateCheckResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | UpdateCheckResult | yes | |
data.current_version | string | yes | Current packaged Grimoire version |
data.update_available | boolean | yes | Whether a compatible release is newer than the current version |
data.source | string | yes | Release source URL used for the check |
data.channel | "stable" | "beta" | yes | Applied update channel |
data.latest | UpdateRelease | null | yes | |
data.latest.version | string | yes | Normalized semantic version |
data.latest.tag | string | yes | Release tag from the update source |
data.latest.name | string | yes | Release display name |
data.latest.prerelease | boolean | yes | Whether the release is marked as a prerelease |
data.latest.published_at | string | yes | Release publication timestamp |
data.latest.url | string | yes | Human-readable release URL |
ExportBookmark
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Bookmark ID |
url | string | yes | Bookmark URL |
title | string | null | yes | Title |
summary | string | null | yes | Summary |
tags | array | yes | Tag names |
category | string | null | yes | Category name |
domain | string | yes | Domain |
is_pinned | 0 | 1 | yes | Pinned flag, 0 or 1; maps Grimoire starred/favorite state |
read_later | 0 | 1 | yes | Read-later flag, 0 or 1 |
opened_count | integer | yes | Number of user-triggered opens |
last_opened_at | string | null | yes | Most recent user-triggered open timestamp |
created_at | string | yes | Creation timestamp |
is_archived | 0 | 1 | yes | Archived flag, 0 or 1; /export currently returns active rows, so emitted rows are 0 |
read_at | string | null | yes | Read timestamp; null means unread |
notes | string | null | yes | Personal notes; null when empty |
IntegrationTokenRecord
Managed local integration token metadata. Full bearer token values are returned only at creation or rotation.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Integration token ID |
name | string | yes | User-visible integration client name |
token_prefix | string | yes | Redacted token prefix for display and support |
created_at | string | yes | Token creation timestamp |
last_used_at | string | null | yes | Most recent successful token use timestamp |
revoked_at | string | null | yes | Token revocation timestamp |
IntegrationTokenCreateRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | User-visible integration client name |
IntegrationTokenCreateResult
One-time integration token creation or rotation result
| Field | Type | Required | Description |
|---|---|---|---|
token | string | yes | Full bearer token. Store it now; it is never returned by list endpoints. |
record | IntegrationTokenRecord | yes | |
record.id | string | yes | Integration token ID |
record.name | string | yes | User-visible integration client name |
record.token_prefix | string | yes | Redacted token prefix for display and support |
record.created_at | string | yes | Token creation timestamp |
record.last_used_at | string | null | yes | Most recent successful token use timestamp |
record.revoked_at | string | null | yes | Token revocation timestamp |
IntegrationTokenCreateResponse
Integration token creation response
| Field | Type | Required | Description |
|---|---|---|---|
data | IntegrationTokenCreateResult | yes | |
data.token | string | yes | Full bearer token. Store it now; it is never returned by list endpoints. |
data.record | IntegrationTokenRecord | yes | |
data.record.id | string | yes | Integration token ID |
data.record.name | string | yes | User-visible integration client name |
data.record.token_prefix | string | yes | Redacted token prefix for display and support |
data.record.created_at | string | yes | Token creation timestamp |
data.record.last_used_at | string | null | yes | Most recent successful token use timestamp |
data.record.revoked_at | string | null | yes | Token revocation timestamp |
IntegrationTokenListResponse
Response data
| Field | Type | Required | Description |
|---|---|---|---|
data | array | yes | Integration token records |
McpErrorResponse
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes | MCP failure message |
DemoLoadResult
Demo data load result
| Field | Type | Required | Description |
|---|---|---|---|
data | object | yes | |
data.bookmarks_created | integer | yes | Number of bookmarks created by the demo load |
data.categories_created | integer | yes | Number of categories created by the demo load |