dorfteich/docs/self-hosting/public-api.md
Claude Fable 5 d73b120d06 #148: Seitenlisten filtern nach createdSince/updatedSince
Neues pageListQuerySchema (ISO 8601, Kulanz für Datum ohne Zeit),
Query-Parameter auf interner und Public-API-Seitenliste, Prisma-where
mit gte; neue Indizes (pondId, createdAt)/(pondId, updatedAt) als
Migration. OpenAPI-Parameter, MCP-Parität (list_pages
created_since/updated_since), Doku (api-guide, mcp-guide,
public-api.md), DB-Test inkl. 400 bei ungültigem Datum.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155v2aT8AG1kZDQEZiCLBWC
2026-07-20 00:49:52 +02:00

4.8 KiB

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

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 (list filters ?createdSince=/?updatedSince=), 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):

claude mcp add --transport http dorfteich https://your-instance.example/api/mcp \
  --header "Authorization: Bearer dt_pat_..."

Stdio-only clients bridge with mcp-remote:

{
  "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.