# Public REST API (issue #104) Dorfteich instances can expose a token-authenticated REST API at `/api/public/v1` for scripts and integrations. It is **off by default**, twice: a Site Admin enables the instance switch (_Admin → Settings → Public API_), and every pond that should be reachable opts in separately (_Pond settings → Public API_). Anything not enabled answers 404 — indistinguishable from an instance without the feature. ## Personal access tokens Every user manages their tokens under _Settings → API tokens_: a name, a scope (`read` or `read+write`), an optional expiry, and an optional restriction to selected ponds. The secret (`dt_pat_…`) is shown exactly once and stored hashed; tokens are revocable and show their last use. A token acts **as its user**: the normal permission model — grants, label scopes, the 404-vs-403 policy — applies unchanged. Scope and pond restriction only narrow it further; they never grant anything the user could not do in the app. ## Using the API ```sh curl -H "Authorization: Bearer dt_pat_..." \ https://your-instance.example/api/public/v1/me ``` - The machine-readable description lives at `/api/public/v1/openapi.json` (reachable without a token while the instance switch is on). - Requests are rate-limited per token (HTTP 429 + `Retry-After`). - Errors carry the api's uniform body: `{ code, message, details? }`. - Page content is Markdown in, Markdown + rendered HTML out. A content update (`PATCH …/pages/{slug}` with `markdown`) **replaces** the whole page; it is applied through the collaborative document, so open editors converge live, and the previous state stays in the version history (the change itself appears as a manual version named "API update"). ### Endpoint overview | Area | Endpoints | | -------- | ---------------------------------------------------------------------------------------------------------------------------------- | | Identity | `GET /me` | | Ponds | `GET /ponds`, `GET /ponds/{slug}` | | Pages | `GET/POST /ponds/{slug}/pages`, `GET/PATCH/DELETE /ponds/{slug}/pages/{pageSlug}` | | Search | `GET /search?q=&pond=&label=` | | Export | `GET /ponds/{slug}/export/markdown` (ZIP) | | Labels | `GET/POST /ponds/{slug}/labels`, `PATCH/DELETE /ponds/{slug}/labels/{id}`, `PUT/DELETE /ponds/{slug}/pages/{pageSlug}/labels/{id}` | | Comments | `GET/POST /ponds/{slug}/pages/{pageSlug}/comments`, `POST/DELETE …/comments/{id}/resolve` | Deliberately not in v1 (stage 2): attachment upload, version endpoints, webhooks. ## Connect Claude Code / MCP clients The instance ships its own MCP endpoint (Streamable HTTP) at `/api/mcp` — no extra process. It has **its own switches**, independent of the REST API: the instance switch under _Admin → Settings → Public API_ ("Enable the built-in MCP endpoint") and a per-pond opt-in in the pond settings. Authentication uses the same personal access tokens. Claude Code (or any Streamable-HTTP client): ```sh claude mcp add --transport http dorfteich https://your-instance.example/api/mcp \ --header "Authorization: Bearer dt_pat_..." ``` Stdio-only clients bridge with `mcp-remote`: ```json { "mcpServers": { "dorfteich": { "command": "npx", "args": [ "mcp-remote", "https://your-instance.example/api/mcp", "--header", "Authorization: Bearer dt_pat_..." ] } } } ``` Tools: `list_ponds`, `list_pages`, `read_page`, `search`, `create_page`, `update_page` (replace semantics, collab-safe like the REST PATCH), `add_comment`, `list_labels`, `set_page_labels`, `export_pond`. A tool call acts as the token's user; write tools need the `write` scope, and ponds without the MCP opt-in stay invisible (404 semantics). The endpoint is stateless — no sessions to manage, safe behind load balancers. ## Security notes - Bearer tokens only — no cookies are involved, so there is no CSRF surface; browser sessions cannot call the public API and tokens cannot manage tokens. - Token creation and revocation are audit-logged, as is every write through the API (action `api.write`, with the token attributed). - Treat a token like a password. Revoke it under _Settings → API tokens_ the moment it may have leaked.