REST API reference
Base URL: $COMPASS_URL (contract default http://<host>:8080/api/v1). Every
request except GET /version needs an X-Compass-Key header. All times are
ISO-8601 UTC. There is no DELETE endpoint in this reference — statements,
processes, lessons, and every other record are superseded or ended, never
removed (a handful of narrow, audited exceptions exist in code but are not
part of this contract; see the deviations note at the end of this page).
Errors always look like {"detail": "<plain-text sentence>", ...fields}. A
409 names the conflicting record; a 422 names the offending field.
Unknown fields in a write body are rejected with 422 (every input model
inherits extra="forbid").
This page lists only endpoints that exist both in the contract and in the
running code, grouped the way the contract groups them.
Auth column: ro = read-only key works, rw = read-write key required,
bruecke = also reachable with a bruecke-scoped key.
Subjekte (subjects)
| Method & path |
Auth |
What it does |
GET /subjekte |
ro |
List subjects ({id, typ, ref_id, titel}). |
POST /subjekte |
rw |
Create a subject; idempotent on (typ, ref_id). |
Sachen (subjects being tracked)
| Method & path |
Auth |
What it does |
GET /sachen |
ro |
List Sachen. Filters: bereich, stufe, suche, persona (ownership), sichtfeld (visibility), alle, limit. sichtfeld and persona answer different questions on purpose — see Concepts. Each entry carries anzahl_geltend (valid statements, all kinds). |
GET /sachen/{slug} |
ro |
One Sache plus its statements, split into gilt / abgeloest / offen / hypothesen. Each statement carries its check state (pruefstand, pruefweg_letzter_lauf): ungeprueft means no check path or never run. |
POST /sachen |
rw |
Create a Sache; idempotent on slug (200 if it already exists). persona_slug makes it a persona rule instead of a company rule. |
Regelbuch (rule book view)
| Method & path |
Auth |
What it does |
GET /regeln/wirkung |
ro |
Does the rule work? For every rule that came out of a lesson, how often the same error recurred after gilt_seit, compared against an equally long span before it (wiederholt_lehre_id, counted transitively). Four groups: wirkt (at least halved), haeufiger, zu_frueh (in force for less than 14 days — no number, not 0), nicht_messbar. alle=true adds rules without a lesson; they get grund='ohne_lehre' and no number either. No model, no estimate. |
GET /regelbuch |
ro |
Read-only "printed" view of every Sache with a currently-held rule, grouped by area (bereich). Optional bereich= filters the shown chapter; bereiche/gesamt in the response are always the full, unfiltered summary. This is Stage A of a three-stage migration of CLAUDE.md §0 into the Core — see the guide. |
Aussagen (statements)
| Method & path |
Auth |
What it does |
POST /aussagen |
rw |
Write a new statement. 409 with the current statement if a regel already holds and ersetzt is missing; 422 for an unknown Sache, a hypothesis without frist+pruefweg_slug, or a level violation. |
GET /aussagen/{id} |
ro |
One statement plus ersetzt_von/ersetzt (the supersede chain). |
POST /aussagen/{id}/status |
rw |
Change status (widerlegt|offen|vorlaeufig|gilt) — hypotheses and open/provisional statements only. |
POST /aussagen/{id}/beenden |
rw |
End a currently-held statement that has no successor (status=abgeloest, gilt_bis=now()). 409 if it isn't currently gilt. |
GET /gilt/{sache} |
ro |
The one currently-held rule, or 404 with {detail, offen}. persona= filters by visibility (403 if the Sache is outside it, unless alle=true). |
GET /gilt-am/{sache} |
ro |
What held on a given datum, with status. Same visibility behaviour as GET /gilt/{sache}. |
GET /warum/{sache} |
ro |
The rule, its decision text, its history, and the last check result. |
GET /konflikte |
ro |
Sachen where something does not line up. Every entry carries art: mehrere_geltende_aussagen (several currently-held statements of the same kind on the same Sache, with differing text, none superseding the other — plus anzahl_aussagen and anzahl_subjekte), stufenverstoss, ungepruefte_stufe, or widerspricht (a recorded WIDERSPRICHT edge, with grund, quelle and gesetzt_von). The same wording twice does not count, nor does a superseded statement, nor an ended edge. And, since 29.09.2026, neither does a Sache that has merely collected answers to different questions — every statement in the group carries quelle = frage:<n>, and all of them point to different questions. Still reported: a statement without a source, one with a foreign source, or two answers to the same question standing side by side. |
POST /widersprueche |
rw |
Two statements contradict each other — with a reason, and without a confidence score. Set by a human (quelle=mensch) or by an answered click-question (quelle=frage with frage_id); no automation writes here. 422 without a reason or for a statement against itself, 404 for an unknown statement, 409 if that pair already has a live edge. |
POST /widersprueche/{id}/beenden |
rw |
Withdraw the contradiction: beendet_am/beendet_von are set, nothing is deleted. The edge then drops out of GET /graph and GET /konflikte but stays readable. 409 if it has already been ended. |
GET /aussagen/{id}/nachweise |
ro |
Which documents support this statement. Each entry carries the document's slug, title and version, plus bestaetigt_am/bestaetigt_von — empty means nobody has looked at it yet. Withdrawn links only appear with ?beendet=true. |
POST /aussagen/{id}/nachweise |
rw |
Attach a document as supporting evidence ({dokument_slug, bemerkung?}), unconfirmed. Attaching and confirming are two steps. 409 if the same document already supports the statement, or if the document has ended. |
POST /nachweise/{id}/bestaetigen |
rw |
Record that someone has looked at this evidence. Sets timestamp and name — and nothing else: the statement's status stays as it is. 409 the second time. |
POST /nachweise/{id}/beenden |
rw |
Withdraw the link: beendet_am is set, nothing is deleted. It drops out of the list and out of GET /warum/{sache}, stays readable, and the same document may be attached again afterwards. |
Stufen (levels)
Which levels exist, and in what order, is the tenant's call — empty by default. COMPASS ships no level vocabulary of its own.
| Route |
Right |
What it does |
GET /sachen-stufen |
ro |
This tenant's active levels, most general first: [{slug, rang, titel, erstellt_am}]. An empty list means no levels are configured — and that means any level set on a Sache counts as unchecked, not as fine. |
PUT /sachen-stufen |
rw |
Sets the whole order: {stufen: [{slug, titel?}]}. Position is the rank, most general first — there is no rang field the caller could set. Anything no longer listed gets entfernt_am and stays (nothing is deleted). 422 on a malformed slug or a duplicate entry. |
Hypothesen (hypotheses)
| Method & path |
Auth |
What it does |
POST /hypothesen |
rw |
Create a hypothesis (art=hypothese, status=vorlaeufig). frist and pruefweg_slug are required. |
POST /hypothesen/{id}/bewerten |
rw |
Resolve it: bestaetigt|widerlegt|verlaengert (with neue_frist). |
GET /hypothesen |
ro |
List, optionally faellig=true for due ones. |
Prüfwege (checks)
| Method & path |
Auth |
What it does |
POST /pruefwege |
rw |
Create or upsert a check. 422 if befehl isn't on the allow-list or contains a writing pattern (>, rm, DELETE, git push, …). |
GET /pruefwege |
ro |
List checks, including the raw cause columns (erfuellt, werkzeug_fehlt_serie, reparatur_noetig, …). |
POST /pruefwege/{slug}/run |
rw |
Run it (30s timeout), store the result, write a fact. A clean run resolves any open deviation state for that check. |
POST /pruefwege/{slug}/beenden |
rw |
End a check — grund and von are required. It no longer runs in the nightly pass, no longer shows up as a deviation and no longer counts in the round; /run then answers 409. The row stays readable, including its last run. Nothing is deleted. |
Abweichungen (deviations)
A deviation means a check's result didn't match what its rule expects. The
cause is always one of three, never guessed: verstoss (violation),
werkzeug_fehlt (a known missing-tool pattern beats any other reading),
or unklar (the expectation text itself couldn't be evaluated).
| Method & path |
Auth |
What it does |
GET /abweichungen |
ro |
List deviations, with ursache, stumm (true after 3 consecutive tool-missing runs), and a ready-to-paste ticket_text. Optional seit=. |
POST /abweichungen/{pruefweg_slug}/verfolgen |
rw |
Mark it tracked, optionally attach a ticket. 409 if the check currently isn't deviating. |
POST /abweichungen/{pruefweg_slug}/regel-ueberholt |
rw |
Raise a Schaltzentrale question ("keep the rule or supersede it") from a deviation. 422 without a currently-held rule on the check. |
POST /abweichungen/{pruefweg_slug}/pruefweg-kaputt |
rw |
Mark the check itself broken — removes it from the GET /offen alert, not from GET /abweichungen. |
Prozesse (processes)
| Method & path |
Auth |
What it does |
POST /prozesse |
rw |
Create a process, or a new version if the slug exists (old version gets gilt_bis). |
GET /prozesse |
ro |
The highest version per slug. alle=true also shows superseded/ended ones. Each entry carries anzahl_schritte and anzahl_luecken. |
GET /prozesse/{slug} |
ro |
The highest version, its steps, each step's rules, and luecken (steps with neither a rule nor frei=true). ueberholt: true if ended. Plus versionen: every state of this process with gilt_seit and gilt_bis. |
GET /prozesse/{slug}?version= |
ro |
That exact state, with its steps. This is how to look up what held before a change: a step taken out of the list is still there, in full, in the previous state — nothing is deleted. A state that does not exist is 404, and the answer names the ones that do. |
GET /prozesse/{slug}/bild/{key} |
ro |
302 to the step's image, or 404. |
POST /prozesse/{slug}/beenden |
rw |
End a process with no successor version. 409 if already ended. |
Schaltzentrale (things for a human to click)
| Method & path |
Auth |
What it does |
POST /fragen |
rw |
Raise a question with at least two options, each carrying a folge (consequence). 422 otherwise. |
GET /offen |
ro |
Everything that needs a click, per source with state ruhig|unbekannt|achtung. Seven sources, including postfach (messages and tasks from agents to the human). Question entries carry bereich, gestellt_von and a folge per action. Every entry carries herkunft_art (mensch|agent|rundgang|null) from the origin the core has on record — null where no trace exists. |
GET /fragen/seit |
ro |
Answered questions since a given timestamp — decision, answer, Sache, who answered. (Not yet in the contract; see the deviations note.) |
POST /fragen/{id}/antwort |
rw, bruecke |
Answer a question; writes a decision, and supersedes the current rule if the chosen option says so. |
POST /offen/hinweis/{id}/erledigt |
rw |
Mark a Hall-of-Lessons hint as read (same effect as POST /lehren/{id}/gelesen). |
POST /offen/{typ}/{id}/erledigt |
rw |
Resolve any other open item (question, approval, design handoff, conflict). |
Kern, Graph & Gedächtnis (core render, graph, memory search)
| Method & path |
Auth |
What it does |
GET /kern/{subjekt_id} |
ro |
The held rules as Markdown, grouped by area, plus a hash. |
GET /graph |
ro |
Nodes and edges around a Sache, to a tiefe (depth). Edge types actually emitted today: GEHOERT_ZU, ERSETZT, GILT_AN, GEPRUEFT_DURCH, BETRIFFT — see the deviations note for edge types the contract describes but the graph endpoint doesn't emit yet. tage= adds a time window on gilt_seit; omitted means everything. |
GET /graph/ansichten |
ro |
Saved graph views of this tenant, newest first (alle=true includes the ended ones). A view is a remembered GET /graph request: starting node, tiefe, tage, name — the core does not recompute it. |
POST /graph/ansichten |
rw |
Create a view. 422 for an unknown sache, an empty name, tiefe outside 1..5 or tage <= 0 (this endpoint rejects instead of clamping, unlike GET /graph); 409 if one of the open views already carries this name. |
POST /graph/ansichten/{id}/beenden |
rw |
End a view — it leaves the list and stays on record (no DELETE). 409 if it is already ended. |
POST /ereignisse |
rw, bruecke |
Record an event (chat|klick|messung|pr|lauf|vorlage). Idempotent on quelle_ref. |
GET /ereignisse |
ro |
List events for a subject, optional seit=. |
GET /suche |
ro |
Full-text search over statements and events. Answer {treffer, gesamt, abgeschnitten, weiter} — gesamt counts after the visibility filter, weiter is the next page's offset or null. persona= filters statement hits by visibility; event hits are never filtered (events carry no bereich). |
GET /regelwerk/abdeckung |
ro |
{entscheidungen_gesamt, mit_aussage, ohne_aussage, quote} — how much of the decision log has a matching statement. quote is null, never 0, when the universe is empty. |
Import / Export
| Method & path |
Auth |
What it does |
POST /import/claude-md |
ro |
Preview: a deterministic, model-free scan (art=claude-md|agents-md|cursor-rules, same scan for all three) for headings with an imperative sentence beneath them, or bullets/numbered items starting with an imperative marker. Each proposal carries regel_text verbatim. Writes nothing. The file path (file) first checks size (max 1 MB) and whether the file is text at all — both 422 with feld: "file" and byte; on failure nothing is read and nothing is guessed. |
POST /import/claude-md/apply |
rw |
Write the reviewed proposal — Sache and statement in one call (regel for art=claude-md, fakt for agents-md/cursor-rules). Idempotent on quelle_ref; a conflicting existing rule or a content mismatch on the same source line is reported (konflikt/quelle_ref_widerspruch), never silently overwritten. |
POST /import/decisions |
rw, bruecke |
Bulk-import decisions keyed by a Z-number. Idempotent on quelle=Z.<n>; a content mismatch under the same key, or a second gilt rule without a resolvable ersetzt_z, goes to widerspruch — the call still returns 200. |
GET /export/claude-md-section0 |
ro |
The rule book as Markdown text. |
GET /export/agents-md |
ro |
Same content shaped for AGENTS.md. |
GET /export/cursor-rules |
ro |
A ZIP of Cursor rule files. |
Customer data export ("your data is yours")
A customer's whole holding, to take with them — unlike the tool formats above (which only pour
the currently valid rules into a foreign config format), this export carries every statement with
its history, lessons (including ended ones), memory, processes (every version), checks, questions
and decisions, personas, and company memory.
| Method & path |
Auth |
What it does |
GET /export/kopf |
ro |
Just {format_version, stand, kunde, zaehlung} — no records, fast. |
GET /export |
ro |
?format=json (NDJSON: one header line, then one line per record with teil) or ?format=markdown (readable rule book: per area/thing the rule in force, "earlier versions", decisions in chronological order, lessons in brief — no LLM, nothing invented). Both StreamingResponse. |
Never included: keys/hashes, sessions/presence. Row-level isolation as everywhere else — another
customer's own key only ever sees their own (empty) export.
Personas
| Method & path |
Auth |
What it does |
GET /personas |
ro |
List personas with counts (anzahl_regeln, anzahl_gedaechtnis), vorlage_name, slack. slack is a marker you set and read back here: COMPASS itself sends nothing to Slack. A service you connect can read it and act on it — without such a service it stays inert. For events COMPASS pushes out on its own, use the outgoing calls further down. |
GET /personas/{slug} |
ro |
Full record plus sichtfeld, regeln, gedaechtnis_letzte (10 newest). 404 for an unknown slug. |
PUT /personas/{slug} |
rw |
Full upsert — every field not sent resets to its default, this is not a partial update. |
GET /personas/{slug}/brief |
ro |
The deterministic Markdown brief. ?max_gedaechtnis=&budget=&max_lehren=. |
GET /personas/{slug}/brief/stats |
ro |
The same numbers the brief is built from, as structured data. |
GET /personas/{slug}/gedaechtnis |
ro |
Full-text search over a persona's memory (own plus visible company memory). |
POST /personas/{slug}/gedaechtnis |
rw |
Add a memory entry (fakt|feedback|ereignis). |
PATCH /personas/{slug}/gedaechtnis/{id} |
rw |
End a memory entry (gilt_bis). |
POST /personas/gedaechtnis/import |
rw |
Bulk-import memory, idempotent on quelle_ref. persona_slug=null writes company memory. |
GET /persona-vorlagen |
ro |
The template catalog. |
GET /persona-vorlagen/{slug} |
ro |
One template. |
POST /persona-vorlagen/vorschlag |
ro |
Rank up to 3 templates against a free-text need (keyword match, deterministic). |
POST /personas/from-vorlage |
rw |
Copy a template into a new persona. 409 on slug conflict. |
GET /personas/{slug}/beurteilung |
ro |
Personnel-file view: open role proposals, wins, self-reported mistakes, relapses, human feedback. |
POST /personas/{slug}/rollen-vorschlag |
rw |
Propose a change to aufgabe|koennen|stil|grenzen, backed by at least one lesson; raises a Schaltzentrale question. |
Bereiche (areas)
| Method & path |
Auth |
What it does |
GET /bereiche |
ro |
The area registry, alphabetical. |
POST /bereiche |
rw |
Add an area. 409 if it exists, 422 if the name isn't a kebab slug. |
Agents: presence and inbox
| Method & path |
Auth |
What it does |
POST /agents/herzschlag |
ro |
Heartbeat for a session (persona_slug, sitzung, client, status?). Upsert; a session counts as online for 15 minutes after its last heartbeat. |
POST /agents/offline |
ro |
Explicitly mark a session offline. |
GET /agents |
ro |
Every active persona with presence, sessions, inbox counts, and live brain-load stats. |
POST /agents/{persona}/nachrichten |
rw (or ro with von_persona set) |
Send a message. Exactly one of von_persona/von_mensch=true is required. art=auftrag additionally checks leitung/nimmt_auftraege_von. |
GET /agents/{persona}/nachrichten |
ro |
A persona's inbox; sitzung= marks undelivered messages as delivered as a side effect. |
POST /agents/nachrichten/an-mensch |
rw (or ro with von_persona set) |
A message/task from a persona TO the human (von_persona/von_sitzung required, an_persona=NULL, an_mensch=true). Appears in GET /offen as the postfach source. |
GET /agents/nachrichten |
ro |
All messages for the tenant, newest first. an_mensch=true\|false narrows to the human's inbox or to persona-to-persona messages. |
POST /agents/nachrichten/{id}/gelesen |
ro |
Mark one message read. |
POST /agents/nachrichten/{id}/status |
rw |
Set a task message's status (offen|angenommen|erledigt|abgelehnt). |
Hall of Lessons
| Method & path |
Auth |
What it does |
POST /lehren |
rw |
Write a lesson (fehler|gelungen|hinweis). ursache is required for fehler; bereich is required for all (use unsortiert if you don't know yet). Idempotent on quelle_ref. |
PATCH /lehren/{id} |
rw |
Correct the area or wording of an existing lesson. Creator only; a closed lesson stays as it is (409). A corrected field is replaced, not appended to — sending only part of the text loses the rest. The cause cannot be changed here either; if it needs to read differently, a new lesson supersedes the old one. |
GET /lehren |
ro |
List, filterable by art, status, bereich, persona (ownership), sichtfeld (visibility), q (full text), offen_hinweise. |
GET /lehren/{id} |
ro |
One lesson plus its linked question and, if promoted, its rule. |
POST /lehren/{id}/aufnehmen |
rw |
Mark it seen and endorsed (status=aufgenommen). 409 unless it was offen. |
POST /lehren/{id}/regel-vorschlagen |
rw |
Raise a Schaltzentrale question to turn it into a rule — never writes the rule directly. |
POST /lehren/{id}/beenden |
rw |
End it (gilt_bis=now()). |
POST /lehren/{id}/gelesen |
rw |
Mark a hinweis read. |
POST /lehren/{id}/antwort |
rw |
Reply to a hinweis; also writes a memory entry for its persona. |
GET /lehren/stats |
ro |
Counts by status and kind, this week's total, unread hints. |
POST /lehren/import-gedaechtnis |
rw |
One-time import of file-sourced memory feedback entries as fehler lessons. Supports trocken=true (dry run). |
Hall of Fame + Daumen (thumbs)
| Method & path |
Auth |
What it does |
GET /hall-of-fame |
ro |
Weekly leaderboard including badges. |
GET /hall-of-fame/abzeichen |
ro |
All badges, newest week first. |
GET /hall-of-fame/quellen |
ro |
Which scoring sources currently fire versus are registry-only. |
POST /daumen |
rw |
A thumbs-up from a human only — a persona as von gets 403. 409 on a second thumb from the same person on the same object. |
POST /daumen/{id}/zuruecknehmen |
rw |
Reverse a thumb with a counter-entry (409 if already reversed). |
GET /daumen |
ro |
List thumbs. |
GET /personas/{slug}/daumen |
ro |
A persona's thumbs total, this week, and 10 most recent. |
Statistik
| Method & path |
Auth |
What it does |
GET /statistik |
ro |
Six blocks (gehirn, koepfe, anwesenheit, arbeit, qualitaet, system) computed deterministically in one transaction, timestamped once as stand. |
The Rounds
(previously named "Der Verbesserer" — the German word was misleading: it
never improved anything, it only measures and proposes. "The rounds"
carries the hospital-ward sense: someone walks through at night, checks in,
notes what stands out, changes nothing.)
| Method & path |
Auth |
What it does |
GET /rundgang |
ro |
The result of 23 fixed checks across 6 categories — a number, a threshold, and affected records per check; never a guess. Response also carries modus (off/computing/computing+reading). |
POST /rundgang/melden |
rw |
If the mode is aus (off): writes nothing. Otherwise files one hinweis lesson per category that crossed its threshold. trocken=true evaluates without writing. |
GET /rundgang/einstellungen |
ro |
The tenant's current mode + a one-sentence explanation + the three levels (off/computing/computing+reading) with title/description. |
PUT /rundgang/einstellungen |
rw |
Sets the mode ({modus, von}). Default for a newly created customer: rechnend (computing). |
POST /rundgang/versenden |
rw |
The morning report: one bundled email per active member covering every still-pending delivery, respecting sofort/taeglich/nie (immediately/daily/never). |
GET /kunden/{slug}/mitgliedschaft/rundgang-einstellungen |
session |
The caller's own delivery preference (sofort/taeglich/nie) for this customer's rundgang. |
PUT /kunden/{slug}/mitgliedschaft/rundgang-einstellungen |
session |
Sets the caller's own preference ({zustellung}). Every member for themselves, no owner/manager gate. |
Webhooks — events to other systems
COMPASS calls out, your system accepts. Full guide including signature
verification and an example: Events to other systems.
| Method & path |
Auth |
What it does |
POST /webhooks |
rw |
Create a webhook: {url, ereignisse}. The response carries the geheimnis — this once only. 422 if outgoing calls are not set up on this installation, if ereignisse is empty or holds an unknown name, or if the address isn't https:// or points into a private network. |
GET /webhooks |
ro |
Your webhooks, without the secret. |
POST /webhooks/{id}/test |
rw |
Queue a delivery with the event ping, regardless of what you subscribed to. 409 if the webhook is deactivated. |
GET /webhooks/{id}/zustellungen?limit=50 |
ro |
Deliveries, newest first — attempt, status, last HTTP code, last error. This is where you look when something doesn't arrive. |
POST /webhooks/{id}/deaktivieren |
rw |
The webhook stops sending and stays readable. 409 if it already is. Nothing is deleted. |
The five event names: regel.gesetzt, regel.abgeloest, abweichung.neu,
abweichung.behoben, frage.beantwortet.
Firma (company memory)
| Method & path |
Auth |
What it does |
GET /firma |
ro |
The six company-memory fields. |
PUT /firma |
rw + person |
Update named fields. Only with a signed-in person: a key alone gets 403. An agent working inside a person's signed-in session may do it. COMPASS takes the author from the sign-in — a von sent in the body is not used. |
GET /firma/stats |
ro |
How many of the six fields are filled. |
Mandanten (tenants) & version
| Method & path |
Auth |
What it does |
GET /kunde |
ro |
The tenant the presented key belongs to ({slug, stufe, menschen, menschen_limit, name}). |
GET /version |
none |
{git_sha, gebaut_am, dienst, grund} — deliberately reachable without a key. |
Deviations from the contract found while writing this page
GET /graph emits GEHOERT_ZU, ERSETZT, GILT_AN, GEPRUEFT_DURCH,
BETRIFFT — and, since 28.09.2026, WIDERSPRICHT. This entry used to say
that WIDERSPRICHT did "not appear in the running code" and was
"documented elsewhere as derived-not-stored": the first is no longer true,
and the second was already the wrong answer — it is the only stored
edge, deliberately so (it follows from a judgement, not from a foreign
key). Still open from the contract: ZEIGT and WIRKT_IN; neither
appears in the running code today.
GET /fragen/seit (and its MCP tool seit_wann_neu) exists in the
running code but is not yet described in the contract. Documented here
because it is a stable, working read endpoint; flagged as ahead of the
contract.
GET /api/v1/health exists in the running code but is not mentioned in
the contract (which only speaks of GET /version) — left out of this
reference for that reason.
- A number of endpoints exist in the running code with no matching
description anywhere in the contract:
GET /aussagen/nachfolgerlos,
POST /aussagen/ersetzt-nachziehen, GET /sachen/aufraeum,
POST /sachen/{slug}/verabschieden, POST /sachen/{slug}/widerrufen,
GET /personas/organigramm, POST /personal-vorschlaege,
POST /personas/{slug}/verabschieden, DELETE /personas/{slug} (a
narrow, audited exception — refuses with 409 unless the persona has
zero traces), and GET/POST /gedaechtnis, GET /gedaechtnis/stats,
POST /gedaechtnis/backfill-bereiche. None of these are documented here.