
# REST API Reference

A concise reference to the ContextNest Community (hosted-nest) REST API. It is
faithful to the server's `API.md`; only endpoints that actually exist are listed
here. For the deeper `cnst_` key model see
[Authentication & API Keys](https://nest.promptowl.ai/d/promptowl-documents/protocol/authentication-api-keys); to point an agent at
a nest over MCP see [Connect an Agent](https://nest.promptowl.ai/d/promptowl-documents/platform/hosted-nest/connect-an-agent-mcp).

- **Base URL:** `http://localhost:3838` (default; override the port with `PORT`).
- **Bodies:** JSON unless noted.
- **Errors:** `{ "error": "message" }` with an appropriate HTTP status.

## Authentication

Two methods, evaluated in order:

1. **Cookie session** — `cnst_session=<id>`, set by `/auth/login` and
   `/auth/register`. The browser default.
2. **Bearer API key** — `Authorization: Bearer cnst_<token>`, issued by
   `POST /auth/keys`. Use for scripts and external tools.

```bash
curl -H 'Authorization: Bearer cnst_<your-token>' http://localhost:3838/nests
```

In `AUTH_MODE=open`, most read endpoints bypass auth (everyone is the anonymous
admin). A **license gate** also applies: any non-GET request, or any governance
path, requires a valid PromptOwl license (`503 License required` otherwise).

## Permission tiers

For any `/nests/:nestId/...` request the middleware computes an effective
permission — `owner | admin | write | read | none` — and enforces a required
level by path:

| Path class | Required |
|---|---|
| `/collaborators`, `/visibility` | `admin` |
| Stewardship actions (`/approve`, `/reject`, `/submit-review`, `/cancel-review`) | `read` (handler enforces fine-grained steward gating) |
| Other non-GET | `write` |
| GET | `read` |

Open mode treats everyone as `owner`.

## Auth & keys

| Method | Path | Purpose |
|---|---|---|
| POST | `/auth/register` | Self sign-up / claim an invited account (gated by `MANUAL_SIGN_IN`). |
| POST | `/auth/login` | Password login → session cookie. |
| POST | `/auth/logout` | Clear the session cookie. |
| POST | `/auth/keys` | Mint an API key (named; `label` required, unique, ≤ 60 chars). Plaintext shown once. |
| GET | `/auth/keys` | List the caller's keys (id, prefix, label, `nest_id`, timestamps) — never plaintext. |
| POST | `/auth/keys/rotate` | Replace **one** key (by `key_id`) and return its new plaintext. |
| DELETE | `/auth/keys/:keyId` | Revoke one key. |
| POST | `/auth/password` | Change the caller's password. |
| POST | `/auth/invite` | (admin) Create an invited user; returns a temporary password once. |
| GET | `/auth/teammates` | (admin) All users + key counts. |
| POST | `/auth/admin/reset-password/:userId` | (admin) In-platform password reset. |
| DELETE | `/auth/users/:userId` | (admin) Remove a user. |
| GET | `/auth/oidc/login` · `/auth/oidc/callback` · `/auth/oidc/logout` | Generic OIDC SSO flow. |

See [Authentication & API Keys](https://nest.promptowl.ai/d/promptowl-documents/protocol/authentication-api-keys) for the full key
semantics.

## Nests

A **nest** = a context vault (a folder of markdown documents + governance state).

| Method | Path | Purpose |
|---|---|---|
| GET | `/nests` | List owned + shared nests. |
| POST | `/nests` | Create a nest — `{ name, description? }`. |
| GET | `/nests/:nestId` | Get one nest + the caller's `permission`. |
| DELETE | `/nests/:nestId` | Delete a nest (owner only). |
| GET | `/nests/:nestId/settings` | Read governance settings. |
| PATCH | `/nests/:nestId/settings` | (admin/owner) `stewardship_enabled`, `allow_self_approve`, `prime_only_review`, `prime_tags`. |
| PATCH | `/nests/:nestId/visibility` | (admin) `{ "visibility": "public" | "private" }`. |
| GET | `/nests/unsynced` | (server-admin) Folders under `DATA_ROOT` not yet nests. |
| POST | `/nests/unsynced/sync` | (server-admin) Convert an unsynced folder into a nest. |
| DELETE | `/nests/unsynced/sync` | (server-admin) Delete an unsynced folder outright. |
| GET | `/nests/:nestId/overview` | Nest-level summary stats. |
| GET | `/nests/:nestId/census` | Everything a nest overview needs — counts by tag/type/status, folder tree, authors, recent edits. |

## Nodes (documents)

A **node id** is the relative path without `.md`, e.g. `nodes/api-design`. A
document filed in a folder has the id `nodes/<folder>/<slug>` — the `folder`
field on create nests it, each `/`-separated segment slugified independently (max
8 levels, each ≤ 100 chars).

| Method | Path | Purpose |
|---|---|---|
| GET | `/nests/:nestId/nodes` | List readable docs. Query: `type`, `tag`, `status`, `approved_only`, `limit`, `folder`, `content`. |
| POST | `/nests/:nestId/nodes` | Create — `{ title, content, tags?, type?, folder?, status?, schedule?, prime? }`. `409` if the derived id is taken. |
| GET | `/nests/:nestId/nodes/:nodeId` | Full node content (JSON). |
| PATCH | `/nests/:nestId/nodes/:nodeId` | Edit — `{ title?, content?, tags?, changeNote?, schedule?, prime? }`. Optional `X-Base-Version` header → `409` on conflict. |
| DELETE | `/nests/:nestId/nodes/:nodeId` | Delete (write). `423` if a pending review freezes it for a non-approver. |
| POST | `/nests/:nestId/nodes/:nodeId/move` | Refile to another folder; carries version history, reviews, grants, etc. to the new id. |
| POST | `/nests/:nestId/nodes/:nodeId/revert` | Restore an earlier version as a new version — `{ targetVersion }`. |
| POST | `/nests/:nestId/assets` | Multipart image/video upload (field `file`). Returns `{ file, url, markdown }`. |
| GET | `/nests/:nestId/assets/:file` | Serve asset bytes (immutable cache). |

### Node read formats

The single-node GET supports content negotiation on top of the default JSON:

| Param / header | Effect |
|---|---|
| `?content=1` (on the **list**) | Include document bodies (off by default — the listing is served from the metadata index). |
| `?format=markdown` | `text/markdown` — YAML frontmatter + body (the export shape). |
| `?format=raw` | Raw node **body** as text, no JSON envelope. Content-Type varies by node type (`document`/`agent`/`skill` → markdown, `artifact` → html, `tool`/`table` → plain). |
| `Accept: text/markdown` (header) | Same as `?format=raw` when no `format` param is present (presence-based; ignores q-values). |
| `?frontmatter=1` | With raw only: prepend the stored YAML frontmatter block. |

Public readers on a `visibility: public` nest receive the **approved snapshot**
body, never an unapproved draft.

## Folders

| Method | Path | Purpose |
|---|---|---|
| GET | `/nests/:nestId/folders` | The nest's folder tree alone (no document opened). Query: `?folder=<path>`, `?recursive=0`. |
| POST | `/nests/:nestId/delete-folder` | (owner/admin) Delete every document under a folder prefix. |

The folder tree returns rows shaped `{ path, count, total }` — `count` =
documents filed directly in that folder, `total` = the whole subtree beneath it.
Folders are not stored objects; they exist only as segments of the node ids
beneath them.

## Collaborators, grants & teams

| Method | Path | Purpose |
|---|---|---|
| GET/POST | `/nests/:nestId/collaborators` | (write+) List / add a direct nest collaborator (`read`/`write`/`admin`, escalation-capped). |
| PATCH/DELETE | `/nests/:nestId/collaborators/:collabId` | (admin) Re-role / remove. |
| GET/POST | `/nests/:nestId/grants` | Share a **single document** or **one folder** — `{ target_type, target, email, role }`. |
| DELETE | `/nests/:nestId/grants/:id` | Revoke a grant. |
| GET/POST/PATCH/DELETE | `/teams`, `/teams/:teamId`, `/teams/:teamId/members[/:userId]` | Reusable owner-managed groups (`admin`/`editor`/`viewer` roles). |
| GET/POST/DELETE | `/nests/:nestId/teams[/:teamId]` | Share / unshare a team onto the nest. |

A **grant** gives one person access to one document or everything under one
folder without a nest-wide collaborator row. `write` outranks `read` when grants
overlap; a grantee sees only the nodes their grants cover. Managing grants is
owner / nest admin / server admin.

## Stewardship & review

| Method | Path | Purpose |
|---|---|---|
| GET/POST | `/nests/:nestId/stewards` | List / assign stewards by scope (`document`/`tag`/`nest`), role `reviewer`/`editor`/`viewer`. |
| DELETE | `/nests/:nestId/stewards/:stewardId` | Soft-remove a steward. |
| POST | `/nests/:nestId/stewards/sync` | Re-import from `<nest>/.context/stewards.yaml`. |
| GET | `/nests/:nestId/nodes/:nodeId/stewards` | Resolved stewards for one node (priority document > tag > nest). |
| GET | `/nests/:nestId/nodes/:nodeId/{can-access,can-approve,can-edit}` | Permission probes for one node. |
| POST | `/nests/:nestId/nodes/:nodeId/submit-review` | Move a node to `pending_review`. |
| POST | `/nests/:nestId/nodes/:nodeId/approve` · `/reject` · `/cancel-review` | Reviewer actions (reject/cancel need a note). |
| GET | `/nests/:nestId/review-queue` | All reviews for the nest. Query: `status`, `limit`, `offset`. |
| GET | `/nests/:nestId/nodes/:nodeId/reviews` | Full review history for one node. |
| GET | `/nests/:nestId/feed` | Activity feed — pending changes interleaved with comment activity. |
| POST | `/nests/:nestId/nodes/:nodeId/request-deletion` | (editor) Flag one document for deletion. |
| POST | `/nests/:nestId/request-deletion` | (editor, nest-scope) Flag a folder or the whole nest. |
| GET | `/nests/:nestId/deletion-requests` | Pending / declined deletion requests. |
| POST | `/nests/:nestId/deletion-requests/:id/delete` · `/decline` | Honour / decline a request. |
| GET | `/me/drafts` · `/me/work` | The caller's unsubmitted drafts / their governance inbox across nests. |

See the governance model for what stewardship, roles, and prime documents mean.

## Versions

| Method | Path | Purpose |
|---|---|---|
| GET | `/nests/:nestId/nodes/:nodeId/versions` | Version metadata + per-version change-log diffs (bodies omitted). |
| GET | `/nests/:nestId/nodes/:nodeId/versions/:version` | One version's full body, reconstructed on demand. |

## Comments

| Method | Path | Purpose |
|---|---|---|
| GET | `/nests/:nestId/nodes/:nodeId/comments` | List comments (optional `?status=open|resolved`). |
| POST | `/nests/:nestId/nodes/:nodeId/comments` | Leave a comment — `{ body, version?, anchor?, parentId? }`. |
| POST | `/nests/:nestId/nodes/:nodeId/comments/:commentId/resolve` | Mark resolved. |

Comments are a standalone collaboration primitive (Word/Docs-style), not part of
the version chain. Any caller with read access may leave or resolve one.

## Glossary & edge types

| Method | Path | Purpose |
|---|---|---|
| GET/POST/DELETE | `/nests/:nestId/definitions[/:id]` | Per-nest glossary — one row per `(nest, term)`, upsert by term. |
| GET/POST/DELETE | `/nests/:nestId/edge-types[/:id]` | Edge-type registry (**feature-flagged** — `404` unless `FEATURE_WORKFLOW_PLANE` is on). |

## Query, search & retrieval (read-only consumers)

These power LLM context retrieval and are open-mode friendly.

| Method | Path | Purpose |
|---|---|---|
| GET | `/nests/:nestId/search?q=<term>` | Full-text search within a nest. |
| GET | `/search?q=<term>&limit=20` | Same match across every nest the caller can see; each hit stamped with its nest. |
| POST | `/nests/:nestId/context` | One-call retrieval → markdown ready to prepend to a prompt, plus trace + node manifest. Body: `prompt` **or** `selector`, `max_tokens?`, `hops?`, `include_drafts?`. Requires a license. |
| GET | `/nests/:nestId/context` | Return `CONTEXT.md` content. |
| GET | `/nests/:nestId/export?format=markdown` | LLM-pointed plain-text view — every accessible node as frontmatter + body, joined by `---`. Approved-only for every caller. Optional `selector`, `max_tokens`. A GET, so it works on public nests without credentials. |
| GET | `/nests/:nestId/export?format=bundle` | Download a portable `.zip` to move the nest to another host. Optional `?includeDrafts=1`. No separate import endpoint — unzip and use the folder importer (`POST /nests/import`). |
| POST | `/nests/:nestId/query` | Graph query (Cypher-ish). |
| GET | `/nests/:nestId/graph` | Ontology graph for the graph view (links, stewardship overlay, coverage gaps). |
| GET | `/nests/:nestId/runnables` | Every runnable (`agent`/`skill`) node with its `schedule` and body — an orchestrator surface. |
| POST | `/nests/:nestId/reindex` | (write+) Force-rebuild the nest's document index; returns `{ indexed }`. |
| POST | `/nests/:nestId/publish` | Publish the nest (snapshots + index). |

## MCP transport

| Path | Purpose |
|---|---|
| `ALL /nests/:nestId/mcp` | Per-nest Model Context Protocol endpoint (JSON-RPC over HTTP). |
| `ALL /mcp` · `GET /index` | Server-level cross-nest MCP + index. |

The transport is **read-tier** (MCP Streamable HTTP is always POST), so each
mutating tool re-checks its own permission inside the handler. Gate rejections
return a JSON-RPC error envelope, not `{error}` JSON. See
[Connect an Agent](https://nest.promptowl.ai/d/promptowl-documents/platform/hosted-nest/connect-an-agent-mcp).

## Admin / operator

| Method | Path | Purpose |
|---|---|---|
| GET/POST/DELETE | `/admin/super-admins[/:email]` | (server-admin) Grant / revoke server-wide superadmins. |
| GET | `/admin/trace` | (server-admin) Cross-tenant operator activity trail. |
| GET | `/nests/:id/trace` | (owner / nest admin) The per-nest slice of the activity trace. |
| GET/PATCH | `/admin/settings` | (superadmin) Runtime settings (sign-in gate, logo, OIDC, connectors, feature flags). |

## Error shapes

| Status | When |
|---|---|
| 400 | Validation error |
| 401 | Auth required |
| 403 | Permission denied (`Insufficient permissions`, steward mismatch) |
| 404 | Nest / node / steward not found, or no permission |
| 409 | Conflict (key exists, version conflict, duplicate) |
| 423 | Node frozen by a pending review |
| 429 | Rate limit |
| 503 | License missing |

## Sources

- `API.md` (Community server repo) — the complete endpoint reference this page condenses.
- `CONFIGURATION.md` (Community server repo) — auth modes and the license gate.
