Öffentliche Fraktions-API v1¶
Read-only-JSON-API, mit der Fraktionen die öffentlichen Termine und Tagesordnungen ihrer Fraktionssitzungen automatisch auf der eigenen Webseite anzeigen. Eine fertige Anleitung zum Einbau steht unter Termine auf der eigenen Webseite.
Grundprinzipien¶
- Opt-in je Organisation: Die API ist standardmäßig deaktiviert und wird bewusst eingeschaltet unter Work-Portal → Organisation → Reiter „API“.
- Opakes Token statt Organisations-Slug: Der Zugriff läuft über ein zufälliges Token in der URL. Organisationen sind dadurch nicht aufzählbar. Das Token kann jederzeit erneuert werden, bisherige URLs werden dann sofort ungültig.
- Strikt öffentliche Inhalte: Ausgeliefert werden ausschließlich öffentliche, angenommene Tagesordnungspunkte (Nummer, Titel) und Sitzungs-Metadaten (Datum, Ort, Status). Niemals enthalten: nicht-öffentliche Punkte (auch nicht als Platzhalter), Protokollinhalte, Beschlüsse, Teilnehmerdaten, Video-Links oder Sitzungsentwürfe.
- Read-only: nur
GET, dazuOPTIONSfür CORS-Preflight. - CORS: Standardmäßig
Access-Control-Allow-Origin: *. Optional schränkt die Organisation die erlaubten Origins auf konkrete Webseiten ein, dann wird nur ein gelisteter Origin gespiegelt. - Caching:
Cache-Control: public, max-age=<konfigurierbar>, Standard 300 Sekunden, Schema 1 Stunde. Bitte clientseitig nicht häufiger als nötig abrufen. - Versionierung: Alle Pfade liegen stabil unter
/api/public/v1/. Inkompatible Änderungen erscheinen nur unter einer neuen Version.
Konfiguration im API-Reiter¶
| Einstellung | Standard | Bedeutung |
|---|---|---|
| Zeitfenster vergangene Sitzungen | 90 Tage | Wie weit zurück Sitzungen ausgeliefert werden |
| Zeitfenster kommende Sitzungen | 365 Tage | Wie weit voraus |
| Sitzungsort ausliefern | an | einzeln abschaltbar |
| Tagesordnung ausliefern | an | einzeln abschaltbar |
| Erlaubte Origins | alle | Kommagetrennte Liste von Webseiten-Domains |
| Cache-Dauer | 300 s | Wert des max-age |
Der Reiter zeigt außerdem eine Nutzungsstatistik (Abrufe gesamt, letzter Abruf, keine IP-Adressen) und ein fertiges Einbindungs-Snippet zum Kopieren.
Endpunkte¶
Basis: https://mandari.de/api/public/v1
| Methode | Pfad | Beschreibung |
|---|---|---|
| GET | /openapi.json | OpenAPI-3.0-Schema, ohne Token abrufbar |
| GET | /fraktionen/<token>/ | Zugangs-Info und Endpunkt-Übersicht |
| GET | /fraktionen/<token>/sitzungen/ | Terminliste: kommende und vergangene Sitzungen im konfigurierten Zeitfenster |
| GET | /fraktionen/<token>/sitzungen/<id>/ | Sitzungsdetail mit öffentlicher Tagesordnung |
Unbekannte, deaktivierte oder inaktive Zugänge liefern einheitlich 404 mit {"error": "not_found"}. Die API verrät nicht, ob ein Token existiert.
Beispiel¶
{
"api_version": "1.0",
"organization": {"name": "Fraktion Beispiel"},
"count": 2,
"meetings": [
{
"id": "6f0c…",
"title": "Fraktionssitzung März",
"number": 12,
"start": "2026-03-02T18:00:00+01:00",
"end": null,
"location": "Fraktionsbüro",
"is_virtual": false,
"status": "invited",
"cancelled": false
}
]
}
Das Sitzungsdetail (…/sitzungen/<id>/) ergänzt das Feld agenda:
{
"agenda": [
{"number": "1", "title": "Tagesordnung festlegen und letztes Protokoll genehmigen"},
{"number": "2", "title": "Spielplatz Musterstraße"}
]
}
Minimalbeispiel für die Einbindung¶
<ul id="sitzungen"></ul>
<script>
fetch("https://mandari.de/api/public/v1/fraktionen/<token>/sitzungen/")
.then((r) => r.json())
.then((data) => {
const list = document.getElementById("sitzungen");
for (const m of data.meetings) {
const li = document.createElement("li");
li.textContent = `${new Date(m.start).toLocaleString("de-DE")} – ${m.title}`;
list.appendChild(li);
}
});
</script>
Sicherheit und Datenschutz¶
- Das Token gewährt ausschließlich Lesezugriff auf ohnehin öffentliche Inhalte. Trotzdem: die URL wie ein Passwort behandeln und bei Bedarf über „API-Token erneuern“ austauschen.
- Aktivierung, Änderungen und Token-Erneuerung werden in der Änderungshistorie der Fraktionssitzungen protokolliert.
- Die Filterung auf öffentliche Inhalte wird serverseitig erzwungen und durch automatische Tests mit Antwort-Scans abgesichert.
Eigene Installation¶
Bei einer selbst betriebenen Installation ersetzt du mandari.de durch deine Domain. Die Pfade unterhalb von /api/public/v1/ bleiben gleich.