Ein externer Prozess soll Beiträge in WordPress anlegen — ein KI-Agent, ein Cronjob, eine Automatisierung, die Inhalte aus einer anderen Quelle einspeist. Die naheliegende Antwort lautet meistens: „Dann veröffentlichen wir eben direkt per REST API.“ Genau das ist die falsche Antwort, und in diesem Beitrag geht es darum, warum — und wie ein Redaktionsworkflow aussieht, der automatisierte Inhalte technisch sauber absichert, statt sich auf gute Absichten zu verlassen.
Der Anlass dafür ist ein Artikel auf jacomeit.de über den Aufbau eines eigenen KI-Teams. Dort geht es um das Warum und das Was: welche Aufgaben sich delegieren lassen und was das für einen Betrieb bedeutet. Hier geht es um das Wie — konkret, technisch, mit WordPress als Zielsystem.
Warum „einfach per REST API veröffentlichen“ die falsche Antwort ist
Die WordPress-REST-API kann einen Beitrag mit "status": "publish" anlegen, wenn der aufrufende Nutzer die Capability publish_posts besitzt. Technisch ist das in fünf Minuten erledigt. Fachlich öffnet man damit eine Tür, die man später nicht mehr zubekommt: Sobald ein automatisierter Prozess veröffentlichen kann, ist jeder Fehler in der Textgenerierung, jeder halluzinierte Fakt und jeder fehlerhafte Trigger sofort live. Es gibt kein Gate mehr dazwischen — und genau dieses Gate ist der eigentliche Wert eines Redaktionsprozesses.
Die Konsequenz ist eine klare Regel: Maschinell erzeugte Inhalte dürfen technisch nur als Entwurf entstehen können. Nicht als Konvention, nicht als Team-Absprache — sondern als Berechtigungsgrenze, die sich nicht versehentlich umgehen lässt.
Zugriff: Application Passwords vs. eigener Auth-Layer
Für den Zugriff externer Prozesse bietet WordPress seit 5.6 Application Passwords direkt im Core an. Für die meisten Automatisierungen reichen sie aus, mit ein paar Einschränkungen, die man kennen sollte:
- Application Passwords sind an einen Benutzer gebunden und erscheinen im WordPress-eigenen Log unter „Anwendungspasswörter“ inklusive letztem Zugriff. Bei Kompromittierung lässt sich genau dieses eine Passwort widerrufen, ohne das Benutzerkonto anzufassen. Der Nutzer, an den sie gebunden sind, sollte deshalb keine eigene, breitere Rolle wie Administrator haben, sondern die im nächsten Abschnitt beschriebene dedizierte Rolle.
- Ein eigener Auth-Layer (z. B. ein Plugin, das eigene REST-Routen unter
register_rest_route()mit einem selbst geprüften Token registriert) lohnt sich, sobald mehrere externe Prozesse mit unterschiedlichen Rechten zugreifen sollen oder eine feingranulare Protokollierung außerhalb des WordPress-Logs gebraucht wird — etwa um Rate Limits oder Idempotenz-Schlüssel (siehe unten) serverseitig zu prüfen, bevor WordPress überhaupt involviert ist.
Für den Einstieg reicht in den meisten Fällen ein Application Password auf einen dedizierten technischen Benutzer. Der Auth-Layer wird erst nötig, wenn die Automatisierung wächst.
Rechte: eine dedizierte Rolle mit minimalen Capabilities
Der technische Benutzer für die Automatisierung bekommt keine Standardrolle, sondern eine eigene, die WordPress über add_role() registriert:
add_role( 'content_automation', 'Content-Automatisierung', [
'read' => true,
'edit_posts' => true,
'upload_files' => true,
// bewusst NICHT enthalten: publish_posts, edit_others_posts, delete_posts
] );
Damit kann der Prozess Entwürfe anlegen, Medien hochladen und eigene Beiträge bearbeiten — aber nicht veröffentlichen, keine fremden Beiträge anfassen und nichts löschen. Wer schon eine Rolle mit ähnlichem Zuschnitt hat, sollte trotzdem eine eigene Rolle anlegen statt eine bestehende zu erweitern: Rollen für Menschen und Rollen für Maschinen sollten sich nicht überschneiden, weil sich sonst Berechtigungsänderungen für die eine Seite ungewollt auf die andere auswirken.
Review-Gate: Herkunft kennzeichnen und sichtbar machen
Dass ein Beitrag als Entwurf entsteht, reicht allein nicht — er muss in der Redaktionsansicht auch als das erkennbar sein, was er ist. Dafür eignet sich ein eigenes Post-Meta, das der automatisierte Prozess beim Anlegen mitschickt:
update_post_meta( $post_id, '_content_origin', 'automation:ki-team' );
update_post_meta( $post_id, '_content_origin_ref', $external_reference_id );
Im Beitragslisten-Screen lässt sich daraus mit einer eigenen Admin-Spalte (Hook manage_posts_columns und manage_posts_custom_column) ein Filter bauen, der ausschließlich maschinell erzeugte Entwürfe zeigt. Redakteure sehen so auf einen Blick, was noch auf Freigabe wartet, ohne im Beitragstext selbst danach suchen zu müssen.
Datenmodell: Blöcke statt HTML-Klumpen
Der Gutenberg-Editor speichert Beiträge als Abfolge von Blockkommentaren im post_content — jeder Block ist über HTML-Kommentare mit seinem Namen und seinen Attributen markiert. Ein automatisierter Prozess kann diese Struktur direkt erzeugen, statt einen einzigen HTML-Klumpen per wp_kses_post() durchzuschleusen:
<!-- wp:paragraph -->
<p>Einleitender Absatz des automatisiert erzeugten Beitrags.</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2>Zwischenüberschrift</h2>
<!-- /wp:heading -->
Der Aufwand dafür lohnt sich: Blöcke lassen sich im Editor nachträglich normal bearbeiten, verschieben und ergänzen. Ein HTML-Klumpen dagegen landet oft als einzelner „Klassischer Absatz“-Block, den ein Redakteur erst mühsam in echte Blöcke zerlegen muss, bevor er sinnvoll editieren kann. Wer strukturiert liefert, spart der Redaktion genau die Arbeit, die den automatisierten Prozess eigentlich entlasten sollte.
Fehlerfälle, die man einplanen muss
Ein Prozess, der regelmäßig gegen die REST API läuft, trifft früher oder später auf:
- Rate Limits — entweder von einem vorgelagerten Reverse Proxy oder von Sicherheits-Plugins, die viele Requests von einer IP als Angriff werten. Exponentielles Backoff mit einer Obergrenze ist hier Pflicht, kein Nice-to-have.
- Timeouts — besonders bei großen Medien-Uploads über
/wp/v2/media. Ein Upload, der nach 30 Sekunden abbricht, hinterlässt im Zweifel ein Attachment ohne zugehörigen Beitrag oder einen Beitrag ohne Beitragsbild. - Teil-Uploads von Medien — deshalb sollte der Prozess nach jedem Medien-Upload aktiv prüfen, ob die zurückgegebene Attachment-ID tatsächlich existiert und die erwartete Dateigröße hat, statt sich auf einen erfolgreichen HTTP-Status allein zu verlassen.
- Idempotenz — der wichtigste Punkt von allen. Jeder extern erzeugte Beitrag bekommt einen eindeutigen externen Schlüssel im Post-Meta (siehe
_content_origin_refoben). Bevor ein Beitrag angelegt wird, prüft der Prozess permeta_query, ob dieser Schlüssel schon existiert. Ohne diese Prüfung erzeugt ein erneuter Lauf nach einem Timeout zuverlässig Duplikate.
Was man messen sollte, um zu merken, dass der Prozess kaputt ist
Ein automatisierter Redaktionsworkflow braucht eigene Kennzahlen, weil er sonst lautlos ausfällt. Sinnvoll sind mindestens: die Zeit zwischen Entwurfserstellung und Freigabe durch einen Redakteur (wächst dieser Wert, stapeln sich unbearbeitete Entwürfe), die Fehlerquote bei Medien-Uploads, die Anzahl der über den Idempotenz-Schlüssel abgefangenen Duplikatversuche und schlicht: ob überhaupt noch regelmäßig neue Entwürfe mit dem erwarteten _content_origin-Wert ankommen. Ein stiller Prozess, der einfach nichts mehr liefert, fällt sonst erst auf, wenn jemand danach fragt.
Fazit
Ein externer Prozess, der Inhalte in WordPress einspeist, ist keine Frage von „geht das technisch“ — das geht immer. Die eigentliche Arbeit liegt in den Grenzen: eine Rolle, die nicht veröffentlichen kann, eine Kennzeichnung, die Herkunft sichtbar macht, ein Datenmodell, das Redakteuren die Arbeit erleichtert statt sie zu verdoppeln, und eine Fehlerbehandlung, die Duplikate und Ausfälle verhindert, statt sie zu ignorieren. Wer das von Anfang an einplant, kann Automatisierung und redaktionelle Kontrolle gleichzeitig haben — ohne dass eines das andere aushebelt.

