dorfteich/docs/manual/api-guide.md
Claude Fable 5 89ffbc0e4d #147: Eigene Identität in der API klar dokumentiert
GET /api/public/v1/me existiert bereits — OpenAPI-Summary nennt jetzt
ausdrücklich die User-ID, api-guide (en+de) ebenso. MCP war bereits
paritätisch (list_ponds + Token-Identität); kein neuer Endpoint nötig.

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

5.1 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 (including your user id), 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

# Only pages created/updated at or after an ISO 8601 instant (issue #148)
curl -H "$AUTH" "https://wiki.example.com/api/public/v1/ponds/team/pages?updatedSince=2026-07-01T00:00:00Z"

# 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.