HTTP endpoints exposed by gno serve for search, documents, graph data, models, jobs, and workspace automation.
POST /api/context/compiled/preview accepts {capsule, budgetTokens, budgetBytes?}; POST /api/context/compiled/check accepts {capsule, markdown}. Local SDK file helpers add compile/check/refresh. Read the compiled context guide for limits, indexed freshness, inclusion, and recovery.
The REST API is the HTTP version of the local GNO engine. Start it with gno serve, then call /api/* from curl, scripts, desktop launchers, internal dashboards, or small apps. It is best when you want automation without embedding the SDK or installing an MCP client.
gno serve # default port 3000
gno serve --port 8080
gno serve --detach # browser workspace + API in backgroundDisconnecting a REST query or Ask request cancels its retrieval, expansion, generation, and verification, and no late result is sent. An embedding or rerank step that is already running may finish before its capacity frees up. Cancelled work is never retried automatically.
Once the server returns a job ID, the job keeps running even if the request that started it disconnects. Cancelling the job or stopping the server stops further work. Completed checkpoints are kept and unfinished embedding work stays pending.
When the server's read queue is full, requests get HTTP 429 RATE_LIMITED; a server that is unavailable or shutting down returns HTTP 503 UNAVAILABLE. Retry as a new explicit request when capacity or service is available. See shared shutdown bounds.
If the index holds a collection the server's config does not list (you removed it from the config file by hand while the server ran, or another client indexed a collection this config omits), requests return HTTP 409 STALE_COLLECTIONS with a message naming the collections, and nothing is deleted. Add the collection back to the config, or run gno cleanup with the config that owns the index.
GET /api/health — process liveness only. A successful response does not claim that retrieval is ready.GET /api/status — index stats, collection health, model state, background watcher state, onboarding state, and the sharedactivation object. Use activation for collection readiness: lexical proof, independent semantic state, persisted connector evidence, and projection completeness. Each request checks every collection folder: while one is missing or unreadable, its collection carries rootError ({ code, message }, never the folder path), healthy is false, and health.checks includes a folder-access warning. Its documents stay indexed, and both clear on the next request once the folder is back.GET /api/capabilities — feature availability for the active install.GET /api/collections, GET /api/tags, GET /api/docs, GET /api/doc — enumerate and fetch local content.GET /api/metadata?collection=notes&values=20 — typed metadata keys with their types, document counts and most common values. values must be 1-100 (default 10); anything else returns 400 VALIDATION.GET /api/doc-asset — stream original source bytes for a document. It supports HEAD and one HTTP Range request with 200, 206, or 416 responses; the native PDF viewer uses it for progressive same-origin loading and Download original.GET /api/doc/:id/sections — extracted heading outline (anchor, level, line, title). Compatible with Web UI readable #anchor links and SDK getSections().POST /api/doc/:id/section-targets and POST /api/doc/:id/section-targets/resolve — create or conservatively resolve a durable SectionTargetV1. Exact and recovered responses include citation lines; ambiguous, stale, and missing omit citation and must not be navigated or cited.GET /api/doc/:id/links, GET /api/doc/:id/backlinks, GET /api/doc/:id/similar, GET /api/graph — graph and relationship data.GET /api/events — server-sent document change events for live UIs.GET /api/traces, GET /api/traces/:traceId, and loopback-only mutation routes — inspect, label, export, delete, or purge private local retrieval receipts.Status is passive for connectors and models: it does not start MCP children, initialize or download models, or call remote inference. Connector evidence is a bounded projection of saved verification receipts. Only the first 64 target/collection pairs are listed; when connectorProjection.truncated is true, connectorProjection.omitted counts the unlisted pairs as passed, failed, incomplete or not applicable, and connector health is decided from every pair.
Status also projects contentTypeBoost.rules as rule IDs plus normalized factors and contentTypeBoost.rulesFingerprint as the full ranking fingerprint. It never exposes configured path prefixes.
The chunking status object reports configured and applied size/overlap, layout state, and pending document/mirror counts. Applied parameters are null for empty/mixed layouts. This is separate from embedding backlog and source-refresh errors. Reading status never changes the policy or starts a rebuild. See chunking configuration.
curl localhost:3000/api/doc-asset?uri=gno://notes/papers/spec.pdf \
-H 'Range: bytes=0-65535' -o spec.part.pdfThe pinned PDF.js worker, character maps, and standard fonts are also served same-origin under /vendor/pdfjs/*. Only the known PDF.js files are served; no CDN is involved.
Models are resolved, downloaded, and loaded on first use, and an existing vector index starts without loading its model. Capability flags describe what is configured, whether or not a model is loaded. Loaded-model counts do not measure GPU memory. Polling status does not keep a model loaded: by default an idle model unloads after five minutes and reloads on the next request.
curl -X POST localhost:3000/api/search -d '{"query":"exact term"}'
curl -X POST localhost:3000/api/vsearch -d '{"query":"concept"}'
curl -X POST localhost:3000/api/query -d '{"query":"research question"}'
curl -X POST localhost:3000/api/ask -d '{"query":"what changed?"}'
curl -X POST localhost:3000/api/ask \
-H 'content-type: application/json' \
-d '{"query":"Who owns the launch decision?","verify":true}'Search bodies mirror the CLI: collection, limit, since, until, category, author, intent, exclude, tagsAny, tagsAll, and mode-specific controls such as noExpand, noRerank, candidateLimit, and queryModes.
POST /api/search and POST /api/query also accept collections, a list of 1 to 256 collection names, to search several collections at once. The filter applies before the result limit, and the response lists the searched collections, sorted, in meta.collections. Sending both collection and collections, an empty list, or more than 256 names returns 400 VALIDATION; names that are not configured return 404 NOT_FOUND naming each one.
curl -X POST localhost:3000/api/query \
-H 'content-type: application/json' \
-d '{"query":"rollout plan","collections":["work","ops"]}'Supported owner filters apply before the corresponding candidate budget. Vector and hybrid language filtering selects matching chunks; standalone lexical language remains reserved. Caller scope intersects filters, and an empty allowlist denies all. Excluded owners cannot consume the nearest-neighbor window, but score thresholds, deduplication, or limited eligible coverage can still produce short or empty results. Query/Ask evidence and generation budgets are unchanged.
Vectors are tied to each document's title and text and to the model and runtime that produced them, so documents with the same body but different titles can rank differently. If stored vector data is missing or corrupt, semantic search reports a failure and hybrid falls back to keyword results. Normal gno embed completes or repairs coverage.
Hybrid fallback reports meta.vectorsUsed: false if no vector search succeeded and meta.mode: "bm25_only" for wholly lexical retrieval. A successful vector search with no matches can report vectorsUsed: true. A model that failed to load is different from a search with no matches: keep the fallback details and send the request again once the model loads.
Search results include source.absPath when the hit is a resolvable file-backed document and the caller is on the same host. A remote caller (one that is not a same-host client: loopback peer, loopback Host, no forwarding headers) never receives absPath on any /api/* response, including search, query, ask, /api/doc, capture, and remember receipts; it opens results by uri and the collection-relative relPath. If absPath is absent, show the URI tail and do not offer file-open for that row. Default snippets skip leading YAML frontmatter and prefer document prose. Each /api/search snippet comes from the chunk that best covers the query terms, with the terms in <mark> tags, and snippetRange is that chunk's line span. A frontmatter-dominated snippet window falls back to stripped chunk prose. line follows that trimmed display range. The Web UI document page is a separate surface: /doc?uri=<encodeURIComponent(uri)>, not GET /api/doc.
Retrieval bodies may include up to 16 projectHints. These values are opaque and untrusted: the server does not resolve or echo them, does not check them against the filesystem, and does not let them change ranking. The response shape is the same with or without the field. REST diagnose returns the query-diagnose@1.0 shape and omits affinity; affinity details come only from local CLI diagnose.
POST /api/ask keeps the existing raw Ask behavior when verify is absent or false. With "verify": true, it generates from one closed Context Capsule and returns the Capsule, freshness receipt, four-state per-claim verdicts, exact evidence IDs and line spans, coverage, gaps, semantic verifier state, and explicit abstention. The verified Ask body is closed: unknown fields fail validation.
Every substantive claim must be supported for the draft to be returned. Otherwise the response withholds it and reports answerStatus: "abstained". A contradiction requires positive conflicting evidence; missing evidence is insufficient, not contradicted. Unavailable or failed semantic verification remains visible rather than silently falling back to an unverified answer.
curl localhost:3000/api/query \
-H 'content-type: application/json' \
-d '{
"query": "authentication",
"collection": "notes",
"limit": 20,
"tagsAny": ["security"]
}'POST /api/context compiles exact indexed evidence for one goal into a deterministic Capsule. The active server supplies the canonical index. The budget covers the complete payload—not each document separately—and the result includes exact URI/line spans, source and passage hashes, coverage gaps, omission counts, and requested capability fallbacks. It is returned to the caller and is not persisted.
Tag filters are normalized, lowercased, deduplicated, and validated before retrieval. Result and candidate limits cover the whole request across all collections, and the same request returns the same Capsule.
curl localhost:3000/api/context \
-H 'content-type: application/json' \
-d '{"goal":"Compare launch proposals","collections":["work"],"budgetTokens":12000, "depthPolicy":"fast"}'POST /api/context/verify accepts a complete saved Capsule and returns a read-only receipt classifying evidence as unchanged, stale, or missing, with independent fingerprint and ranking state. Index mismatch and malformed Capsules fail before evidence reads. Add "format":"md" for the trust-delimited readable projection. Each untrusted block uses a collision-resistant Markdown fence derived from its content, so indexed text cannot forge the closing boundary. JSON is canonical by default.
Context API errors are deliberate: invalid input, filters, budgets, or identity return 400; no evidence returns 404; source, index, context, mutation, and provenance conflicts return 409; tokenizer unavailability returns 503; retrieval, load, snapshot, and runtime failures return 500. Error messages come from a fixed public catalog; internal paths, causes, and stack traces are not returned.
GET /api/changes?since=<ISO-8601-or-cursor>&collection=notes&limit=100
GET /api/diff?ref=<document-ref>&change=<opaque-change-id>
GET /api/impact?ref=<document-ref>&collection=notes&maxDepth=3&maxNodes=100&maxEdges=250These read-only endpoints share the CLI, MCP, and SDK contracts. Changes contain bounded identity, hash, lifecycle, and structural metadata—not source bodies. Diff discloses partial, expired, or unavailable retained history. Impact returns one bounded evidence path per dependent document; repeat collection= to limit it to those collections.
Saved-Capsule registration management remains CLI-only. The local /api/events stream can emit a closed capsule-reverified event after persistence, but never a question, label, path, URI, hash, receipt, credential, Capsule, passage, or source content.
Restoring an identical file at its former path reactivates it on the next successful sync and adds one reactivate change. Repeated unchanged syncs add nothing. Graph repair covers affected references in other collections. The journal stays metadata-only.
POST /api/collections and DELETE /api/collections/:name — add or remove indexed folders.POST /api/sync — trigger reindexing from the browser or an automation, for every collection, one collection, or just some of its files. See Reindex with POST /api/sync.POST /api/capture — capture a note with structured provenance, optional preset scaffolds, and a receipt that separates write, sync, and embed state.GET /api/requests/:requestId — look up the optional requestId sent with POST /api/capture, POST /api/memory/remember, or PUT /api/docs/:id before retrying. See Retry-safe writes.POST /api/docs, PUT /api/docs/:id, POST /api/docs/:id/refactor-plan, POST /api/docs/:id/rename, POST /api/docs/:id/move, POST /api/docs/:id/duplicate, POST /api/docs/:id/deactivate — create and manage editable documents.POST /api/folders — create a folder inside a collection.POST /api/models/pull, GET /api/models/status, GET /api/jobs/active, GET /api/jobs/:id — model downloads and long-running job polling.GET /api/connectors — inspect configured integration targets without starting them. POST /api/connectors/verify — explicitly run one read-only, collection-scoped retrieval proof for an installed MCP target.Reference-safe rename and move are deliberately two-step. Preview returns the canonical file-refactor-preview@1.0 plan, including every examined reference, canApply, and a deterministic SHA-256 planDigest. Apply requires that exact digest, schemaVersion: "1.0", and confirmation: "apply". Current state is replanned before mutation, so stale, ambiguous, malformed, unsupported, read-only, occupied, and cross-collection plans fail closed. applied_with_sync_pending means the filesystem committed and only the later index refresh needs Update All; it is not a failed file move.
curl localhost:3000/api/connectors/verify \
-H 'content-type: application/json' \
-d '{"connectorId":"cursor-mcp","collection":"notes"}'Connector verification is active only when this endpoint or the Web Connectors action is invoked. It does not edit client configuration. Skill targets return an explicit runtime-unverifiable result because GNO cannot prove that the client loaded or executed an installed skill file.
Use /api/capture for second-brain capture with source: frontmatter and presets such as person, company-project, meeting, or idea-original. Use /api/docs for raw note creation without provenance capture semantics. /api/capture writes the note and syncs it into text search before it responds, so a 201 capture is retrievable. If the file lands but sync fails, the response is 500 CAPTURE_SYNC_FAILED with the written path in details; run gno update. Capture content must be text, /api/capture does not accept legacy overwrite, and capture writes fail instead of replacing a late-arriving file. The browser clipper route POST /api/capture/clip keeps its own contract (202 plus a sync job, and an Idempotency-Key).
With no body, POST /api/sync reindexes every collection. collection limits it to one, gitPull runs git pull first, and paths re-reads only the listed files of collection, given relative to its root or as absolute paths inside it, at most 256 files. A listed file that no longer exists is removed from the index.
curl -X POST localhost:3000/api/sync \
-H 'content-type: application/json' \
-d '{"collection":"notes","paths":["a.md","sub/b.md"]}'The response is 202 with a jobId and a status: running when the sync starts at once, or queued when another job is running or waiting. A sync for a collection that already has one waiting joins it and returns the same jobId, so any number of requests cause at most one more pass: listed files are added to it (past 256 files it becomes a whole-collection pass), and a whole-collection sync widens it to the whole collection. GET /api/jobs/:id reports queued until the pass starts, plus its scope (collection, and paths or null for the whole collection). Waiting syncs live in the server process; any still waiting when it stops do not run, and the index stays consistent.
400 VALIDATION — a bad body, an empty paths list or one over 256 files, or paths without collection or together with gitPull.400 INVALID_PATH — a path outside the collection (including through a symlink), a directory, or a path its pattern, include, or exclude rules leave out.409 CONFLICT — another process holds the index write lock when the sync would start at once.429 QUEUE_FULL — 64 different syncs are already waiting. Retry after the running job finishes.Only reindexing queues. Other jobs, such as adding a collection, are still refused with 409 CONFLICT while a job runs.
gno serve binds the API to the local loopback interface. The API token does not turn it into a remotely reachable server.GNO_API_TOKEN and send X-GNO-Token from non-browser clients.export GNO_API_TOKEN="secret"
gno serve
curl -H "X-GNO-Token: secret" localhost:3000/api/statusA host absolute path shows the owner's home directory and folder layout, so only callers on the owner's machine receive one. A caller counts as same-host when the socket peer is loopback, the Host header names a loopback host, and no forwarding header is present. Every other caller gets /api/* JSON without these host path fields:
source.absPath on search, query, and ask results and on /api/doc.absPath on capture and remember receipts, and in CAPTURE_SYNC_FAILED error details.path on document create, editable copy, rename, move, duplicate, trash, and create-folder responses, and both path and the file:// uri on a document save (PUT /api/docs/:id).Remote callers identify and open a document by its gno:// uri plus the collection-relative relPath, which is all GET /api/doc?uri= and GET /api/doc-asset?uri= need. A capture or remember error for a file that was written but not indexed names it by gno:// URI on every surface. The same-host Web UI, the CLI, and the SDK keep absPath for Reveal and file-open; a remote Web UI opens originals through /api/doc-asset. Streamable HTTP MCP applies the same rule to every caller; see MCP security posture.
Configuration paths follow the same rule. A remote caller names a collection by its name and gets no:
configPath, dbPath, or collection root path on /api/status, and no collection root path on /api/collections (list, create, and update).path on /api/status, and no connector install path on /api/connectors.On your own machine the Collections page, dashboard, and connector list show these paths as before. A remote Web UI shows the same pages without the path lines and without suggested-folder quick picks.
POST /api/search, /api/query, /api/query/diagnose, /api/ask, and /api/context accept the same filter object. Invalid predicates return a client error with a field path. Document detail returns stored typedMetadata and metadataError; do not infer types in clients.
Read the metadata guide: examples, operators, missing fields, limits, and recovery.
On a server started with the dedicated archive config and index: GET /api/sessions/status and POST /api/sessions/import with {sourceId, dryRun?, limit?}. Host paths are rejected. GET /api/sessions/discover, POST /api/sessions/sources, DELETE /api/sessions/sources/:id, and POST /api/sessions/init with {archive, collection} are same-host client only and return 403 otherwise. Same-host means the socket peer is loopback, the Host header names a loopback host, and no forwarding header is present; mutating requests also need the CSRF conditions, so a browser page on another origin is refused while a local process on the same machine (the Web UI, curl, a script) is treated as the owner. Errors carry the specific code in details.sessionsCode. Receipts, status, and errors carry no session content or host paths. See the session error codes.
POST /api/sessions/automation/run { "profileId": "claude" }
PUT /api/sessions/automation/:id { "sources": ["claude-code"], "cadence"?: "30m", "limit"?: 200, "retries"?: 3 }
GET /api/sessions/automation/:id/preview
POST /api/sessions/automation/:id/enable { "hook"?: { "harness": "claude-code" }, "schedule"?: { "cadence"?: "30m" } }
POST /api/sessions/automation/:id/disable { "hook"?: true, "schedule"?: true }
DELETE /api/sessions/automation/:idrun is open to any allowed client (CSRF-protected). It runs a configured profile through the importer and returns the shared sessions-automation-run object; unknown keys are rejected. It cannot enable triggers or widen sources.{ profileId, hook, schedule, pendingCleared, running, removed, warnings }.