# API guide _Deutsche Fassung: [docs/de/manual/api-guide.md](../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: ```sh 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 ```sh # 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=&label=) 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) ```sh # 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": 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 ```sh 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 ```sh 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`](../self-hosting/public-api.md)._