dorfteich/docs/manual/api-guide.md
Claude Fable 5 ffcc337ed0
Some checks failed
CD / Build and push images (push) Successful in 3m57s
CD / Deploy to Test (push) Successful in 9s
CI / Lint, typecheck, test (push) Failing after 4m16s
CI / Auth e2e pack (push) Has been skipped
CI / Import/export fidelity gate (push) Has been skipped
CI / Build container images (push) Has been skipped
CD / Smoke tests against Test (push) Successful in 1m19s
CD / Promote to Int (push) Successful in 11s
Page-tree parity for the public REST API and MCP (#110)
The slug-based machine surfaces now see and shape the hierarchy:

- REST: page list/detail carry parent (the parent page's slug, nulled
  when the token's user may not read it — same no-leak rule as the
  internal list); create accepts parent; PATCH accepts parent
  (slug nests, null moves to the top level, appended at the end of the
  new sibling group via the new PagesService.moveToEnd). Cycle/depth
  refusals keep their regular error codes. OpenAPI updated.
- MCP: list_pages returns parent, create_page takes an optional parent
  slug, update_page moves with parent (slug|null); tool errors carry
  the api code (page_cycle covered in the e2e pack).
- ZIP export deliberately stays flat — noted in features.md; the
  hierarchy is organizational only.

e2e: REST pack covers nested create, list shape, move/root-move, 409
page_cycle, 404 unknown parent; MCP pack covers nested create, list
parent, and the cycle tool error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 10:49:52 +02:00

4.8 KiB

API guide

Deutsche Fassung: docs/de/manual/api-guide.md

How to talk to Dorfteich from scripts and integrations. The public REST API lives at /api/public/v1; its machine-readable description is served at /api/public/v1/openapi.json.

Switching it on

The API is off by default, twice:

  1. A site admin enables the instance switch (Admin → Settings → Public API).
  2. Each pond that should be reachable opts in (pond settings → "Expose this pond through the public API").

Anything not enabled answers 404 — indistinguishable from an instance without the feature.

Personal access tokens

Create tokens under Settings → API tokens: a name, a scope (read or read+write), an optional expiry date, and optionally a restriction to selected ponds. The secret (dt_pat_…) is shown once — copy it immediately. Tokens are revocable and show their last use.

A token acts as you: it can read and write exactly what you can, narrowed by its scope and pond restriction — never more. Token management itself always requires the browser session; a leaked token cannot mint new tokens.

Authenticate with a bearer header:

curl -H "Authorization: Bearer dt_pat_..." \
  https://wiki.example.com/api/public/v1/me

GET /me is the smoke test — it returns your user, the token scope, and any pond restriction.

Reading

# The ponds this token can reach
curl -H "$AUTH" https://wiki.example.com/api/public/v1/ponds

# Pages of a pond: slug, title, parent (page-tree slug), labels, timestamps
curl -H "$AUTH" https://wiki.example.com/api/public/v1/ponds/team/pages

# One page — Markdown source AND rendered, sanitized HTML
curl -H "$AUTH" https://wiki.example.com/api/public/v1/ponds/team/pages/meeting-notes

# Full-text search (optionally ?pond=<slug>&label=<labelId>)
curl -H "$AUTH" "https://wiki.example.com/api/public/v1/search?q=seerose"

# The whole pond as a Markdown ZIP
curl -H "$AUTH" -o team.zip \
  https://wiki.example.com/api/public/v1/ponds/team/export/markdown

Search snippets mark hits with **…**; results carry pond and page slugs for follow-up calls.

Writing (requires the write scope)

# Create a page from Markdown (optional "parent": a page slug nests it)
curl -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"title": "Meeting notes", "markdown": "# Agenda\n\n- Ducks\n"}' \
  https://wiki.example.com/api/public/v1/ponds/team/pages

# Rename, REPLACE the content, and/or move in the page tree
# ("parent": <slug> nests the page, "parent": null moves it to the top level)
curl -X PATCH -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"markdown": "New content."}' \
  https://wiki.example.com/api/public/v1/ponds/team/pages/meeting-notes

# Move a page to the trash
curl -X DELETE -H "$AUTH" \
  https://wiki.example.com/api/public/v1/ponds/team/pages/meeting-notes

A content PATCH replaces the whole page. It is applied through the live collaborative document: anyone editing the page at that moment sees the change appear, nothing forks, and the previous state remains in the version history as a restorable snapshot (the update itself shows up as a version named "API update").

Labels

GET    /ponds/{pond}/labels                       # the label tree
POST   /ponds/{pond}/labels                       # {name, color?, parentId?}
PATCH  /ponds/{pond}/labels/{labelId}             # rename/recolor/move in one call
DELETE /ponds/{pond}/labels/{labelId}
PUT    /ponds/{pond}/pages/{page}/labels/{labelId}   # assign
DELETE /ponds/{pond}/pages/{page}/labels/{labelId}   # unassign

Label-tree management needs pond-admin rights (like in the app).

Comments

GET    /ponds/{pond}/pages/{page}/comments?filter=all|open|resolved
POST   /ponds/{pond}/pages/{page}/comments        # {body, parentId?} — Markdown
POST   /ponds/{pond}/pages/{page}/comments/{id}/resolve
DELETE /ponds/{pond}/pages/{page}/comments/{id}/resolve   # reopen

The pond's comment policy applies exactly as in the app.

Errors, limits, semantics

  • Errors carry the uniform body { "code": "...", "message": "...", "details": {...} }. The code is stable and machine-checkable.
  • 404 vs 403: what you may not read answers 404 (existence stays hidden — including ponds without the API opt-in); a write on something you may read but not change answers 403. A token without the write scope gets 403 scope_required on every write route.
  • Rate limit per token; 429 responses carry a Retry-After header.
  • No cookies are involved anywhere — there is no CSRF surface, and browser sessions cannot call the public API.

Out of scope (for now)

Attachment upload, version endpoints, and webhooks are deliberately not part of v1.

Operator's view of the same feature (switches, security notes): docs/self-hosting/public-api.md.