Document what the API can do

The markdown had no writing section at all, so posts, blocks, drafts and
deletes were only visible in the admin. Adds them with every field, plus
date and number rules and the media delete in both places.
This commit is contained in:
Matthias G
2026-08-01 18:20:53 +02:00
parent ed5be8dd93
commit 997f94912e
2 changed files with 113 additions and 4 deletions
+83
View File
@@ -105,6 +105,80 @@ curl -s -X POST -H "Authorization: Bearer $TOKEN" \
Antwort: `{ "marked": 1 }`, also die Zahl der tatsächlich neu vermerkten Einträge. Unbekannte Kennungen und Einträge fremder Projekte werden still verworfen, die Antwort bleibt 200. Antwort: `{ "marked": 1 }`, also die Zahl der tatsächlich neu vermerkten Einträge. Unbekannte Kennungen und Einträge fremder Projekte werden still verworfen, die Antwort bleibt 200.
## Schreiben
Ein Zugang mit Schreibrecht legt immer Entwürfe an. Veröffentlicht wird von Hand in der Verwaltung.
### GET /api/v1/post-types
Die verwaltbaren Beitragsarten mit Schlüssel, Bezeichnung und Farbe. Vor dem Schreiben abfragen statt raten.
### POST /api/v1/posts
Legt einen Beitrag als Entwurf an.
| Feld | Pflicht | Bedeutung |
|---|---|---|
| `project` | ja | Slug des Projekts |
| `title` | ja | höchstens 200 Zeichen |
| `type` | ja | Schlüssel einer Beitragsart |
| `teaser` | nein | höchstens 500 Zeichen |
| `audience` | nein | `internal`, `customer` oder `public`, Standard `internal` |
| `author_name` | nein | sonst der Name des Zugangs |
| `slug` | nein | sonst aus dem Titel gebildet |
| `cover_media_id` | nein | Kennung eines hochgeladenen Bildes |
| `publish_at` | nein | gewünschtes Datum, siehe unten |
| `blocks` | nein | Inhalt, höchstens 100 Blöcke |
| `idempotency_key` | nein | derselbe Schlüssel liefert denselben Beitrag zurück |
```bash
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{
"project": "trakk",
"title": "SLA-Uhr zählt Feiertage nicht mehr mit",
"teaser": "Reaktionszeiten liefen über Wochenenden weiter.",
"type": "fix",
"audience": "customer",
"cover_media_id": "<medien-id>",
"publish_at": "2026-06-04T09:00:00Z",
"idempotency_key": "sla-2026-07-31"
}' \
http://localhost:4700/api/v1/posts
```
Antwort 201 mit dem angelegten Entwurf. `number` ist `null`, die Nummer kommt beim Veröffentlichen.
### PATCH /api/v1/posts/:id
Ändert Titel, Anreißer, Art, Zielgruppe, Slug, Aufmacherbild oder Datum. Nur solange nichts veröffentlicht ist, sonst 409. Zum Ändern wird die Kennung gebraucht, nicht der Slug.
```bash
curl -s -X PATCH -H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{"teaser":"Neuer Anreißer.","publish_at":"2026-06-04T09:00:00Z"}' \
http://localhost:4700/api/v1/posts/<id>
```
### PUT /api/v1/posts/:id/blocks
Setzt die Blöcke vollständig, in der übergebenen Reihenfolge. Höchstens 100.
```bash
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{"blocks":[{"type":"text","data":{"text":"Erster Absatz."}}]}' \
http://localhost:4700/api/v1/posts/<id>/blocks
```
### GET /api/v1/posts/:id
Liest den Entwurf mit allen Blöcken zurück, zum Prüfen vor der Freigabe. Mit Schreibrecht findet dieser Aufruf den Entwurf auch über den Slug.
### DELETE /api/v1/posts/:id
Löscht einen Entwurf. Veröffentlichtes lässt sich nicht löschen.
## Bilder ## Bilder
### POST /api/v1/media ### POST /api/v1/media
@@ -136,6 +210,15 @@ curl -s -X DELETE -H "Authorization: Bearer $TOKEN" \
Liefert eine Variante aus. Die Pfade stehen am Medium unter `variants`, je Eintrag mit `width`, `format` und `path`. Liefert eine Variante aus. Die Pfade stehen am Medium unter `variants`, je Eintrag mit `width`, `format` und `path`.
## Werkzeuge
| Adresse | Inhalt |
|---|---|
| `GET /api/v1/openapi.json` | maschinenlesbare Beschreibung für Postman, Bruno, Insomnia oder einen KI-Assistenten |
| `GET /api/v1/style-guide` | dieser Redaktionsleitfaden als Markdown |
Beide verlangen ein Token. In der Verwaltung unter API lädt ein angemeldeter Admin beide Dateien ohne Token herunter.
## Redaktionsleitfaden ## Redaktionsleitfaden
Wer über die API schreibt, schreibt für Menschen. Diese Regeln gelten für jeden Eintrag. Wer über die API schreibt, schreibt für Menschen. Diese Regeln gelten für jeden Eintrag.
+30 -4
View File
@@ -185,7 +185,7 @@ export default async function AdminApiPage() {
method="GET" method="GET"
path="/api/v1/posts/:slug" path="/api/v1/posts/:slug"
auth="read" auth="read"
summary="Ein einzelner Eintrag. Nicht sichtbar heißt 404, nicht 403, damit die Antwort seine Existenz nicht verrät." summary="Ein einzelner Eintrag über Slug oder Kennung. Mit Schreibrecht findet der Aufruf auch eigene Entwürfe. Nicht sichtbar heißt 404, nicht 403, damit die Antwort seine Existenz nicht verrät."
example={'curl -s -H "Authorization: Bearer $TOKEN" \\\n http://localhost:4700/api/v1/posts/regel-engine'} example={'curl -s -H "Authorization: Bearer $TOKEN" \\\n http://localhost:4700/api/v1/posts/regel-engine'}
/> />
@@ -221,8 +221,8 @@ export default async function AdminApiPage() {
method="POST" method="POST"
path="/api/v1/posts" path="/api/v1/posts"
auth="write" auth="write"
summary="Legt einen Beitrag an, immer als Entwurf. Mit idempotency_key erzeugt ein wiederholter Aufruf keinen zweiten Beitrag." summary="Legt einen Beitrag an, immer als Entwurf. Aufmacherbild und Datum gehen gleich mit. Mit idempotency_key erzeugt ein wiederholter Aufruf keinen zweiten Beitrag."
example={'curl -s -X POST -H "Authorization: Bearer $TOKEN" \\\n -H "content-type: application/json" \\\n -d \'{\n "project": "trakk",\n "title": "SLA-Uhr zählt Feiertage nicht mehr mit",\n "teaser": "Reaktionszeiten liefen über Wochenenden weiter.",\n "type": "fix",\n "audience": "customer",\n "idempotency_key": "sla-2026-07-31"\n }\' \\\n http://localhost:4700/api/v1/posts'} example={'curl -s -X POST -H "Authorization: Bearer $TOKEN" \\\n -H "content-type: application/json" \\\n -d \'{\n "project": "trakk",\n "title": "SLA-Uhr zählt Feiertage nicht mehr mit",\n "teaser": "Reaktionszeiten liefen über Wochenenden weiter.",\n "type": "fix",\n "audience": "customer",\n "cover_media_id": "<medien-id>",\n "publish_at": "2026-06-04T09:00:00Z",\n "idempotency_key": "sla-2026-07-31"\n }\' \\\n http://localhost:4700/api/v1/posts'}
/> />
<ApiEndpoint <ApiEndpoint
@@ -237,7 +237,7 @@ export default async function AdminApiPage() {
method="PATCH" method="PATCH"
path="/api/v1/posts/:id" path="/api/v1/posts/:id"
auth="write" auth="write"
summary="Ändert Titel, Anreißer, Art, Zielgruppe, Slug oder Aufmacherbild. Nur solange nichts veröffentlicht ist, sonst 409." summary="Ändert Titel, Anreißer, Art, Zielgruppe, Slug, Aufmacherbild oder Datum. Nur solange nichts veröffentlicht ist, sonst 409."
example={'curl -s -X PATCH -H "Authorization: Bearer $TOKEN" \\\n -H "content-type: application/json" \\\n -d \'{"teaser":"Neuer Anreißer."}\' \\\n http://localhost:4700/api/v1/posts/<id>'} example={'curl -s -X PATCH -H "Authorization: Bearer $TOKEN" \\\n -H "content-type: application/json" \\\n -d \'{"teaser":"Neuer Anreißer."}\' \\\n http://localhost:4700/api/v1/posts/<id>'}
/> />
@@ -264,6 +264,32 @@ export default async function AdminApiPage() {
summary="Lädt ein Bild hoch und erzeugt WebP-Varianten. Nur Bildformate, höchstens 15 MB." summary="Lädt ein Bild hoch und erzeugt WebP-Varianten. Nur Bildformate, höchstens 15 MB."
example={'curl -s -X POST -H "Authorization: Bearer $TOKEN" \\\n -F "file=@screenshot.png" \\\n -F "project=trakk" \\\n -F "alt=Spaltenmenü mit den neuen Einträgen" \\\n http://localhost:4700/api/v1/media'} example={'curl -s -X POST -H "Authorization: Bearer $TOKEN" \\\n -F "file=@screenshot.png" \\\n -F "project=trakk" \\\n -F "alt=Spaltenmenü mit den neuen Einträgen" \\\n http://localhost:4700/api/v1/media'}
/> />
<ApiEndpoint
method="DELETE"
path="/api/v1/media/:id"
auth="write"
summary="Löscht ein Bild samt Fassungen. Hängt es noch an einem Beitrag, kommt 409."
example={'curl -s -X DELETE -H "Authorization: Bearer $TOKEN" \\\n http://localhost:4700/api/v1/media/<id>'}
/>
</section>
<section className="flex flex-col gap-4">
<SectionLabel>Datum und Nummer</SectionLabel>
<div className="flex max-w-read flex-col gap-4 text-pretty text-ink-2">
<p className="m-0">
<code className="font-mono text-small text-ink">publish_at</code> nimmt eine ISO-Zeitangabe entgegen,
beim Anlegen und beim Ändern. Der Zugang veröffentlicht damit nichts, er hinterlegt nur das Datum.
Gibt ein Mensch den Beitrag frei, übernimmt Logbuch genau dieses Datum statt der aktuellen Uhrzeit.
So lassen sich ältere Meldungen richtig einsortieren.
</p>
<p className="m-0">
Die laufende Nummer wird erst beim Veröffentlichen vergeben. Ein Entwurf hat
{' '}
<code className="font-mono text-small text-ink">number: null</code>. Damit bleiben die Nummern im
Archiv lückenlos, auch wenn Entwürfe wieder verworfen werden.
</p>
</div>
</section> </section>
<section className="flex flex-col gap-4"> <section className="flex flex-col gap-4">