diff --git a/docs/feldnachweis.md b/docs/feldnachweis.md new file mode 100644 index 0000000..2c9390e --- /dev/null +++ b/docs/feldnachweis.md @@ -0,0 +1,230 @@ +# Feldnachweis: live ermittelte Feldnamen + +Alle Angaben stammen aus echten API-Antworten vom 20.09.2026, nicht aus Dokumentation. +Rohbelege liegen als `docs/beleg-*.json` daneben. + +--- + +## 1. NeuronWriter + +Basis-URL `https://app.neuronwriter.com/neuron-api/0.5/writer`, Header `X-API-KEY`. + +### 1.1 POST /list-projects + +Aufruf mit leerem Body `{}`. + +```json +[ + {"id":"3da4d745e0e223cb","project":"3da4d745e0e223cb","name":"skrift.de","language":"German","engine":"google.de"} +] +``` + +Verwendet: `project` = `3da4d745e0e223cb`. +Anmerkung: `id` und `project` sind wertgleich, die Doku nennt nur `project`. + +### 1.2 POST /new-query + +Request: `{"project":"...","keyword":"...","engine":"google.de","language":"German"}` + +Antwort (Beleg: `beleg-01-new-query.json`): + +```json +{ + "query": "565abcca58f65e52", + "query_url": "https://app.neuronwriter.com/analysis/view/565abcca58f65e52", + "share_url": "https://app.neuronwriter.com/analysis/share/565abcca58f65e52/d8418...", + "readonly_url": "https://app.neuronwriter.com/analysis/content-preview/565abcca58f65e52/060df..." +} +``` + +**Abweichung vom Auftrag:** Das Feld heisst `query`, nicht `query_id`. +Es ist gleichzeitig Request-Parameter aller Folgeaufrufe. + +### 1.3 POST /get-query + +Request: `{"query":"565abcca58f65e52"}` + +Statuswerte real beobachtet: `in progress` -> `ready`. +Die Analyse war nach 7 Polls a 20 Sekunden fertig, also nach rund 2 Minuten. +Das konfigurierte Limit von 30 Versuchen entspricht 10 Minuten und ist reichlich. + +Top-Level-Schluessel der fertigen Antwort (42 KB): + +``` +ideas, competitors, metrics, terms, terms_txt, status, keyword, +language, engine, project, query, serp_summary, query_url, share_url, readonly_url +``` + +#### Mapping auf die im Auftrag geforderten Felder + +| Auftrag | Tatsaechlicher Pfad | Struktur | +|---|---|---| +| `query_id` | `query` | String | +| `keyword` | `keyword` | String | +| `begriffe_pflicht` | `terms.content_basic` | `[{t, usage_pc, sugg_usage:[min,max]}]` | +| `begriffe_erweitert` | `terms.content_extended` | `[{t, usage_pc, sugg_usage:[min,max]}]` | +| `fragen` | `ideas.topic_matrix` (Objekt!) + `ideas.people_also_ask` + `ideas.content_questions` + `ideas.suggest_questions` | siehe unten | +| `wortzahl_ziel` | `metrics.word_count.target` | Integer (hier 1178) | + +#### Details, die man nicht raten kann + +`terms` enthaelt genau diese Schluessel: +`title`, `desc`, `h1`, `h2`, `content_basic`, `content_extended`, `entities`. +Es gibt **kein** `terms.content`. Die Doku suggeriert das. + +Beispiel-Eintraege: + +```json +terms.content_basic[0] = {"t":"handschriftlich","usage_pc":70,"sugg_usage":[1,17]} +terms.content_extended[0] = {"t":"handgeschriebene briefe","usage_pc":40,"sugg_usage":[1,5]} +terms.h2[0] = {"t":"handschriftlich","usage_pc":50} // kein sugg_usage +terms.entities[0] = {"t":"Brief","importance":33.89,"relevance":0.53, + "confidence":1.70,"links":[["wikipedia","http://de.wikipedia.org/wiki/Brief"]]} +``` + +`sugg_usage` ist ein Array `[min, max]`, keine Zahl. +`usage_pc` ist der Anteil der Wettbewerber, die den Begriff nutzen, keine Empfehlung. + +`terms_txt` spiegelt dasselbe als Fliesstext. Schluesselnamen sind hier **nicht identisch**: +`title`, `desc_title` (nicht `desc`), `h1`, `h2`, `content_basic`, +`content_basic_w_ranges`, `content_extended`, `content_extended_w_ranges`, `entities`. +Die `_w_ranges`-Varianten enthalten die Nutzungsspanne direkt im Text +(`handschriftlich: 1-17x`) und sind als Schreibvorlage am brauchbarsten. + +`ideas` hat vier Schluessel, die sich strukturell unterscheiden: + +```json +ideas.suggest_questions = [] // war im Test LEER +ideas.people_also_ask = [{"q":"..."}] // 4 Eintraege +ideas.content_questions = [{"q":"..."}] // 28 Eintraege +ideas.topic_matrix = {"Frage als Schluessel": {"importance": 10}} // OBJEKT, 10 Eintraege +``` + +`topic_matrix` ist kein Array. Die Frage steht im Schluessel, die Gewichtung im Wert. +In der Doku taucht `topic_matrix` gar nicht auf, obwohl es die wertvollste Quelle ist: +die Fragen sind bereits nach Wichtigkeit 1 bis 10 gewichtet. +Der Workflow sortiert danach und dedupliziert gegen die anderen drei Quellen. + +`metrics` hat nur `word_count` und `readability`, beide mit `median` und `target`. +Im Test waren `median` und `target` identisch (1178 bzw. 29). + +`competitors` liefert 31 Eintraege mit +`rank, url, title, desc, headers, content_score, readability, word_count, content_len`. +`headers` ist ein Array von Zweier-Arrays: `["h2","Text der Ueberschrift"]`. +Der Workflow uebernimmt die Gliederungen der Top 5 in den Brief. + +`serp_summary` ist in der Doku nicht erwaehnt und enthaelt +`top_intent` (hier `informational`), `intent_stats`, `top_content_type` (hier `video`) +und `content_type_stats` in Prozent. + +### 1.4 POST /evaluate-content und /import-content + +Request laut Doku: `query` plus **`html`** (nicht `content_html`), optional `title`, `description`. +Antwort: `{"status":"ok","content_score":}`. +Wird in Workflow 2 live gegengeprueft. + +--- + +## 2. n8n + +- Version `1.120.4`, ermittelt ueber `GET /rest/settings` -> `data.versionCli` +- Public API unter `/api/v1` mit Header `X-N8N-API-KEY` funktioniert +- `/rest/node-types` und `/types/nodes.json` sind nicht ohne Session-Login erreichbar + +Verwendete Node-Versionen, verifiziert durch einen importierten und +erfolgreich ausgefuehrten Testworkflow: + +| Node | typeVersion | +|---|---| +| `n8n-nodes-base.manualTrigger` | 1 | +| `n8n-nodes-base.scheduleTrigger` | 1.2 | +| `n8n-nodes-base.webhook` | 2.1 | +| `n8n-nodes-base.set` | 3.4 | +| `n8n-nodes-base.code` | 2 | +| `n8n-nodes-base.if` | 2.2 | +| `n8n-nodes-base.httpRequest` | 4.3 | +| `n8n-nodes-base.splitInBatches` | 3 | +| `n8n-nodes-base.wait` | 1.1 | +| `n8n-nodes-base.noOp` | 1 | +| `n8n-nodes-base.emailSend` | 2.1 | + +Bei `splitInBatches` v3 ist **Ausgang 0 = done** und **Ausgang 1 = loop**. +Vertauschen fuehrt zu einem Workflow, der genau einmal laeuft und dann still endet. + +**Gelernt beim Testlauf:** Ein Webhook mit `responseMode: "lastNode"` haelt die +HTTP-Verbindung bis zum Workflow-Ende offen. Bei Poll-Schleifen laeuft der +Reverse Proxy (openresty) nach 60 Sekunden in ein 504, obwohl der Workflow +weiterlaeuft. Beide Workflows nutzen deshalb `responseMode: "onReceived"`. + +--- + +## 3. WordPress (skrift.de) + +Benutzer `ai-agent`, Rolle `editor`, Authentifizierung per Application Password. +Geprueft ueber `GET /wp-json/wp/v2/users/me?context=edit`. + +### 3.1 Registrierte Meta-Felder + +Ermittelt ueber `OPTIONS /wp-json/wp/v2/posts`, Pfad +`endpoints[].args.meta.properties`. 43 Schluessel registriert. + +**Vorhanden und direkt ueber `meta` beschreibbar:** + +``` +_seopress_titles_title (string) +_seopress_titles_desc (string) +_seopress_analysis_target_kw (string) +_seopress_robots_index (string) +_seopress_social_fb_title (string) +... sowie 38 weitere _seopress_* und _elementor_* Schluessel +``` + +**Nicht vorhanden:** `kurzantwort`, `faq`, `cta_ziel`, `stand_datum`. + +### 3.2 JetEngine ist auf skrift.de nicht installiert + +`GET /wp-json/` liefert diese Namespaces: + +``` +oembed/1.0, skrift/v1, elementor-one/v1, maspik/v1, elementor/v1, +elementor-pro/v1, seopress/v1, llar/v1, elementor-hello-elementor/v1, +jet-form-builder/v1, elementor/v1/documents, elementor-ai/v1, +elementor/v1/feedback, wp/v2, wp-site-health/v1, wp-block-editor/v1, wp-abilities/v1 +``` + +`jet-form-builder/v1` ist JetFormBuilder, ein anderes Plugin. Es gibt keinen +JetEngine-Namespace und `GET /wp-json/wp/v2/types` zeigt keine JetEngine-CPTs. +Die im Auftrag genannten JetEngine-Felder existieren also nicht. + +### 3.3 Cache-Endpunkt + +Das Skrift-Plugin registriert nur `GET /skrift/v1/preis`, also keinen Cache-Purge. +WP Rocket ist nicht als Namespace sichtbar. + +Nutzbar ist stattdessen ein echter, vorhandener Endpunkt: + +``` +POST /wp-json/seopress/v1/commands/clear-cache +``` + +### 3.4 Ausgangslage Inhalte + +`GET /wp-json/wp/v2/posts?status=any` liefert 0 Beitraege. +`GET /wp-json/wp/v2/categories` liefert nur `uncategorized` (ID 1). +Workflow 2 muss Kategorien also anlegen koennen, nicht nur zuordnen. + +--- + +## 4. Gitea + +Version `1.25.4`, Repo `lucas.orth/Skrift-Auto-Article`, Default-Branch `main`, leer. + +Der hinterlegte Token hat die Scopes `read:issue, read:repository`. +Schreibende Aufrufe scheitern mit: + +```json +{"message":"token does not have at least one of required scope(s), + required=[write:repository], token scope=read:issue,read:repository"} +``` + +Benoetigt wird `write:repository`.