{"openapi":"3.1.0","info":{"title":"KI-Markt API","version":"1.0.0-beta","description":"Eine Schnittstelle für zwei Dinge: **KI-Rechenleistung** (Chat, Embeddings, Sprache, Bilder, Videos) und **die Funktionen der Plattform** (Bücher, Podcasts, Magazin, Storyfilter, Landingpages, CRM und mehr). Beides nutzt denselben Schlüssel und dieselbe Adresse.\n\n## In fünf Minuten zum ersten Aufruf\n\n1. Melde dich an. Du bekommst ein Testguthaben für die KI-Rechenleistung.\n2. Öffne in der App den Bereich **API** und erzeuge einen Schlüssel. Er beginnt mit `km_live_` und wird nur einmal angezeigt.\n3. Setze ihn als Umgebungsvariable `KM_KEY` und sende den ersten Aufruf:\n\n```bash\ncurl -X POST https://ki-markt.com/v1/chat/completions \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model\": \"toni-mittel\",\n    \"messages\": [\n        {\n            \"role\": \"user\",\n            \"content\": \"Fasse in zwei Sätzen zusammen, was ein Voicebot ist.\"\n        }\n    ]\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/chat/completions\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"model\": \"toni-mittel\",\n        \"messages\": [\n            {\n                \"role\": \"user\",\n                \"content\": \"Fasse in zwei Sätzen zusammen, was ein Voicebot ist.\",\n            },\n        ],\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/chat/completions\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"model\": \"toni-mittel\",\n      \"messages\": [\n          {\n              \"role\": \"user\",\n              \"content\": \"Fasse in zwei Sätzen zusammen, was ein Voicebot ist.\"\n          }\n      ]\n  }),\n});\nconsole.log(await antwort.json());\n```\n\n## Anmeldung\n\nJede Anfrage trägt den Schlüssel im Kopf: `Authorization: Bearer km_live_…`. Der Schlüssel gehört zu deinem Unternehmen. **Mandant und Person kommen immer aus dem Schlüssel**, nie aus dem Körper der Anfrage: Selbst wenn du eine fremde `company_id` mitschickst, arbeitet jede Funktion nur mit den Daten deines Unternehmens. Für jede Anwendung empfehlen wir einen eigenen Schlüssel. Du kannst ihn einzeln begrenzen (Monatslimit in Euro, erlaubte Größen, erlaubte IP-Adressen) und jederzeit widerrufen.\n\n## Rechte der Plattform-Funktionen\n\nEin neuer Schlüssel darf zunächst nur die KI-Rechenleistung nutzen. Plattform-Funktionen schaltest du am Schlüssel einzeln frei:\n\n| Recht | Erlaubt | Beispiele |\n|---|---|---|\n| `lesen` | Daten abrufen und durchsuchen | Bücher auflisten, Folgen lesen, Leads suchen |\n| `schreiben` | Anlegen und ändern | Buch anlegen, Kapitel ändern, Lead aktualisieren |\n| `veroeffentlichen` | Wirkt nach außen | Folge veröffentlichen |\n| `loeschen` | Unumkehrbar | Buch löschen, Lead löschen |\n\nBei **Veröffentlichen** und **Löschen** musst du den Aufruf zusätzlich mit `\"bestaetigt\": true` im Körper bestätigen. Fehlt es, antwortet die Schnittstelle mit `409` und führt nichts aus.\n\n## So sieht ein Aufruf einer Plattform-Funktion aus\n\nAlle Plattform-Funktionen sind `POST`-Aufrufe an `https://ki-markt.com/v1/platform/{bereich}/{funktion}`. Der JSON-Körper enthält die Parameter der Funktion (oder `{}`, wenn sie keine hat). Die Antwort hat immer dieselbe Hülle:\n\n```json\n{\n  \"object\": \"platform.result\",\n  \"bereich\": \"books\",\n  \"werkzeug\": \"get_books\",\n  \"stufe\": \"lesen\",\n  \"data\": { \"success\": true, \"data\": { \"...\": \"Rückgabe der Funktion\" } }\n}\n```\n\n`data.success` sagt, ob die Funktion ihre Aufgabe erledigt hat. Fachliche Fehler (zum Beispiel eine unbekannte ID) kommen mit HTTP `200` und `\"success\": false` samt Erklärung in `data`, Fehler der Schnittstelle selbst (Schlüssel, Recht, Limit) mit dem passenden HTTP-Status und dem Fehlerformat unten. Eine Übersicht aller für deinen Schlüssel erlaubten Funktionen liefert `GET https://ki-markt.com/v1/platform`.\n\n## Toni und der Kontext deines Unternehmens\n\n**Toni** ist der digitale Assistent deines Unternehmens. Du erreichst ihn per Chat, Sprache, Telefon, Uhr, Auto, App, Desktop und Büro-Dashboard, er spricht dich mit Du und Vornamen an und arbeitet im Namen deines Unternehmens. Seine Stärke ist der **Kontext**: Toni kennt aus deinen Interviews, was du tust, aus dem Wissensgraphen, wie deine Daten zusammenhängen, aus deinem Sprachprofil, wie du klingst, aus dem Geschäftsmodell, womit du Geld verdienst, aus Limbic-Typen und Keywords, wen du ansprichst und wonach gesucht wird, und aus dem Storyfilter, wie du deine Geschichte erzählst. Aus diesem Kontext entstehen Texte, Seiten, Antworten, Angebote und Abläufe, die nach deinem Unternehmen klingen statt nach Standard.\n\nDie Plattform-API gibt dir die Bausteine, mit denen Toni arbeitet: `POST /v1/platform/{bereich}/{funktion}` ruft eine Funktion eines Bereichs auf. Dein Schlüssel `km_live_…` gehört zu genau einem Unternehmen, **Mandant und Person kommen immer aus dem Schlüssel**, und seine Rechte (lesen, schreiben, veröffentlichen, löschen) bestimmen, was er darf. Die Kapitel gehen vom Fundament (Toni, Unternehmen, Interviews, Wissensgraph, Sprachprofil, Geschäftsmodell) über Psychologie und Story (Limbic, Storyfilter, Keywords) und die Ausspielung (Seiten, Karten, Podcasts, Bücher) zum Handeln (Agenten, Teams, Redaktion, CRM, Ziele) bis zu Rechten und Datenschutz.\n\n<details>\n<summary><strong>Toni, dein digitaler Assistent</strong></summary>\n\n### Wer Toni ist\n\nToni ist ein **persönlicher digitaler Assistent** auf Augenhöhe: Du-Form, Vorname, knappe Sätze, trockener Humor in kleinen Dosen und keine Chatbot-Floskeln. Dass er eine KI ist, sagt er einmal, nicht in jeder Antwort; Lageberichte liefert er nur auf Anforderung. Fachlich arbeitet Toni wie ein **Stabschef**: Er denkt mit allen Werkzeugen der Plattform mit, aber alles, was etwas verändert oder nach außen wirkt, läuft durch Regeln (siehe Kapitel „Rechte, Mandantentrennung und Datenschleuse“).\n\n### Wo du Toni erreichst\n\n| Kanal | Was es ist |\n|---|---|\n| Chat | Toni-Chat in der Web-App und als schwebendes Fenster mit Seitenkontext. Dazu „Frag den Chef“ für Fragen an das Firmenwissen. |\n| Sprache | Live-Gespräch im Browser und in den Geräte-Apps. |\n| Telefon | Toni ruft dich an (lohnende Anfrage, Rückfrage, Meldung) und telefonierende Chat-Agenten nehmen Anrufe an (Toni Call). |\n| Toni Watch | Eigene Uhr mit Sprachgespräch, Rückfragen und Aufnahmen. |\n| Toni Car | Toni im Browser des Fahrzeugs, große Bedienelemente. |\n| Toni App | Dieselbe Oberfläche auf dem Handy, gekoppelt per Code. |\n| Toni Desktop | Installierbare Chrome-/Edge-App für Mac und Windows. |\n| Toni Dashboard | Büro-Fernseher mit vier Arten: Kundensupport, Finanzen, Webseiten und Anfragen, KI-Team. |\n| Telegram | Text und Sprachnachrichten an Toni. |\n| Externe KI-Assistenten | Über das MCP-Gateway (`/mcp`) kann ein fremder Assistent Tonis Werkzeuge und, mit Durchgriff, Toni selbst nutzen. |\n\nAlle Geräte nutzen dieselbe Schnittstelle; der Verlauf von App und Web-Chat ist derselbe.\n\n### Gedächtnis und Firmenkontext\n\n- **Gedächtnis:** Mit den Werkzeugen `remember` und `forget` merkt sich Toni Dinge über dich und das Unternehmen. Nach einem Gespräch werden Erinnerungen automatisch herausgezogen; das lässt sich je Person abschalten, im Gedächtnis-Panel siehst du alles.\n- **Wissensgraph im Prompt:** Ein kompakter Auszug (je Sammlung Anzahl und Titel, höchstens rund 3.500 Zeichen) steht fest in Tonis Anweisung.\n- **Unternehmensstimme:** Sprachprofil, Kern des Storyfilters und die aktiven Angebote reisen als verbindlicher Block mit. Er übersteuert andere Stilangaben, damit Texte wie die Person klingen.\n\n### Was Toni selbst tut\n\nToni nutzt **denselben Werkzeugkatalog**, den die Plattform-API pro Bereich anbietet. Er kann im Hintergrund arbeiten, bei Bedarf Rückfragen stellen (Web, dann Uhr, dann Anruf) und sich proaktiv melden: Gutes und Schiefgelaufenes aus Redaktion und Betrieb erscheint in der Glocke. Rituale (Morgen, 13 Uhr, Abend) fassen Lage und Entscheidungen zusammen. Eigene Ziele setzt er nie, die setzt der Chef (Kapitel „Ziele und Tonis Wand“).\n\n### Berührungspunkte mit der API\n\n- Die Funktionen des Bereichs `reflections` betreffen Tonis Erkenntnisse: **Reflexionen** sind festgehaltene Erkenntnisse aus Gesprächen (`get_reflections`, `get_reflection`, `create_reflection`, `update_reflection`, `delete_reflection`, `get_reflection_statistics`), dazu die Glocke der Person (`get_notifications`, `get_notification_count`, `mark_notification_read`, `create_notification`, `delete_notification`). Die vertiefende Analyse einer Reflexion und das Besprechen im Chat laufen in der App.\n- Alle anderen Bereiche sind Tonis Werkzeuge. Ein API-Aufruf bewirkt dasselbe wie Toni im Chat, nur ohne Gespräch.\n- Stand der Plattform: Ein direkter Chat mit Toni über die API („Toni-Durchgriff“) ist noch nicht freigegeben; im MCP-Gateway gibt es ihn bereits.\n\n</details>\n\n<details>\n<summary><strong>Unternehmen, Person und Schlüssel</strong></summary>\n\n### Mandant: dein Unternehmen\n\nAlles in der Plattform gehört zu einem **Unternehmen** (Mandant). Jeder Datensatz, vom Lead über das Buch bis zum Interview, trägt die Kennung seines Unternehmens, und jede Funktion arbeitet nur mit Daten des eigenen Mandanten. Das Unternehmen selbst hat Stammdaten (Name, Anschrift, Domain, Branche, Impressum, Datenschutztext, AGB, Logo, Social-Links, Tracking-Kennungen) und erkannte Eigenschaften wie Geschäftsmodelltyp, Zielgruppentyp, Notdienst, Einsatzradius oder ob Termine nötig sind. `trigger_website_crawl` (CRM) liest Impressum, Farben, Branche und Screenshot der Webseite automatisch ein.\n\n### Person und Rollen\n\nEine **Person** (Mitglied) gehört zu einem Unternehmen. Wichtig sind:\n\n- **Inhaber:** die Person, die das Unternehmen angelegt hat. Sie darf Einstellungen mit Tragweite ändern.\n- **Firmen-Admin:** Rolle „Companyadmin“, verwaltet Mitglieder und Freigaben.\n- **Agentur:** arbeitet für Kundenfirmen und schaltet in eine Kundenfirma um, aber nur in eine, die sie selbst angelegt hat oder deren Inhaber sie ist.\n- **Plattform-Admin:** Betreiber, sieht firmenübergreifend.\n- **Partner:** Mitglieder im Partnerprogramm.\n\nEine Person mit mehreren Firmen wechselt per Klick zwischen verknüpften Konten, ohne sich neu anzumelden. Es bleibt trotzdem immer genau eine Firma aktiv.\n\n### Was „Kontext“ heißt\n\n„Kontext“ ist alles, was die Plattform über dein Unternehmen weiß und bei der Arbeit mitnimmt:\n\n| Baustein | Beantwortet die Frage … | Kapitel |\n|---|---|---|\n| Interviews und Insights | Was tut das Unternehmen, was sagt die Person? | Interviews |\n| Wissensgraph | Wie hängen Leads, Angebote, Bücher, Termine zusammen? | Wissensgraph |\n| Sprachprofil | Wie klingt die Person? | Sprachprofil |\n| Geschäftsmodell | Womit wird Geld verdient, wie wird kalkuliert? | Geschäftsmodell |\n| Limbic-Typ, Motive | Wer wird angesprochen und was bewegt ihn? | Limbic |\n| Storyfilter | Welche Geschichte wird erzählt? | Storyfilter |\n| Keywords | Wonach wird gesucht? | Keywords |\n\nDiese Bausteine werden fast immer **aus Interviews abgeleitet** und dann an Bücher, Podcasts, Seiten, Agenten und Kampagnen gehängt. Die meisten Inhaltsobjekte tragen deshalb Verweise wie `interview_id`, `storyfilter_id`, `linguistic_id`, ein Limbic-Profil und ein Limbic-Styleguide.\n\n### Der Schlüssel\n\nDein API-Schlüssel beginnt mit `km_live_` und wird einmal angezeigt. Er gehört zu deinem Unternehmen und zu der Person, die ihn erzeugt hat. `company_id` und `member_id` im Anfragekörper werden überschrieben. Funktionen, die an der Person hängen (zum Beispiel Landingcard oder Glocke), wirken auf die Person des Schlüssels. Rechte, Limits und Bestätigungen stehen im Kapitel „Rechte, Mandantentrennung und Datenschleuse“.\n\n### Dateien, Domains, Webseiten\n\nDie Domains deines Unternehmens verwaltest du in der App. Hochgeladene Firmendateien sind sichtbar für die Firma oder nur für die Person, die sie hochgeladen hat; private Dateien sehen andere nie.\n\n</details>\n\n<details>\n<summary><strong>Interviews und Insights</strong></summary>\n\n### Warum Interviews die Quelle sind\n\nInterviews sind der Rohstoff des Kontexts. Du erzählst in einem Gespräch, was dein Unternehmen tut, für wen, mit welchen Problemen und mit welchem Ergebnis. Aus dem **Transkript** entstehen Sprachprofil, Limbic-Profil, Storyfilter, Geschäftsmodell, Expertise, Angebote, Keywords und Vorschläge für Agent-Teams. Fast jedes dieser Objekte trägt deshalb ein Feld `interview_id`.\n\n### Vorlage, Interview, Transkript\n\n- **Vorlage (Template):** Einleitung, Ausleitung, ein Auftrags-Prompt und Fragen. Jede Frage hat `question`, `wanttoreach` (was du erfahren willst) und `prompt`. Neben deinen eigenen Vorlagen gibt es einen **Katalog** globaler Vorlagen, den du mit `duplicate_template` übernimmst.\n- **Interview:** entsteht aus einer Vorlage, mit Gesprächspartner, Sprache und Status. Neue Interviews haben den Status `new`; sonst ist der Status Freitext.\n- **Art der Aufnahme:** `interview` (Kundengespräch), `braindump` (Notiz nach dem Termin) und `vorort` (Aufnahme beim Kunden vor Ort).\n- **Wege hinein:** Audio-Upload, Live-Interview über einen Einladungslink mit Warteraum, reiner Text, Aufnahmen von Toni-Geräten. Audio wird transkribiert.\n\n### Was aus dem Transkript wird: Insights\n\nNach der Transkription entsteht eine strukturierte Auswertung (`insights`) in sechs Kategorien:\n\n| Kategorie | Inhalt |\n|---|---|\n| Humorvolle Aussagen | Schlagfertiges, Selbstironie, leichte Momente |\n| Persönliche Geschichten | Prägende Erlebnisse, Entstehungsgeschichten, Erfolgsbeispiele |\n| Kernbotschaften | Zentrale Erkenntnisse, Methoden, Philosophie |\n| Zitierfähige Aussagen | Prägnante Formulierungen, Visionen, Versprechen |\n| Fachliche Insights | Methoden, Prozesse, Best Practices, Fehler und Learnings |\n| Authentische Aussagen | Überzeugungen, Werte, was die Marke ausmacht |\n\nJeder Eintrag hat den Text, oft eine Zeitmarke, den Kontext und Hinweise zum Einsatz. Dazu kommen Metadaten: Hauptthemen, Zielgruppen, passende Content-Formate, Schlüsselbegriffe und offene Themen. Abgeschlossene Interviews bekommen den Status `completed`.\n\n### Qualitätsanalyse\n\nDie Plattform vergleicht das Transkript mit den Fragen der Vorlage: Ergebnis sind eine Quote (`qualityScore`, Prozent Vollständigkeit), `answeredQuestions`, `missingQuestions`, `implicitAnswers` (Antworten, die zwischen den Zeilen stecken) und das Kennzeichen `followUpRequired` für ein Folgegespräch. `get_interview_insights` liefert Insights und diese Qualitätsdaten.\n\n### Wirkung in anderen Funktionen\n\n- **Sprachprofil, Limbic-Profil, Motivprofil:** werden aus dem Transkript erzeugt (siehe jeweilige Kapitel).\n- **Keywords:** Gespräche, Anrufe und Mails können Suchbegriffe liefern; die Extraktion startet ein Mensch bewusst, nicht automatisch.\n- **Geschäftsmodell:** Angebotsprofil und Canvas lassen sich aus mehreren Interviews ableiten.\n- **Agent-Teams:** Aus Insights schlägt die Plattform Teams vor.\n- **Stand der Plattform:** Transkription und Analyse starten in der App; die API legt Vorlagen und Interviews an und liest Ergebnisse.\n\n</details>\n\n<details>\n<summary><strong>Wissensgraph</strong></summary>\n\n### Was der Wissensgraph ist\n\nDer Wissensgraph bildet die Daten deines Unternehmens als **Netz** ab. **Datensätze** (Leads, Termine, Bücher, Angebote, Kurse, Interviews, Mitglieder, Kampagnen, Testimonials …) liegen in **Sammlungen**; **Kanten** verbinden sie: ein Lead hat einen Anruf, ein Angebot stammt aus einem Interview, ein Buch hat Kapitel, ein Keyword wird von einer Kampagne angesprochen. Jeder Datensatz ist über `sammlung/schlüssel` eindeutig (zum Beispiel `leads/lead_12345`).\n\n### Sammlungen und Kanten\n\nDer Graph kennt mehr als hundert Sammlungen. Die wichtigsten sind `leads`, `appointments`, `books`, `bookchapters`, `podcasts`, `episodes`, `courses`, `offers`, `testimonials`, `interviews`, `keywords`, `keywordgroups`, `storyfilters`, `linguistics`, `limbic_profiles`, `limbic_styleguides`, `limbic_business_models`, `personality_profiles`, `portals`, `templates`, `qrcodes`, `chatagents`, `agent_teams`, `members` und `companies`. Dazu kommen Gesprächsdaten (Unterhaltungen, E-Mails, Erinnerungen) und die Sammlungen der Dateien.\n\nTypische Kanten: `belongs_to_company`, `has_lead`, `has_chapter`, `has_episode`, `has_offer`, `conducted_interview`, `from_interview`, `targets_keyword`, `has_styleguide`, `has_business_model`, `team_has_member`. Weil Kanten die Herkunft festhalten, lassen sich Fragen wie „Welche Angebote kamen aus diesem Interview?“ beantworten.\n\n### Lesen und Suchen\n\n- Überblick: `get_company_summary` (Anzahl Mitglieder, Leads, Bücher, Podcasts, Kurse, stärkste Verbindungen) und `get_company_knowledge_graph` (Tiefe 1 bis 3).\n- Nachbarn: `get_entity_context` zeigt alle Verbindungen eines Datensatzes, `find_connections` den kürzesten Pfad zwischen zwei Datensätzen.\n- Gezielt filtern: `search_graph` mit höchstens acht Bedingungen (`==`, `!=`, `<`, `<=`, `>`, `>=`, `contains`, `exists`, `missing`).\n- Chronik: `get_activity_timeline`.\n- **Wissen suchen:** `wissen_suche` durchsucht semantisch Handbuchseiten, Stories, Angebote, Produkte, Bücher, Testimonials, Interviews und Positionierung und gibt Treffer mit Auszug, Quelle und Score zurück. `wissen_graph` geht von einem Treffer zu den verknüpften Datensätzen.\n\nAlle Funktionen lesen nur und liefern ausschließlich Daten deines Mandanten. Fehler kommen mit HTTP 200 und `data.success: false`.\n\n### Wie der Graph entsteht und was er nicht ist\n\nDer Graph wird aus den Daten der Plattform **gespiegelt**; die Plattform selbst bleibt die führende Quelle. Darum kann er einem frisch geänderten Datensatz kurz hinterherhinken. Für Zahlen im Tagesgeschäft (zum Beispiel offene Leads) gilt der Live-Stand der jeweiligen Funktion, etwa `get_leads`, nicht der Graph.\n\n### Dateien als Wissen\n\nHochgeladene Firmendateien (PDF, Word, Excel, Text) werden in Abschnitte zerlegt und für die Suche eingebettet; auf Wunsch entsteht eine Analyse, die Leads, Produkte und Angebote erwähnt. Eine Datei ist `firma` (für alle der Firma) oder `privat` (nur für die Person). Private Inhalte tauchen in den allgemeinen Graph-Funktionen nie auf; die Dateisammlungen sind dort ganz ausgeblendet und nur über die Dateisuche mit Sichtprüfung erreichbar.\n\n### Wirkung\n\nTonis Firmenkontext, Agent-Teams (sie bekommen den Graph-Kontext ihres Themas), Redaktion und Chat-Agenten greifen auf den Graphen zurück.\n\n</details>\n\n<details>\n<summary><strong>Sprachprofil</strong></summary>\n\n### Zweck\n\nEin **Sprachprofil** (in der Plattform „Linguistic“) beschreibt, **wie eine Person spricht und schreibt**. Es sorgt dafür, dass Texte, Antworten und Stimmen nach dem Menschen hinter dem Unternehmen klingen. Es entsteht aus der Auswertung eines Interviews, gehört zum Unternehmen und trägt Verweise auf das Quell-Interview (`interview_id`) und die Person (`member_id`).\n\n### Aufbau des Profils\n\nDas Profil hat zwei Inhaltsfelder: `analysis` (Rohauswertung) und `profile_data` (aufbereitete Fassung). Zusätzlich gibt es Status und Sprache (`lang`). Die Listenansicht zeigt nur Kopfdaten, die Einzelansicht die Inhalte. Die Auswertung gliedert sich in:\n\n| Bereich | Inhalt |\n|---|---|\n| Lexikalisch | Wortschatz (Umfang, Niveau, Vielfalt), Fachbegriffe, Stilebene, Anredeform Sie/Du/gemischt, charakteristische Wörter |\n| Syntaktisch | Satzlänge, Komplexität, Verhältnis Haupt-/Nebensätze, Zeitformen, Modalverben, Passiv, Satzarten, Bindewörter |\n| Pragmatisch | Kommunikationsstil (direkt bis indirekt), Höflichkeit, emotionale Ausdruckskraft, Gesprächsführung, Argumentationsmuster |\n| Paraverbal | Betonung, Pausen, Sprechtempo, Lautstärke, Rhythmus |\n| Individuell | typische Redewendungen, Signature-Phrasen, vermiedene Themen, sprachliche Ticks, Besonderheiten |\n| Diskursmarker | Füllwörter, Gliederungswörter, Bewertungen, Gesprächssteuerung |\n| Psycholinguistisch | Denkweise, Entscheidungsstil, Risikobereitschaft, Detail- und Strukturbedürfnis |\n| Limbisch | dominanter und zweiter Typ auf den drei Achsen Dominanz, Stimulanz, Balance, emotionale Ansprache, Motivationsfaktoren |\n| Empfehlungen | bevorzugte Ansprache, zu Vermeidendes, Kanalwahl, Timing, Marketing-Tonalität |\n\nDazu kommt ein kurzer Titel, der den Sprachstil zusammenfasst.\n\n### Wie das Profil wirkt\n\n- **Chat-Agenten:** Ein Teil des Agenten-Auftrags ist das Sprachprofil. Daraus werden kurze Zeilen: Charakter, Ton, Satzbau, Gesprächsführung, Wendungen (höchstens sechs, nie mehr als eine je Antwort), Lieblingswörter, gemiedene Themen und „nur beim Sprechen“ (Tempo, Pausen, Füllwörter). Auch die Anrede Du oder Sie kommt aus dem Profil.\n- **Toni:** Das Profil steht im verbindlichen Unternehmensblock; Texte sollen wie die Person klingen.\n- **Inhalte:** Bücher, Hörbücher, Podcasts, Episoden, Kurse, Stories und Portale tragen ein `linguistic_id`, das die Textgenerierung steuert.\n- **Hörbücher:** Anrede und Sprechmuster prägen die Sprachausgabe.\n\n### Auswahl\n\nEin Agent kann ein Profil festlegen; ohne Festlegung gilt automatisch das neueste fertige Profil der Firma.\n\n### API-Funktionen\n\n`get_speech_profiles`, `get_speech_profile`, `update_speech_profile` (Felder gezielt ändern), `delete_speech_profile`, `summarize_speech_profile` (Kurzfassung). Das Erzeugen aus einem Interview geschieht in der App; die API liest und pflegt das Ergebnis.\n\n</details>\n\n<details>\n<summary><strong>Geschäftsmodell, Angebote und Produkte</strong></summary>\n\n### Das Geschäftsmodell\n\nDas Geschäftsmodell sagt, **womit dein Unternehmen Geld verdient und wie Angebote gerechnet werden**. Pro Unternehmen gilt das aktive Modell, gibt es keines, das neueste. Status: `draft`, `under_review`, `active`, `archived`; archivierte Modelle sind nicht änderbar. Es besteht aus vier Teilen:\n\n1. **Business-Model-Canvas:** neun Felder: Wertversprechen, Kundensegmente, Kanäle, Kundenbeziehungen, Einnahmequellen, Schlüsselressourcen, Schlüsselaktivitäten, Schlüsselpartner, Kostenstruktur.\n2. **Produkttreppe:** sechs Stufen vom günstigen zum persönlichen Angebot: Buch, Online-Kurs, Gruppen-Coaching, 1:1-Coaching, Done-for-you, Lizenzierung, dazu eine Kohärenz-Bewertung.\n3. **Limbische Einordnung:** Haupt- und Nebentyp auf den Achsen Stimulanz, Dominanz, Balance, eine Kohärenzzahl und emotionale Auslöser (siehe Kapitel Limbic).\n4. **Angebotsprofil:** die Grundlage für Kalkulation und Angebotstexte.\n\n### Das Angebotsprofil\n\n| Feld | Bedeutung |\n|---|---|\n| `business_type` | `rental`, `service`, `product_sales`, `subscription`, `project`, `mixed` |\n| `pricing_model` | Preislogik in Worten |\n| `offer_components` | Angebotsbausteine mit Rolle `mandatory`, `optional` oder `alternative` (werden per Name zusammengeführt) |\n| `default_billing_cycle` | `once`, `monthly`, `yearly` |\n| `default_contract_months` | übliche Laufzeit |\n| `ai_hints` | Regeln und Hinweise für die KI, werden angehängt, nie ersetzt |\n| `calculation_basis` | Kalkulationsgrundlage deines Unternehmens (Beträge in Cent) |\n| `story_defaults` | firmenweite Sätze für Mentor und Plan, die auf Angebotsseiten erscheinen |\n\nAuch die Herkunft ist festgehalten (`source_interview_ids`, `extracted_at`). `get_business_model` liest, `update_business_model` ändert gezielt; sagst du Toni im Chat eine Preisregel, landet sie hier.\n\n### Angebote im Katalog\n\nDer Bereich `offers` verwaltet die **Leistungen und Pakete**, die du anbietest, nicht die Angebotsbelege. Ein Eintrag hat Titel, Status (`state`, zum Beispiel `present` oder `future`), optional ein Quell-Interview und frei strukturierten Inhalt (`title`, `description`, `price`, `needs`, `targetAudience`, `processSteps`). Angebote können eine eigene Story tragen (Überschrift, Problem, Transformation, Handlungsaufforderung). Sie erscheinen auf Landingcards und Portalen.\n\n### Produkte\n\n`search_products` durchsucht den Produktkatalog semantisch (Titel, Beschreibung, Datenblatt) und kombiniert das mit harten Zahlenfiltern auf normierte Attribute wie Gewicht, Akkulaufzeit oder Preis. Nur lesend.\n\n### Angebote und Rechnungen als Belege\n\n- `draft_quote` entwirft ein Angebot, `create_invoice_draft` legt nur einen **Rechnungsentwurf** an. Preise setzt immer die Preisberechnung der Buchhaltung aus Katalogpreisen, Kalkulation und Geschäftsmodell, nie die KI frei.\n- `offene_rechnungen` und `rechnung_lesen` (Bereich `buchhaltung`) lesen nur. Versenden und Stellen bleiben beim Menschen.\n\n### Wirkung\n\nDas Geschäftsmodell steuert Angebotskalkulation, die Transformation (von ist zu soll) in der Redaktion und den Ton von Angebotsseiten.\n\n</details>\n\n<details>\n<summary><strong>Handbuch, Glossar, Expertise, Kundenstimmen</strong></summary>\n\n### Betriebshandbuch\n\nDas Handbuch besteht aus **Seiten**: Arbeitsanweisungen (SOPs), Richtlinien, Workflows, Schulungsunterlagen. Jede Seite hat Titel, Slug, Kategorie (Standard `02-sops`), Seitentyp (Standard `sop`), Status (`draft`, `approved`, `archived`, auch `active`), Hauptinhalt und optional strukturierte Teile: Ziel, Schritte, Checkliste, Kennzahlen, häufige Probleme, verwandte Seiten, Verantwortliche. Neben den Seiten deiner Firma gibt es **systemweite** Seiten (`is_internal`), die alle sehen.\n\nSeiten lassen sich in den Suchindex aufnehmen; `semantic_search_handbook` findet sie per Bedeutung („Wie gehe ich mit einer Beschwerde um?“). `summarize_handbook_page` liefert Kurzfassung, Risiken und Verbesserungsideen. Das Handbuch ist Quelle für `wissen_suche`, für Agent-Teams (erfolgreiche Seiten fließen in Vorschläge ein) und für Toni.\n\n### Glossar\n\nFachbegriffe mit Definition, zweisprachig Deutsch und Englisch (`term_de`, `definition_de`, `term_en`, `definition_en`), mit Beispielen, Synonymen, Kategorie, Status und Priorität. Neben deinen Einträgen gibt es **globale** Begriffe; sie kommen nur mit `include_global` in die Liste. Das Glossar hält Wörter in Texten, Angeboten und Antworten einheitlich.\n\n### Expertise\n\nEin Expertise-Eintrag ist **strukturiertes Fachwissen**, meist aus einem Interview gewonnen: Titel, Status (`active`, `draft`, `archived`), optional `interview_id` und frei strukturierter Inhalt. Die Zusammenfassung erkennt die Schlüssel `title`, `initial_expertise_identification`, `process_analysis`, `experience_and_learning_curve` und `recommendations_and_methods`. Chat-Agenten, Bücher, Kurse und Stories können eine Expertise als Wissensquelle referenzieren.\n\n### Kundenstimmen\n\nEin Testimonial enthält Person (`name`, `company`, `city`, `ceo`, `position`), Zitat (`quote`), optional eine Geschichte aus `problem`, `solution` und `future`, zwei Bildbeschreibungen und Bewertung `rating` (1 bis 5). Zwei Einordnungen sind wichtig:\n\n- `variante`: `schmerzloesung`, `process` oder `zitat` (wie es gezeigt wird).\n- `state`: Zeitbezug `past`, `present` oder `future`.\n\nStimmen lassen sich von Hand anlegen, aus einer Webseite importieren (`scrape_testimonials_from_url`), nach Bewertung filtern (`filter_by_rating`), auswerten (`get_testimonial_statistics`) und exportieren (`export_testimonials`). Sie erscheinen auf Portalen, Landingcards und Angebotsseiten als Beleg.\n\n### Zusammenspiel\n\nHandbuch, Glossar, Expertise und Testimonials sind das **dokumentierte Wissen**, Interviews und Wissensgraph das **erzählte und verknüpfte**. Toni und die Chat-Agenten nutzen alle vier: Das Glossar liefert die Sprache, das Handbuch die Abläufe, Expertise die Fachtiefe, Testimonials den Beweis.\n\n</details>\n\n<details>\n<summary><strong>Limbic-Typen, Motive und Psychologie-Profile</strong></summary>\n\n### Limbic in einem Satz\n\nDas **Limbic-Modell** ordnet Menschen nach ihren emotionalen Antrieben ein, nicht nach Alter oder Beruf. Die Plattform nutzt es, um Texte, Bilder, Stimmen, Farben und Angebote an den Menschen anzupassen, den du erreichen willst. Es gibt **sieben Typen**; sie sind auf den drei Achsen **Dominanz, Stimulanz und Balance** angesiedelt. Diese Achsen nutzt das Geschäftsmodell (primärer und sekundärer Typ) und das Sprachprofil (Abschnitt „limbisch“).\n\n### Die sieben Typen\n\n| Schlüssel | Name | Kern | Reizwörter |\n|---|---|---|---|\n| `traditionalist` | Traditionalist | Sicherheit, Beständigkeit, Vertrauen, Risiko vermeiden | bewährt, zuverlässig, etabliert, Qualität |\n| `harmonizer` | Harmonizer | Balance, Fairness, Gemeinschaft, Kooperation | fair, ausgewogen, Gemeinschaft, Harmonie |\n| `open` | Offener (Open-minded) | Innovation, Neugier, Lernen, Neues entdecken | innovativ, neugierig, offen, entdecken |\n| `hedonist` | Hedonist | Spaß, Genuss, Spontaneität, Erlebnis | Spaß, aufregend, spontan, lebhaft |\n| `adventurer` | Abenteurer | Risiko, Nervenkitzel, Herausforderung, Grenzen überschreiten | Abenteuer, Risiko, Herausforderung, mutig |\n| `performer` | Performer | Dominanz, Status, Erfolg, Überlegenheit, Exklusivität | Erfolg, führend, Premium, exklusiv |\n| `disciplined` | Disziplinierter | Kontrolle, Ordnung, Struktur, Effizienz | Struktur, Ordnung, Kontrolle, Disziplin |\n\nSchreibweisen: Die Plattform speichert den Schlüssel am häufigsten als `open`; in der Akademie erscheint `open_minded`, und die Hörbuch-Sprachausgabe kennt eigene Schlüssel (`harmoniser`, `stimulant`). Unbekannte Werte fallen dort auf `performer` zurück. Für eigene Integrationen bleib bei den Schlüsseln der Tabelle.\n\n### Wo `limbic_type` vorkommt\n\n- **Keyword:** Jeder Suchbegriff wird bei der Anreicherung einem Typ zugeordnet: dem Typ, dessen Motiv hinter der Suche steckt. Fehlt eine gültige Angabe, gilt `open`.\n- **Inhalte:** Bücher, Hörbücher, Podcasts, Episoden, Kurse, Stories und Portale tragen einen Typ, ein Limbic-Profil und einen Limbic-Styleguide als Vorgabe für Ton, Farbe und Bild.\n- **Hörbuch-Kapitel:** `create_chapter_audio` mit `limbic_type` formt den Kapiteltext zu einer gesprochenen Fassung um, abgestimmt auf Tempo, Betonung und Pausen des Typs.\n- **Meta-Anzeigen:** Je Kampagne entstehen drei Anzeigen (Primär, Sekundär, Mischung), gewichtet aus dem Limbic-Profil.\n\n### Profil, Styleguide, Neuro-Trigger\n\n- **Limbic-Profil:** Ergebnis der Analyse eines Interviews. Es enthält Haupt- und Nebentyp, Wert je Typ, Konfidenz, Alterseinordnung (jung, mittel, alt) mit neurochemischem Profil (Dopamin für Stimulanz, Testosteron für Dominanz, Cortisol für Balance), Zielsegmente und Empfehlungen.\n- **Limbic-Styleguide:** Gestaltungsregeln je Typ: Farbpalette, Schriften, Bildstil, Stimme und Musik, Tonalität, Designprinzipien, Formulierungen und Inhaltsformate.\n- **Neuro-Trigger-Profil:** Wirksame emotionale Auslöser mit Stärke und Potenzial; es wird aus mehreren Modellen zusammengesetzt.\n\n### Berührung mit der API\n\nStand der Plattform: Limbic-Profile, Styleguides und Motivprofile werden in der App und von Toni erzeugt und gelesen; eigene Funktionen dafür sind in der öffentlichen API noch nicht freigegeben. Der Limbic-Typ begegnet dir trotzdem überall: als Feld `limbictype` an Keywords, als Parameter `limbic_type` bei `create_chapter_audio` (Bereich `audiobooks`), im Geschäftsmodell (`business_model`) und im Sprachprofil (`speechanalysis`).\n\n</details>\n\n<details>\n<summary><strong>Luxx-/Reiss-Motive und weitere Persönlichkeitsmodelle</strong></summary>\n\n### Die 16 Lebensmotive\n\nDas **Luxx-Motivprofil** folgt dem Reiss-Modell und fragt: Was treibt diesen Menschen im Kern an, unabhängig davon, was er gerade sagt? Es bewertet 16 Motive von 0 bis 100:\n\n| Motiv (Schlüssel) | Bedeutung |\n|---|---|\n| Einfluss (`Power`) | Kontrolle, Autorität |\n| Unabhängigkeit (`Independence`) | Autonomie, eigene Entscheidungen |\n| Wissensdurst (`Curiosity`) | Lernfreude, Wissbegier |\n| Zugehörigkeit (`Acceptance`) | Akzeptanz, dazugehören |\n| Ordnung (`Order`) | Struktur, Organisation |\n| Sparsamkeit (`Saving`) | Ressourcen bewahren |\n| Ehre (`Honor`) | Integrität, Loyalität |\n| Idealismus (`Idealism`) | Werte, Sinn, Überzeugungen |\n| Geselligkeit (`Social Contact`) | Beziehungen, Gemeinschaft |\n| Familie (`Family`) | Nähe, Fürsorge |\n| Status (`Status`) | Anerkennung, Position |\n| Gerechtigkeit (`Vengeance`) | Ausgleich |\n| Romantik (`Romance`) | Liebe, emotionale Nähe |\n| Genuss (`Eating`) | Essen, kulinarische Freude |\n| Bewegung (`Physical Activity`) | Sport, Fitness |\n| Ruhe (`Tranquility`) | Ausgeglichenheit, Frieden |\n\nGespeichert werden die englischen Schlüssel. Die Stärke liest sich so: 80 bis 100 Kernmotiv, 60 bis 79 wichtig, 40 bis 59 mittel, 20 bis 39 niedrig, darunter sehr niedrig. Zu jedem Motiv gehört ein Beleg aus dem Gespräch; das Profil nennt die drei stärksten und die drei schwächsten Motive und eine Konfidenz von 0 bis 1.\n\n### Entstehung und Anpassung\n\nDas Profil entsteht aus einem Interview und gehört zu einem **Persönlichkeitsprofil**. Du kannst Motivwerte nach Rückmeldung anpassen; so wird aus einer Schätzung ein bestätigtes Bild. Der Export als JSON oder HTML ist möglich. Auch **Keywords** bekommen Motive: Ein Hauptmotiv je Suchbegriff beeinflusst, wie ein Text dazu ansetzt.\n\n### Das Persönlichkeitsprofil\n\nDas **Persönlichkeitsprofil** fasst aus einem oder mehreren Interviews zusammen, wie jemand denkt und entscheidet: Denkmuster, Zeithorizont, Umgang mit Wandel und Unsicherheit, Entscheidungsstil und -tempo, Risikoprofil, Führungsarchetyp, Konfliktstil, Werte und Prinzipien, Geld- und Zeitumgang, Routinen und Vorlieben. Es trägt eine Vollständigkeit, eine Konfidenz und die Kennzeichnung, ob ein Mensch es bestätigt hat. Toni nutzt es, um Ton und Dosierung zu wählen: Wer schnell entscheidet, bekommt weniger Vorschläge auf einmal und knappere Sätze.\n\n### Weitere Sichten im Master-Profil\n\nZusätzlich zu Limbic und Luxx kennt die Plattform drei weitere Modelle:\n\n- **DISG:** Dominant, Initiativ, Stetig, Gewissenhaft. Es beschreibt Verhalten, besonders unter Stress.\n- **Spiral Dynamics:** neun Wertestufen, von Beige, Purpur, Rot, Blau, Orange, Grün, Gelb bis Türkis und Coral. Eine Aussage wird nur gespeichert, wenn die Sicherheit ausreicht.\n- **Empathy Map:** vier Quadranten der Zielgruppe: Sagt, Denkt, Fühlt, Tut. Sie hängt an der Zielgruppen-Analyse, nicht am Persönlichkeitsprofil.\n\n### Zusammenspiel\n\nDie fünf Modelle (Limbic, Luxx, DISG, Spiral, Empathy Map) werden bei Bedarf zu einem **Master-Profil** zusammengesetzt. Es wird nicht gespeichert, damit es nie veraltet. Widersprechen sich die Modelle, gewichtet die Plattform nach Konfidenz und warnt bei geringer Stimmigkeit. Aus DISG-Stress, Luxx-Motiven, Spiral-Werten und Limbic-Emotion entsteht außerdem das **Neuro-Trigger-Profil** der wirksamsten emotionalen Auslöser.\n\nStand der Plattform: Diese Profile werden in der App und von Toni erzeugt und gelesen; eigene API-Funktionen dafür sind noch nicht freigegeben.\n\n</details>\n\n<details>\n<summary><strong>Storyfilter, Storyskript und Magazin</strong></summary>\n\n### Warum es Storyfilter gibt\n\nEin **Storyfilter** legt fest, **wer die Geschichte ist und wie sie erzählt wird**, bevor ein einziger Satz entsteht. Alle Texte, Seiten, Podcasts, Bücher und Anzeigen holen sich ihre Botschaft daraus und bleiben so auf einer Linie. Das Prinzip ist eine **gedrehte Heldenreise**: Der **Kunde ist der Held**, dein Unternehmen der **Mentor**.\n\n### Das Storyskript A bis O\n\nFünf Akte mit je drei Feldern. Die Schlüssel in der Plattform tragen die Buchstaben und bleiben unverändert:\n\n| Akt | Felder |\n|---|---|\n| 1. Ein **Held** | A Charakter (`a_character`), B Wunsch (`b_wish`), C Traum (`c_dream`) |\n| 2. hat ein **Problem** | D äußeres Problem (`d_external_problem`), E inneres Problem (`e_internal_problem`), F moralisches Problem (`f_moral_problem`) |\n| 3. trifft einen **Mentor** | G Mentor-Charakter (`g_mentor_character`), H Freundschaft (`h_friendship`), I Kompetenz (`i_competence`) |\n| 4. bekommt einen **Plan** | J Mentor-Plan (`j_mentor_plan`), K Prozess-Plan (`k_process_plan`), L Sicherheits-Plan (`l_security_plan`) |\n| 5. beginnt die **Reise** | M Handlungsaufforderung (`m_call_to_action`), N Verlust-Drama (`n_lost_drama`), O Paradies bzw. Vision (`o_vision`) |\n\nZusätzlich gibt es Bedürfnisse (`needs`), Kernbotschaft (`coremessage`), drei Schlüsselbotschaften, Wertversprechen, Kundennutzen, erwartete Ergebnisse, Ergebnis, Kosten- und Zeitrahmen sowie Hashtag- und Social-Media-Ansatz.\n\n### Was die API zeigt\n\nDie Plattform-API zeigt eine Kurzfassung. In der Liste (`get_storyfilters`) heißen die Felder ohne Präfix: `character`, `wish`, `external_problem`, `internal_problem`, `mentor`, `core_message`, `value_proposition`, `customer_benefit`, `expected_results`. Beim Schreiben (`create_storyfilter`, `update_storyfilter`) gelten `a_character`, `b_wish`, `d_external_problem`, `e_internal_problem`, `g_mentor_character`. Stand der Plattform: Die übrigen Felder pflegst du in der App.\n\n### Wo der Storyfilter wirkt\n\n- **Stories:** `storyfilter_id` an der Story bindet sie an die Linie.\n- **Podcasts, Episoden, Bücher, Portale, Angebote, Kurse:** tragen ebenfalls einen Verweis.\n- **Toni:** Der Kern des Storyfilters steckt im verbindlichen Unternehmensblock.\n- **Anzeigen und Angebotsseiten:** Texte folgen Problem, Mentor und Plan.\n\n### Stories und Magazin\n\nEine **Story** ist ein Magazin-Beitrag mit Titel, Untertitel, Text, Status, Zielkanal, Hashtags und Varianten für Social Media, E-Mail, Webseite und Vertrieb. Wichtige Verknüpfungen: `storyfilter_id` (Linie), `linguistic_id` (Sprachprofil), Limbic-Typ und -Styleguide. Der Status läuft von `draft` über `review` und `approved` zu `published` (oder `archived`). Der Zielkanal kann Webseite, Blog, Newsletter, E-Mail-Sequenz, Posting (LinkedIn, Facebook, Instagram, Xing), Drucksache, Präsentation oder Videoskript sein; Magazin-Formate sind `personal_curated`, `praxis`, `interview` und `reportage`. Eine Story erscheint auf der Webseite erst, wenn sie veröffentlicht ist **und** einer Domain zugeordnet wurde. **Story-Vorlagen** (Storytemplates) liefern Aufbau, Anweisungen und Plattformregeln je Format.\n\n### API-Funktionen\n\n`get_stories`, `get_story`, `create_story`, `update_story`, `delete_story`, `get_storyfilters`, `create_storyfilter`, `update_storyfilter`. Funktionen, die per KI erzeugen (zum Beispiel das Anwenden der Heldenreise), sind in der API derzeit nicht freigegeben.\n\n</details>\n\n<details>\n<summary><strong>Keywords, Suchabsicht und Motive</strong></summary>\n\n### Was ein Keyword hier ist\n\nEin **Keyword** ist ein Suchbegriff mit Markt- **und** Menschendaten. Die Plattform sammelt Keywords nicht nur aus Suchwerkzeugen, sondern vor allem aus **dem, was deine Kunden sagen**: Interviews, Anrufe, Mails und Besprechungsnotizen. Ein Keyword ist damit zugleich Suchanfrage, Kundenproblem und Planungseinheit für Inhalte.\n\n### Marktdaten\n\n| Feld | Bedeutung |\n|---|---|\n| `keyword` | Suchbegriff, kleingeschrieben |\n| `search_volume` | durchschnittliche Suchen pro Monat |\n| `cpc` (`cpc_average`) | durchschnittlicher Klickpreis in Werbung |\n| `competition_level` | Konkurrenz um den Begriff |\n| `main_intent` | Suchabsicht: `informational` (wissen), `commercial` (vergleichen), `transactional` (kaufen), `navigational` (eine Seite finden) |\n| `lang` | Sprache |\n| `suggest` | Vorschlag aus der Suchvervollständigung |\n\n`get_keyword_search_volume` und `get_keyword_cpc` fragen aktuelle Werte für einen Ort ab (zum Beispiel `germany`). Beide rufen einen externen Datendienst auf und sind deshalb auf 20 Aufrufe je Schlüssel und Stunde begrenzt.\n\n### Menschendaten\n\nBei der Anreicherung ergänzt die Plattform zu jedem Keyword:\n\n- `pain` (Schmerzpunkt): welches Problem der Suchende lösen will,\n- `solution`: welche Art Lösung er braucht,\n- `intention`: die tiefere Motivation,\n- `wish`: der Wunsch,\n- `needs`: das **konkrete emotionale Bedürfnis** („Die Familie auch nach dem eigenen Tod versorgen“, nicht „Sicherheit“),\n- `limbictype`: der Limbic-Typ des Suchenden (Kapitel Limbic),\n- `topicSubcategory`, `semanticTags` und verwandte Keywords.\n\nDazu kommen `state` (Zeitbezug: `past`, `present` oder `future`) und die **Herkunft**: Interview, Anruf, Mail oder Besprechung, mit Datum des Gesprächs.\n\n### Keyword-Gruppen\n\nKeywords sind in **Gruppen** organisiert (`group_id`). Eine Gruppe steht für ein Thema und trägt Titel, Schmerzpunkt, Themenkategorie, Inhaltsabsicht und Zielgruppe. Gruppen entstehen bei der Extraktion aus einem Gespräch: Das Transkript wird in Abschnitte zerlegt, zu Themen verdichtet und zu Keywords ausgebaut. Kurze Gespräche unter 300 Wörtern liefern kein verlässliches Signal und werden übersprungen. Die Extraktion startet ein Mensch bewusst je Firma.\n\n### Motive je Keyword\n\nEinem Keyword lassen sich Luxx-Motive zuordnen (zum Beispiel Status oder Ruhe) mit einem Hauptmotiv. So kann ein Text zum selben Begriff je nach Motiv anders ansetzen. Stand der Plattform: Die Zuordnung geschieht in der App und über Toni.\n\n### Wirkung\n\n- **Portale und Seiten:** Ein Portal verweist mit `keyword_id` auf sein Keyword.\n- **Kampagnen:** Suchkampagnen entstehen aus Keywords.\n- **Redaktion:** Gruppen werden zu Themenfeldern, Suchanfragen und Nachfrage steuern, welche Seite als Nächstes entsteht.\n- **Bücher:** Haupt- und Nebenkeywords fließen in Titel, SEO und Kapitel.\n\n### API-Funktionen\n\n`get_keywords` (optional nach `group_id`), `search_keywords` (mit Suchvorschlägen), `create_keyword`, `update_keyword`, `delete_keyword`, `get_keyword_search_volume`, `get_keyword_cpc`. Sie gehören zum Bereich `landingpages`.\n\n</details>\n\n<details>\n<summary><strong>Landingpages, Portale, Landingcards, QR-Codes und Kampagnen</strong></summary>\n\n### Drei Objektarten im Bereich Landingpages\n\n- **Template (Vorlage):** Name, Slug, Variante, Farben, Schriften und Meta-Angaben. Es gibt Varianten (zum Beispiel `landingpage`, `interview`, `company`, `template-bio-profile` für Landingcards) und Seitentypen wie `job`, `lead`, `vcard`, `partner`, `product` und `offer`. Das Template bestimmt das Aussehen.\n- **Portal:** die veröffentlichbare Seite oder Seitengruppe mit Titel, Slug, Domain, Sprache und Status (`draft`, `public`). Ein Portal verweist auf ein Template (`template_id`) und ein Keyword (`keyword_id`).\n- **Keyword:** siehe Kapitel Keywords.\n\n### Portal-Typen\n\nDer Portal-Typ (`portaltype`) entscheidet, welche Inhalte die Seite erzeugt: `company` (Unternehmensseite), `product`, `book`, `audiobook`, `ebook`, `course`, `podcast`, `event`, `job`, `testimonial`, `blog`, `thema`, `aggregator`, `newsletter`, `sea-landingpage` (Landingpage für Suchanzeigen) und `resonanz`. Ein **Resonanzportal** nutzt Storyfilter und Limbic-Styleguide, um Besucher nach ihrem Antrieb (zum Beispiel „Ich brauche einen Mentor“) zu führen. Daneben gibt es Sonderformen wie Rubbellos, Masterclass, Webinar und Presseportal.\n\n### Was ein Portal mitbringt\n\nEin Portal kann die Bausteine des Kontexts bündeln: Storyfilter, Limbic-Typ, -Profil und -Styleguide, Sprachprofil, Angebot, Chat-Agent, Podcast, Buch, Hörbuch, Kurs, Produkte, Newsletter-Anbindung. Veröffentlichte Seiten werden über Edge-Server ausgeliefert; Meta-Titel, Open-Graph, strukturierte Daten und eine maschinenlesbare Kurzfassung für KI-Suchen gehören dazu.\n\n### Landingcards\n\nEine **Landingcard** ist die digitale Visitenkarte (Bio-Landingpage) einer Person. Sie besteht aus Profil (Kontakt, Bio, Social-Links), einem Landingcard-Template (Farben, Texte, Abschnitte) und einem **QR-Code**, der auf sie zeigt. Besucher sehen Angebote, starten einen Chat und hinterlassen Kontaktdaten; daraus entsteht ein **Lead**. `member_id` setzt der Server aus dem Schlüssel. `bulk_create_landingcards` legt Karten für mehrere Mitglieder an; `activate_landingcard` und `deactivate_landingcard` schalten sie ein und aus.\n\n### QR-Codes\n\nEin QR-Code hat Name, Ziel-URL (`target`), Aktivstatus und einen automatisch vergebenen `short_code`. Das Bild kodiert eine **Kurzlink-Adresse** der Plattform, nicht das Ziel selbst. So kannst du das Ziel später ändern, ohne neu zu drucken. Optional hängt ein Code an einem Dokument. NFC-Tags funktionieren nach demselben Prinzip. Scans zählen als Besuchsquelle „QR“; Stand der Plattform: Die Zähler `scans_total` und `scans_unique` in `get_qrcode_analytics` sind noch Platzhalter.\n\n### Kampagnen\n\nEine **Kampagne** hat einen Provider (zum Beispiel `google`), einen Typ (zum Beispiel `search`), Aktivstatus, Budget und Verknüpfungen zu Keyword, Template und Dokument. Suchkampagnen bestehen aus **Anzeigengruppen** und **Anzeigen** mit Texten aus Storyfilter, Sprachprofil und Limbic-Profil (`create_google_search_campaign`). Meta-Kampagnen tragen Texte für Primärtext, Überschrift und Beschreibung; Bilder entstehen erst auf ausdrückliche Freigabe.\n\n### API-Funktionen\n\nLandingpages: `get_templates`, `create_template`, `copy_template`, `get_portals`, `create_portal`, `update_portal`. Karten: `get_landingcards`, `create_landingcard`, `get_landingcard_analytics`. QR: `get_qrcodes`, `create_qrcode`, `update_qrcode`, `get_qr_image` (Base64), `get_qrcode_stats`. Kampagnen: `get_campaigns`, `create_google_search_campaign`, `activate_campaign`, `pause_campaign`.\n\n</details>\n\n<details>\n<summary><strong>Chat-Agenten: Webchat, Sprache und Telefon</strong></summary>\n\n### Was ein Chat-Agent ist\n\nEin **Chat-Agent** ist ein Gesprächspartner nach außen: Er antwortet Besuchern im Webchat, spricht im Sprach-Chat und kann telefonieren. Er ist nicht Toni, sondern eine **Konfiguration**, die Toni-Technik mit dem Wesen, dem Wissen und den Regeln deines Unternehmens ausstattet. (Nicht zu verwechseln mit den Baukasten-Agenten, die Abläufe abarbeiten; siehe das folgende Kapitel.)\n\n### Aus welchen Teilen er besteht\n\nDer Assistent zum Einrichten fragt sieben Dinge ab: Wer ist er, wo arbeitet er, was soll er erreichen, wie tritt er auf, was weiß er, was darf er tun, und das Ausprobieren. Daraus entsteht das **Briefing**, bestehend aus:\n\n- **Auftrag:** Rolle (`empfang`, `beratung`, `service`, `termine`, `assistenz` oder `eigene`), Gegenüber, Ziel, Erfolgskriterium, Aufgaben, Verbote, Übergabe an Menschen, Wissen, Tabuwörter, Länge der Antworten (`kurz`, `mittel`, `ausfuehrlich`). Der Auftrag wird nach jedem Speichern automatisch formuliert; ein von Hand geschriebener Auftrag bleibt unangetastet.\n- **Sprachprofil:** Ton, Satzbau, Wendungen und Anrede aus dem Sprachprofil (Kapitel Sprachprofil).\n- **Anrede und Sprache:** Du oder Sie, Standardsprache, automatische Spracherkennung.\n- **Kanalabschnitt:** je Kanal nur ein kurzer Formatteil (Text, Stimme, Telefon).\n\nDas Briefing stammt aus einer einzigen Quelle und gilt für Webchat, Sprach-Chat, Telefon, Kundensupport und Landingcard gleichermaßen.\n\n### Typ, Stil und Gesprächsziel\n\n| Feld | Werte |\n|---|---|\n| Agententyp (`agentType`) | `book`, `audiobook`, `podcast`, `company`, `sea-landingpage`, `sales`, `support`, `custom` |\n| Gesprächsstil | `helpful`, `consultative`, `friendly`, `professional`, `empathetic`, `solution_oriented` (hilfsbereit, beratend, freundlich, professionell, einfühlsam, lösungsorientiert) |\n| Gesprächsablauf | `problem_solving`, `lead_generation`, `support`, `consultation`, `sales` |\n| Empathie | `high`, `medium`, `low` |\n| Anrede | `du`, `sie`, `strict`, `adaptive` |\n| Kanäle | `text`, `audio`, `telefon`, mehrere gleichzeitig möglich |\n\n### Wissen und Verknüpfungen\n\nEin Agent kann an ein Buch, ein Angebot, ein Keyword, einen Storyfilter, ein oder mehrere Interviews, eine Expertise, einen Beruf und ein Limbic- oder Persönlichkeitsprofil gebunden sein. Er sucht außerdem in Handbuch und Firmendateien. Der Webchat behält die letzten zwölf Nachrichten im Gedächtnis. Agenten lassen sich in einer **Hierarchie** anordnen (übergeordneter Agent, Ebene, Vererbung).\n\n### Telefon (Toni Call)\n\nTelefonierende Agenten arbeiten über einen eigenen Telefonanschluss deiner Firma oder eine Nebenstelle. Du kannst sie ohne Telefonanlage im Browser testen. Eine **Chefsekretärin**-Rolle bietet Termine an, trägt sie in den Kalender ein, legt Erinnerungen an, merkt E-Mails vor (ohne sie zu senden) und stellt auf Wunsch zum Chef durch. Gespräche werden ausgewertet und zusammengefasst; ausgehende Anrufe zählen als eigene Gesprächsart. `list_telefon_agenten` zeigt, welche Agenten telefonieren.\n\n### Gespräche ansehen\n\nDer Reiter „Gespräche“ im Toni-Chat zeigt alle Webchats und Telefonate nur lesend, nie mit Telefonnummer oder IP.\n\n### Grenzen\n\nZugangsdaten und Systemanweisungen sind für Besucher nicht lesbar. Aufrufe der Agent-Konfiguration verlangen eine Anmeldung und das Recht an der Firma.\n\n</details>\n\n<details>\n<summary><strong>Baukasten: Anwendungen und Canvas</strong></summary>\n\n### Zwei Dinge auf einem Fundament\n\nDer **Baukasten** lässt dich eigene Abläufe bauen, ohne Code: **Anwendungen** für Menschen (dieses Kapitel) und **Agenten**, die ohne Menschen arbeiten und bei Bedarf fragen (nächstes Kapitel). Beide nutzen **dieselben Werkzeuge wie Toni**, also die Funktionen der Plattform-API, und arbeiten immer mit Mandant und Rechten der Person, die sie benutzt oder auslöst. Der Baukasten generiert keinen Programmcode: Es gibt nur feste Bausteine, deren Aufbau die Plattform prüft und in Sätzen zurückmeldet, wenn etwas nicht stimmt.\n\n### Canvas: der Entwurf neben dem Chat\n\nEin **Canvas** ist eine kleine Anwendung, die neben dem Toni-Chat erscheint und sich live füllt, während Toni arbeitet. Der Canvas ist zugleich der **Entwurf einer Anwendung**: Jede Änderung (von Toni oder von dir) wird als Revision gespeichert, sodass du Schritte rückgängig machen kannst. Toni legt den ersten Wurf mit `canvas_setzen` an und ändert danach gezielt einzelne Bausteine.\n\n### Die Bausteine\n\n| Gruppe | Bausteine |\n|---|---|\n| Inhalt | `text` (Markdown), `hinweis` (Info, Tipp, Warnung, Pflicht), `ablauf` (Flussdiagramm), `tabelle`, `bild` |\n| Erfassen | `checkliste` (Punkte mit Pflicht, Foto, Kommentar), `formular` (Text, Langtext, Zahl, Datum, Auswahl, Ja/Nein, Foto), `schritte` (Wizard) |\n| Daten | `liste` und `kennzahl` holen Werte aus **lesenden** Werkzeugen, `eintraege` zeigt frühere Durchläufe |\n| Handeln | `aktion`: ein Knopf, der ein **schreibendes** Werkzeug ausführt, mit Bestätigungssatz |\n| Layout | `abschnitt` (einklappbar), `spalten` |\n\nJeder Baustein hat eine `id` und darf `sichtbar_wenn` tragen (Bedingung mit `gleich`, `ungleich`, `leer`, `nicht_leer`, `angehakt`, `groesser`, `kleiner`, `enthaelt`). Platzhalter wie `{{kontext.…}}` (Gerät oder Lead der Anwendung) und `{{formular.feld}}` setzen Werte ein; Ausdrücke und Funktionen gibt es nicht. Eine Anwendung mit erfassenden Bausteinen bekommt unten „Durchlauf abschließen“; die Werte landen als Eintrag.\n\n### Veröffentlichen und Freigeben\n\n**Veröffentlichen** erzeugt eine **unveränderliche Version**; der Entwurf läuft daneben weiter. Wer die Anwendung sieht, legt eine Freigabe fest: ganze Firma, nur Admins oder einzelne Personen. Der Dialog zeigt in Sätzen, was die Anwendung liest und tun darf. **Veröffentlichen und Freigeben macht nur ein Mensch**; Toni kann Entwürfe bauen, nicht in Betrieb nehmen.\n\n### Werkzeuge und Schutz\n\n- Datenquellen und Aktionen kommen aus dem Werkzeugkatalog der Plattform (`baukasten_werkzeuge` listet sie, getrennt nach lesen und schreiben).\n- Welches Werkzeug mit welchen Parametern läuft, steht in der Version; der Browser schickt nur Baustein-ID und Werte.\n- Gesperrt sind Löschen, Meta-Werkzeuge, Oberflächen-Werkzeuge und der Baukasten selbst.\n- Jede schreibende Ausführung steht im Aktionsprotokoll (Person, Herkunft, Werkzeug, Ergebnis; Geheimnisse geschwärzt).\n- Im Entwurf laufen Aktionen nur für Verwalter, mit dem Hinweis „Test, wird wirklich ausgeführt“.\n\n### API-Funktionen\n\n`canvas_oeffnen`, `canvas_lesen`, `canvas_setzen`, `canvas_baustein_einfuegen`, `canvas_baustein_aendern`, `canvas_baustein_entfernen`, `canvas_baustein_verschieben`, `canvas_rueckgaengig`, `anwendungen_liste`, `anwendung_oeffnen` und `baukasten_werkzeuge`. Sie bearbeiten den Entwurf; Veröffentlichen geschieht in der App.\n\n</details>\n\n<details>\n<summary><strong>Baukasten-Agenten: Schritte, Auslöser, Läufe und Leitplanken</strong></summary>\n\n### Was ein Agent ist\n\nEin **Agent** arbeitet Schritte ab, ohne dass jemand daneben sitzt, und fragt nach, sobald etwas Schreibendes oder Nach-außen-Wirkendes ansteht. Er ist ein **Entwurf**, bis ein Mensch ihn als unveränderliche **Version** veröffentlicht; nur eine aktive Version darf laufen. (Nicht zu verwechseln mit dem Chat-Agenten, der Gespräche führt.)\n\n### Briefing und Leitplanken\n\nDas **Briefing** hat ein Ziel, ein „fertig wenn“ und Anweisungen in Sätzen. Dazu kommen höchstens 20 **Leitplanken** („Nie …“, „Immer …“, „Nur …“) und das **KI-Modell** in vier Stufen (`schnell`, `ausgewogen`, `gruendlich`, `tiefgang`), pro Schritt änderbar.\n\n### Schritte und Freiheitsgrad\n\n| Typ | Freiheit | Wirkung |\n|---|---|---|\n| `werkzeug`, `entscheidung` | fest | ein Werkzeug mit festen Parametern; Bedingung mit Sprungziel |\n| `tabelle`, `pdf_werte`, `berechnen` | fest | Tabellen umformen, Werte aus PDF lesen, rechnen, ohne KI |\n| `ki` | geführt | KI-Aufgabe mit Eingaben und festen Ergebnisfeldern |\n| `datei` | geführt | Dateien des Laufs lesen (PDF, CSV, XLSX, TXT, JSON) |\n| `browser` | geführt | fremde Webseite bedienen, nur auf freigegebenen Domains |\n| `frei` | frei | KI wählt aus erlaubten Werkzeugen selbst (mit Rundengrenze) |\n| `frage` | Mensch | wartet auf Freigabe oder Eingabe |\n\nFür Wiederholungen nimmst du feste Schritte, für Spielraum geführte oder freie. Ergebnisse stehen in `{{schritte.<key>.<feld>}}`, Eingaben in `{{eingabe.<feld>}}`.\n\n### Auslöser\n\n- **Von Hand,** per Toni-Chat oder Sprache.\n- **Zeitplan:** einmalig, täglich, wöchentlich, monatlich, jährlich oder Intervall ab 15 Minuten; höchstens zehn.\n- **Plattform-Ereignis:** 24 Ereignisse in sechs Gruppen: Anfragen und Kunden (`lead.neu`, `rueckruf.gewuenscht`, `webchat.neu`), Mails, Anrufe und Tickets (`mail.neu`, `anruf.analysiert`, `ticket.neu`), Termine (`kurstermin.gebucht`, `podcast.gast_gebucht`), Angebote, Rechnungen und Abos (`angebot.angenommen`, `rechnung.bezahlt`, `rechnung.ueberfaellig`, `mahnung.faellig`), Interviews, Dateien und Anwendungen (`interview.fertig`, `datei.neu`) und Service (`wartung.meldung`).\n- **Webhook:** ein geheimer Link, der den Agenten startet.\n\nZeitplan- und Ereignisläufe arbeiten mit den Rechten der Person, die veröffentlicht hat.\n\n### Läufe, Budget, Rückfragen\n\nEin Lauf ist `wartet_auf_start`, `laeuft`, `wartet`, `angehalten`, `fertig`, `fehler` oder `abgebrochen`; jeder Schritt schreibt Ereignisse mit Begründung. Das **Budget** pro Lauf (60 Schritte, 40 KI-Aufrufe, 30 Minuten) hält den Lauf an, statt still weiterzulaufen. Schreibende Werkzeuge lösen eine **Rückfrage** aus: „Ja, einmal“ gilt nur für diese Parameter, „Ja, immer“ merkt sich das Werkzeug im Entwurf, „Nein“ hält an. Werkzeuge mit Außenwirkung (`send_`, `publish_`, `submit_`, `activate_`, `deploy_`) fragen auch bei „immer“ nach. Mit `simulation: true` läuft der Entwurf als Probe: Lesen echt, Schreiben nur angezeigt.\n\n### Ergebnis und Sicherheit\n\nAm Ende steht ein Bericht (Glocke oder Mail). Zugangsdaten für fremde Seiten erreichen die KI nie. **Veröffentlichen macht nur ein Mensch.**\n\n### API-Funktionen\n\n`agent_entwurf`, `agent_oeffnen`, `agent_briefing`, `agent_setzen`, `agent_schritt_einfuegen/_aendern/_entfernen`, `agent_lesen`, `agenten_liste`, `agent_starten`, `agent_lauf_status`, `agent_rueckfragen` (lesen), `agent_anhalten`, `agent_fortsetzen`. Beantwortet werden Rückfragen von einem Menschen in der Oberfläche, nicht über die API.\n\n</details>\n\n<details>\n<summary><strong>Agent-Teams und KI-Mitarbeiter</strong></summary>\n\n### Agent-Teams: mehrere Agenten, ein Ziel\n\nEin **Agent-Team** ist eine Gruppe von Chat-Agenten mit einer gemeinsamen Aufgabe, die miteinander **diskutieren** und dabei auf den Wissensgraphen deines Unternehmens zugreifen. Jedes Team hat Name, Ziel und Typ:\n\n| Typ | Fragestellung |\n|---|---|\n| `optimization` | Was kann verbessert werden? |\n| `implementation` | Was muss konkret umgesetzt werden? |\n| `process` | Welche Prozesse müssen aufgebaut oder verändert werden? |\n| `strategy` | Wohin soll es gehen? |\n| `custom` | frei |\n\nJedes **Mitglied** hat eine Rolle im Team (`teamRole`, `roleDescription`), einen Agententyp (zum Beispiel `seo`, `sales`, `support`, `book`, `podcast`), erlaubte Werkzeug-Kategorien (`mcpTools`), eine Sprechreihenfolge (`speakingOrder`) und das Recht, ein Gespräch zu eröffnen (`canInitiate`). Ein Team braucht mindestens zwei Mitglieder.\n\n### Wie Teams entstehen\n\n- **Aus einem Interview:** Die Plattform liest die Insights und schlägt zwei bis vier Teams vor, je aus drei Perspektiven: Optimierung, Prozess, Umsetzung. Vorhandene Chat-Agenten werden wiederverwendet.\n- **Aus dem Betriebswissen:** Handbuchseiten, erfolgreiche Reflexionen und frühere gute Läufe liefern Vorschläge ohne Interview.\n- **Von Hand oder per Toni:** `create_team` legt ein Team an, `list_teams` listet die Teams deines Unternehmens.\n- **Selbsttätig:** Ein Steuerungstakt prüft nach festen Regeln: ein kritischer Engpass bekommt ein Untersuchungsteam, ein neues Interview löst Vorschläge aus, ein aktives Team ohne Lauf bekommt eine Diskussion, freigegebene Aufgaben werden ausgeführt, schwache Läufe (Qualität unter 50) werden neu angestoßen. Nachts nur bei kritischen Engpässen.\n\n### Läufe\n\nEin **Lauf** ist eine Diskussion mit Rundenzahl- und Token-Budget und einer Qualitätsbewertung. Ergebnisse (Erkenntnisse, Aufgaben) werden im Graphen verknüpft. Aus dem Feedback schlägt die Plattform eine veränderte Identität eines Agenten vor und wendet sie erst nach menschlicher Freigabe an.\n\nStand der Plattform: Die API bietet nur `list_teams` und `create_team`; Details, Graph-Kontext und Diskussionen laufen in der App.\n\n### KI-Mitarbeiter unter Toni\n\nToni gibt Aufträge an **acht feste Figuren** weiter. Jede hat ein eigenes Aufgabengebiet und darf nur die Werkzeuge dieses Gebiets benutzen; sie arbeiten im Hintergrund und melden sich im Chat zurück.\n\n| Figur | Aufgabe | Gebiete |\n|---|---|---|\n| Ufo | Alles rund ums Telefon | Telefon-Agenten, CRM |\n| Miss Penny | Chefsekretärin | CRM, E-Mail, Termine, Handbuch |\n| Sherlock | analysiert jede Anfrage | CRM, Mitglieder, Recherche |\n| König Dagobert | Angebote, Nachfassen, Rechnungen, Mahnungen | Angebote, Buchhaltung, Katalog, Aufträge |\n| Rex | Spürhund für Belege | Dateien, E-Mail |\n| Nemo | sorgt dafür, dass man dich findet | Webseiten, Kampagnen, Landingpages, Podcasts, Stories, Testimonials, Bücher |\n| Merlin | der Weise, der alles weiß | Dateien, Wissensgraph, Handbuch, Glossar |\n| Zerberus | passt auf deine Daten auf | nimmt keine Aufträge an |\n\nDu kannst Namen und Zusatzwissen je Figur anpassen und sie ausschalten. Toni übergibt Aufträge mit `an_mitarbeiter_geben`; Figuren geben nie selbst Aufträge weiter. Außenwirkung (Senden, Veröffentlichen) lehnen sie ohne Vollmacht ab und fragen erst nach. Ein neuer Ablauf für eine Figur läuft zuerst als **Probe**; ein Mensch gibt ihn per Karte frei.\n\n</details>\n\n<details>\n<summary><strong>Redaktion, Content-Engine und Content-Ideen</strong></summary>\n\n### Was die Redaktion tut\n\nDie **Redaktion** (Content-Engine) lässt deine Webseiten und Kanäle **selbst wachsen**: Sie erkennt, wonach gesucht wird und wo Lücken sind, und lässt Toni passende Beiträge schreiben. Du siehst das als **Newsroom** mit vier Räumen: **Schreibtisch** (das Brett aus Ideen, „Toni schreibt“, deine Durchsicht, geplant und online), **Eingang** (Quellen), **Plan** (Kalender) und **Sichtbarkeit** (Überblick, Themengebiete, Suche und Lücken, Netz).\n\n### Von der Nachfrage zur Seite\n\n1. **Nachfrage:** Suchanfragen aus der Search Console, Keywords aus Gesprächen, Produkte und das Geschäftsmodell ergeben die Nachfrage. Ein Thema gilt als gefunden, wenn es aus mindestens zwei Quellenarten stammt, dreimal in 90 Tagen auftaucht oder eine echte Marktlücke ist.\n2. **Themenfeld:** Keyword-Gruppen werden zu **Themenfeldern**, diese zu **Themengebieten** mit einem **Ratgeber** (Überblicksseite) und Unterseiten je Absicht (kaufen, mieten, Vergleich, Kosten).\n3. **Platzierung:** Jede Domain bekommt eine Rolle (Hauptdomain oder Ableger); ein Thema liegt auf seiner Heimat-Domain und bis zu drei weiteren.\n4. **Abgleich:** Jede Suchanfrage wird einer Seite zugeordnet. Ergebnis je Anfrage: gut bedient, schwach, falsche Seite, Lücke oder neu.\n5. **Arbeit:** Aus Lücken werden **Arbeiten**: neue Seite, Überblicksseite, Umschreiben, Abschnitt ergänzen, Titel und Meta, Verlinkung, Übernahme einer fremden Seite. Bremsen: eine offene Arbeit je Seite, Messruhe nach Veröffentlichung, begrenzter Vorrat.\n6. **Schreiben:** Toni liefert Struktur, Vertiefung, FAQ und Bilder. Ein **Belegabgleich** prüft jeden Satz; unbelegte Sätze werden umgeschrieben oder gestrichen, der Rest steht im Prüfbericht.\n7. **Durchsicht und Veröffentlichung:** je nach Arbeitsweise.\n\n### Arbeitsweise je Domain\n\nEin einziger Regler steuert, wie viel Toni selbst darf:\n\n| Stufe | Bedeutung |\n|---|---|\n| `aus` | Toni schreibt nichts |\n| `fragt` | Toni schreibt, du schaust vor der Veröffentlichung drüber |\n| `frei` | Toni schreibt und veröffentlicht selbst |\n\nDazu kommt ein Tagesbudget für neue Vorhaben; Ruhetage und Messruhe bremsen zu schnelle Änderungen.\n\n### Eingang: Quellen\n\nIm Eingang trägst du Feeds, Webseiten oder YouTube-Kanäle ein; neue Einträge werden gegen deine Themenfelder bewertet. Passt ein Eintrag gut, wird daraus ein **Impuls** für einen Beitrag mit deiner Sicht und Verweis auf die Quelle. Selbsttätig legt Toni nur passende Beiträge an, höchstens drei je Quelle und Tag.\n\n### Content-Ideen aus dem Sparring\n\nDer Bereich `sparring` verwaltet **Content-Ideen**, die im Gespräch mit Toni entstehen oder von Hand angelegt werden. Eine Idee hat:\n\n| Feld | Bedeutung |\n|---|---|\n| `content_type` | Ziel: `podcast_episode`, `book_chapter`, `blog_post`, `social_media`, `landingpage` |\n| `keyword`, `target_audience` | Thema und Zielgruppe |\n| `urgency`, `impact` | Dringlichkeit und Wirkung, jeweils 0,0 bis 1,0 |\n| `composite_score` | Gesamtwertung zur Rangfolge |\n| Status | `active`, `in_progress`, `archived` |\n\n`get_idea_backlog` zeigt die besten Ideen, `get_content_ideas` filtert, `create_content_idea` legt eine an, `approve_idea` gibt sie zur Produktion frei, `archive_idea` räumt weg.\n\n### Wirkung\n\nKeywords liefern die Themen, Sprachprofil und Storyfilter den Ton, Geschäftsmodell und Angebote das Ziel, der Wissensgraph die Belege. Das Ergebnis erscheint als Themenseite, Magazin-Beitrag oder Posting und wird nach der Veröffentlichung gemessen: Besuche, Bots, Anfragen.\n\n</details>\n\n<details>\n<summary><strong>Podcasts, Bücher, Hörbücher und Akademie</strong></summary>\n\n### Gemeinsames Muster\n\nPodcasts, Bücher, Hörbücher und Kurse haben dieselbe Herkunft: Sie werden aus **Interview, Storyfilter, Sprachprofil, Keywords, Angebot und Limbic-Typ** abgeleitet. Fast alle tragen die Felder `interview_id`, `storyfilter_id`, `linguistic_id`, `offer_id`, `limbictype` und einen Limbic-Styleguide, damit Ton, Bild und Stimme zum Rest passen.\n\n### Podcasts und Episoden\n\nEin **Podcast** ist die Sendung: Titel, Untertitel, Beschreibung, Sprache, Rhythmus (`hourly`, `daily`, `weekly`, `monthly`), Programmart (`episodic` oder `serial`), Kategorien, Autor, Intro und Outro, Angebot mit Handlungsaufforderung. Seine Schmerzpunkt-, Lösungs- und Absichtsfelder stammen aus dem Storyfilter. Eine **Episode** gehört zu genau einem Podcast und hat Nummer, Titel, Beschreibung, Zusammenfassung, Shownotes, Transkript, Dauer und Veröffentlichungsdatum. Der Produktionsstand läuft in der App über `Idea`, `Briefing`, `Vorproduktion`, `Produktion`, `Abnahme`, `Freigegeben`; über die API ist `status` ein Freitext, gebräuchlich sind `draft`, `ready`, `scheduled`, `published`. Eine Qualitätsbewertung prüft Episoden auf Gesamtscore, riskante Inhalte, Urheberrecht, Datenschutz, Keyword-Optimierung, Struktur, Ton, Wahrheitsgehalt und Themenpassung. `schedule_episode` plant ein Datum ein, `get_rss_feed` liefert den Feed-Link. Gesprochen wird mit einer geklonten Firmenstimme.\n\n### Podcast-Gäste\n\nGäste durchlaufen die Phasen `eingeladen`, `vorbereitung`, `aufnahme`, `nachbereitung` oder `abgesagt`. Der Gast bucht seinen Termin aus **Aufnahmetagen**, die der Betrieb festlegt. `get_podcast_gaeste` und `get_podcast_aufnahmetage` lesen den Stand; `find_podcast_gast_kandidaten` sucht mögliche Gäste in Anfragen, Adressbuch und Ansprechpartnern.\n\n### Bücher\n\nEin **Buch** hat Titel, Untertitel, Autor, Beschreibung, Status (`draft`, `published`, `in_progress`), Sprache, Preis, ISBN und viele Marketingfelder (Klappentext, Kapitel-Einblicke, „Über den Autor“, SEO-Titel und -Keywords). **Kapitel** haben Titel, Text, Position (`order`) und Status. Die Buchstruktur folgt dem Storyfilter; das Limbic-Profil bestimmt die Ansprache. `get_isbn_status` prüft die ISBN, `export_book_as_pdf` startet den Export, `get_book_statistics` zählt.\n\n### Hörbücher\n\nEin **Hörbuch** hat dieselbe Gliederung (Hörbuch, Kapitel). Der Status läuft von `draft` über `in_production` und `processing` zu `ready` oder `error`. `create_chapter_audio` formt den Kapiteltext in eine **gesprochene Fassung** um, passend zum Limbic-Typ (Tempo, Betonung, Pausen, Anrede), und überschreibt den Text des Kapitels. Danach spricht die Plattform die Kapitel; sind alle `ready`, ist das Hörbuch fertig.\n\n### Akademie\n\nKurse bestehen aus **Modulen**, Module aus **Lektionen**. Ein Kurs hat Typ (`internal`, `external`, `onboarding`, `howto`), Zugang (`Public`, `Privat`), Preis oder `isfree`, Dauer, Lernziele, Zielgruppe, drei Problem- und drei Ergebnis-Felder sowie Dozent. Präsenz- oder Online-Kurse können Termine mit Ort, Teilnehmerzahl und Buchung haben. Der Status im Datenmodell ist `draft` oder `publish`.\n\n### API-Funktionen\n\n`get_podcasts`, `create_episode`, `update_episode`, `get_episode_stats`; `get_books`, `create_chapter`, `reorder_chapters`; `get_audiobooks`, `create_audiobook_chapter`; `get_courses`, `create_module`, `create_lesson`, `summarize_course`. Alle löschenden Funktionen verlangen `\"bestaetigt\": true`.\n\n</details>\n\n<details>\n<summary><strong>CRM: Leads, Kontakte, Termine und Aufgaben</strong></summary>\n\n### Lead: ein Interessent mit Herkunft\n\nEin **Lead** ist ein Interessent oder Kontakt: Name, Anrede, Firma, Kontaktdaten, Status, Pipeline und vor allem seine **Herkunft** (`funnelSource`, Landingpage, Absendeseite, Kampagnen-Kennung, Anzeigen-Klick). Dazu kommen Spam-Kennzeichen, Notizen (eine Liste mit Inhalt, Autor und Datum), Wiedervorlage und Dealwert. Ein Lead entsteht über Webformulare, Landingcards, Webchat, Anrufe, Buchungslinks oder von Hand. Intern angelegte Hilfseinträge zählen nicht als echte Anfrage.\n\n### Status, Pipelines und Workflows\n\nDer Lead-Status hängt von deiner **Pipeline** ab; die Plattform prüft nur auf Gleichheit. Eine **Pipeline** ist eine Reihe von Stufen (zum Beispiel neu, Termin, Gespräch geführt, gewonnen, verloren). `list_pipelines` zeigt deine Pipelines, `list_workflows` die hinterlegten Abläufe, `change_lead_status` verschiebt einen Lead. Die Stufe eines **Deals** setzt die Plattform bei aktivierter Automatik aus Anrufen, Mails und Angeboten selbst.\n\n### Termine\n\nEin Termin gehört zu einem Lead und hat Art (Standard `interview`), Dauer (mindestens 15, Standard 45 Minuten), Kanal, Ort oder Meeting-Link und Status: `scheduled`, `confirmed`, `completed`, `cancelled`, `no-show`. Der Termin zieht den Lead mit: **geplant oder bestätigt** setzt ihn auf `appointment`, **erledigt** auf `interviewed`, **abgesagt** auf `rejected`; bei `no-show` entscheidet der Mensch. `check_availability` zeigt freie Zeitfenster eines Tages unter Berücksichtigung der Wochenzeiten, vorhandener Kalendertermine (auch Google, Microsoft, ICS), Urlaub und Feiertagen.\n\n### E-Mail-Vorlagen und Aufgaben\n\n- **E-Mail-Vorlagen** (`list_email_templates`) haben Typen wie Allgemein, Rückruf, Follow-up und Toni-App-Einladung. Eigene Werte setzt du mit `{wert.name}`. Gesendet wird nur an die Adresse des Leads, mit SMTP deiner Firma, nach Kontaktsperre.\n- **Aufgaben** laufen in einer Liste zusammen: aus CRM, Recruiting und Toni. Status: `suggested` (Vorschlag, wartet auf Okay), `open`, `done`, `dismissed`. Beantwortete Anrufe und Mails erledigen passende Aufgaben selbst.\n- **Kontaktsperre:** Gesperrte Kontakte bekommen keine Mails und Anrufe.\n\n### Auswertung eines Leads\n\n`summarize_lead` fasst einen Lead kurz zusammen. Anrufe und Mails werden ausgewertet (Transkript, Zusammenfassung, nächste Schritte); daraus entstehen Aufgaben, Keywords und Ereignisse für Agenten (`lead.neu`, `anruf.analysiert`, `mail.neu`). Eine Anreicherung ergänzt Position, Branche, Firmengröße und Profile aus öffentlichen Quellen.\n\n### Marktplatz und Konvertierung\n\nLeads tragen Felder für Marktplatz, Zuweisung und Preis. Ein Lead kann zu einem **Mitglied** mit eigener Firma konvertiert werden (Konvertierungs-Assistent), zum Beispiel für den Zugang zur Toni-App. Die Karte „Toni-App“ am Lead stellt Zugangscode und Einladungs-Link bereit.\n\n### API-Funktionen\n\n`get_leads` (Filter Status, Pipeline, Suche), `get_lead`, `create_lead`, `update_lead`, `delete_lead`, `change_lead_status`, `get_appointments`, `schedule_appointment`, `update_appointment`, `cancel_appointment`, `list_pipelines`, `list_workflows`, `list_email_templates`, `summarize_lead`, `check_availability`, `trigger_website_crawl`.\n\n### Wirkung\n\nDer Wissensgraph verknüpft Leads mit Anrufen, Terminen, Mails und Interviews; die Redaktion misst, welche Seite Anfragen bringt; Agenten starten bei neuen Leads; Toni nennt in Berichten die Lead-Zahlen aus dem CRM, nicht aus dem Graphen.\n\n</details>\n\n<details>\n<summary><strong>Ziele und Tonis Wand</strong></summary>\n\n### Ziele des Chefs\n\nToni setzt sich keine eigenen Ziele; **Ziele setzt der Chef**. Ein Ziel hat einen Istwert, eine Prognose, eine Ampel und eine Umsetzung aus **Teilaufgaben und Schritten**. Den Istwert misst die Plattform aus dem Kennzahlverlauf ab dem Startdatum (verfügbar sind rund 90 Tage). Schritte gehören entweder Toni oder einem Menschen; menschliche Schritte erscheinen als Aufgaben in der Aufgabenliste, und der Status spiegelt sich in beide Richtungen: `offen`, `in_arbeit`, `erledigt`, `verworfen`. Toni kann Ziele vorschlagen und planen, **aktivieren darf nur der Chef**.\n\n### Tonis Wand\n\nDie **Wand** ist der Ort für Orientierung und Selbststeuerung, aufgebaut als **Kette**, die von oben nach unten zu den täglichen Handlungen führt:\n\n1. **Ergebnis:** das Ziel in einem Satz, genau ein aktives Ziel.\n2. **Warum:** was dahintersteht, mit dem **Bedürfnis**, das es bedient.\n3. **Identität:** wer du dafür sein willst.\n4. **Handlung:** tägliche Schritte mit Rhythmus (einmalig, täglich oder an bestimmten Tagen) und Strich für heute.\n\nZu einem Ziel gehören **Teilziele** (Vorschlag, offen, erreicht) und **Stationen** als Bilder. Die sechs Grundbedürfnisse sind `sicherheit`, `abwechslung`, `bedeutung`, `verbindung`, `wachstum` und `beitrag`.\n\n### Inneres System und Sitzungen\n\n- **Sitzung „Was dich antreibt“:** rund 25 Minuten, neun Schritte; sie ordnet Bedürfnisse, Regeln, Werte und Muster, und hält eine Szene fest (Ich-Form, Präsens, Ort, Geräusch, Gefühl).\n- **„Ich hänge gerade“:** eine **Intervention** von zwei bis drei Minuten in sechs Schritten; Toni erkennt Signale selbst oder du drückst den Knopf. Am Ende steht, was gewirkt hat. Mit einem Wenn-dann-Plan lässt sich ein Hindernis absichern.\n- **Morgen- und Abendroutine:** meditativ im Vollbild mit eigenem Bild und Musik, mit lebenden Sätzen aus deinem Stand.\n\n### Wer bestimmt, was gilt\n\nToni macht **Vorschläge**, ob für Ziele, Bedürfnisse, Teilziele oder Aufgaben; du entscheidest mit „Stimmt“ oder „Stimmt nicht“. Abgehakt wird eine Handlung nur, wenn du ausdrücklich sagst, dass du sie getan hast.\n\n### Wirkung\n\nDas aktive Ziel steuert Tonis Prioritäten: Berichte, Rückfragen und Vorschläge richten sich an der Kette aus. Ziele verbinden sich mit Aufgaben (`crm-…`, `recruiting-…`, `toni-…`) und Erkenntnissen.\n\nStand der Plattform: Ziele und Wand sind persönliche Daten der Chefin oder des Chefs und gehören nicht zur öffentlichen API. Du erreichst sie in der App und im Gespräch mit Toni.\n\n</details>\n\n<details>\n<summary><strong>Rechte, Mandantentrennung und Datenschleuse</strong></summary>\n\n### Mandant: die harte Trennlinie\n\nJede Funktion arbeitet nur mit den Daten **deines Unternehmens**; Unternehmen und Person kommen aus dem Schlüssel. Schickst du `company_id` oder `member_id` im Körper mit, werden sie überschrieben. Fremde IDs liefern „nicht gefunden“. Toni, Agenten und Mitarbeiter arbeiten mit Mandant und Rechten der auslösenden Person.\n\n### Rechte am Schlüssel\n\nEin neuer Schlüssel darf zunächst nur KI-Rechenleistung nutzen; Plattform-Funktionen schaltest du einzeln frei:\n\n| Recht | Erlaubt | Beispiele |\n|---|---|---|\n| `lesen` | Daten abrufen und durchsuchen | Bücher listen, Leads suchen |\n| `schreiben` | Anlegen und ändern | Kapitel ändern, Lead aktualisieren |\n| `veroeffentlichen` | wirkt nach außen | Folge veröffentlichen |\n| `loeschen` | unumkehrbar | Buch löschen, Lead löschen |\n\nDie Stufe ergibt sich aus dem Funktionsnamen (`get_`, `list_`, `search_` lesen; `delete_` löschen; `publish_`, `activate_` veröffentlichen; im Zweifel schreiben). **Löschen und Veröffentlichen verlangen zusätzlich `\"bestaetigt\": true`**; ohne diese Angabe antwortet die API mit 409 und führt nichts aus. Fehlt das Recht, kommt 403 mit dem Code `recht_fehlt`. Weiter begrenzbar: Monatslimit in Euro, erlaubte Größen und IP-Adressen, Aufrufe pro Minute, Widerruf jederzeit.\n\n### Was bewusst draußen bleibt\n\nFunktionen, die KI-Rechenleistung verbrauchen (Erzeugen, Analysieren, Transkribieren, Anreichern, Extrahieren, Vorschlagen) oder nach außen senden (Versenden, Einreichen, Ausspielen), sind derzeit nicht in der öffentlichen API. Ebenso fehlen Bereiche mit persönlichen Daten (Motivprofile, Ziele, Wand), Stellenanzeigen, Webseiten-Texte, Mitglieder, Domains, E-Mail, Anrufe und Marktplatz. Stand der Plattform, änderbar.\n\n### Datenschleuse\n\nDie **Datenschleuse** maskiert Personenbezogenes, **bevor** ein Text an eine externe KI geht, und tauscht es in der Antwort zurück. Sie erkennt Namen, Firmen, E-Mail-Adressen, Telefonnummern, IBAN, BIC, Steuernummern, Adressen, Geburtsdaten, Kartennummern, Zugangsdaten und Domains und setzt Platzhalter wie `«PERSON_1»`. Unmaskiert bleiben Städte allein, Beträge, IDs und Datumsangaben. Zwei Stufen: Muster und Stammdaten der Firma, dazu ein lokales Erkennungsmodell für unbekannte Namen. Eigene Listen („immer“, „nie maskieren“) sind möglich; Zugangsdaten, Karten und IBAN bleiben trotzdem maskiert.\n\n- **Im Toni-Chat** schaltet die Firma sie ein oder aus (Standard aus), nur der Inhaber legt um.\n- **Beim KI-Gateway der API** läuft sie je Schlüssel, Standard an.\n- **Fail closed:** Ist die Schleuse nicht erreichbar, geht nichts hinaus. Das Protokoll hält nur Art und Anzahl fest.\n\n### Vollmachten und Rückfragen\n\nTonis Handeln im Hintergrund läuft durch eine **Vollmachtsprüfung**: Beim ersten Mal erscheint eine Vorlage mit „Ja, einmal“, „Ja, immer so“ oder „Nein“. Harte Leitplanken bleiben im Code: Löschen, Geld, Massenversand, Verträge, fremde Plattformen, Tagesbudget, neue Empfänger.\n\n### Mensch am Schalter\n\nDinge mit Tragweite entscheidet ein Mensch: Agenten und Anwendungen veröffentlichen, Ziele aktivieren, Außenwirkungen freigeben. Webhooks gehen nur an freigegebene Ziele (`list_webhook_ziele`, von Firmen-Admins angelegt): https, öffentliche Adressen, signiert, ohne Weiterleitungen. Zugangsdaten für fremde Webseiten erreichen die KI nie. Firmendateien sind `firma` oder `privat`; Private sieht nur die Person selbst, Agenten und Anwendungen sehen nur Firmendateien.\n\n</details>\n\n<details>\n<summary><strong>Kundensupport und weitere Bereiche</strong></summary>\n\n### Kundensupport über alle Kanäle\n\nDer **Kundensupport** sammelt Nachrichten aus E-Mail (auch mehreren Postfächern), Shop-Plattformen, Kommentaren und Messengern in einer Oberfläche, die über einen Arbeitsplatz im Browser bedient wird. Je Kanal gilt eine **Arbeitsweise**:\n\n| Stufe | Wirkung |\n|---|---|\n| `aus` | Kanal wird nicht bearbeitet |\n| `fragt` | Toni schlägt eine Antwort vor, ein Mensch sendet |\n| `frei` | Toni antwortet allein, aber nur in eng umrissenen Fällen |\n| `simulation` | Alles wird durchgerechnet, nichts gesendet |\n\nAutomatisch antwortet Toni nur, wenn Absicht (zum Beispiel Statusfrage), Identität des Kunden (E-Mail oder Telefon) und ein frischer Datensatz aus der Faktenquelle zusammenpassen. Heikle Anliegen (zum Beispiel Reklamation oder Rückzahlung) und Wörter wie Anwalt oder Anzeige gehen immer an einen Menschen und erzeugen ein Ticket. Eine **Frühwarnung** meldet Ansturm, Beschwerdehäufung, Eskalationswörter, Stau und stille Kanäle, ohne Kundentext in Mail oder Glocke. Aus freigegebenen und korrigierten Antworten **lernt** das System pro Firma, nie firmenübergreifend; Fakten bleiben die einzige Wahrheit.\n\nAPI: `kundensupport_suche` findet ähnliche Kundenthemen samt unserer Antwort, `kundensupport_muster` gruppiert wiederkehrende Anliegen der letzten Tage. Beide lesen nur.\n\n### E-Mail-Vorlagen und Dateien erzeugen\n\nDer Bereich `verbindungen` verwaltet **E-Mail-Vorlagen** für Mails an Leads (`list_email_vorlagen`, `create_email_vorlage`, `update_email_vorlage`, `preview_email_vorlage`) und erzeugt Dateien (`create_datei`: Word, Excel, CSV, JSON, Markdown, Text) im Ausgabe-Ordner der Firma. Webhook-Ziele listet `list_webhook_ziele` auf.\n\n### Angebote und Rechnungen\n\nHier gilt die Trennung: Der **Katalog** beschreibt, was du anbietest (Kapitel Geschäftsmodell). **Belege** (Angebot, Rechnung, Mahnung, Abo) leben in der Buchhaltung: Abos und wiederkehrende Rechnungen, Mahnwesen mit Entwürfen, Buchungslinks, KI-Guthaben. Diese Bereiche sind nur zum Lesen oder für Entwürfe in der API, versendet wird per Klick in der App.\n\n### Mein Unternehmen\n\nIn der App zeigt **Mein Unternehmen** die Firma als Bild: Hallen für Vertrieb, Marketing, Kommunikation, Lieferung, Produktion, Service, Finanzen, Personal und die KI-Halle mit den KI-Mitarbeitern. Zahlen kannst du auf Zeiträume wie heute, 7, 14, 90 Tage, Jahr oder gesamt umstellen.\n\n### Das Muster hinter den Bereichen\n\nFast alle Bereiche folgen demselben Muster: ein Objekt (Buch, Kampagne, Seite, Kurs, Agent) kennt den **Kontext** über Verweise (`interview_id`, `storyfilter_id`, `linguistic_id`, `limbictype`, `offer_id`, `keyword_id`). Willst du ein Objekt für dein Unternehmen passend machen, setzt du diese Verweise zuerst. Der Rest folgt aus dem Kontext.\n\n</details>\n\n\n## Fehler\n\n| Status | `code` | Bedeutung |\n|---|---|---|\n| 400 | `invalid_json`, `invalid_messages` … | Der Körper ist kein gültiges JSON oder ein Pflichtfeld fehlt. `param` nennt das Feld. |\n| 401 | `invalid_api_key` | Schlüssel fehlt, ist falsch oder widerrufen. |\n| 402 | `insufficient_quota` | Das KI-Guthaben ist aufgebraucht. Bitte in der App aufladen. |\n| 403 | `recht_fehlt`, `ip_not_allowed`, `stufe_nicht_erlaubt` | Dem Schlüssel fehlt das Recht, die Adresse oder die Größe. |\n| 404 | `unknown_area`, `unknown_function`, `model_not_found` | Bereich, Funktion oder Größe gibt es nicht. |\n| 409 | `bestaetigung_fehlt` | Veröffentlichen und Löschen brauchen `\"bestaetigt\": true`. |\n| 413 | `file_too_large` | Datei größer als 25 MB. |\n| 422 | `function_failed` | Die Funktion ließ sich nicht ausführen. `message` nennt den Grund. |\n| 429 | `rate_limit_exceeded`, `monthly_limit` | Zu viele Anfragen pro Minute oder Monatslimit des Schlüssels erreicht. |\n| 503 | – | Die Datenschleuse ist nicht erreichbar. Es wurde nichts an die KI gesendet. Später erneut versuchen. |\n\n## Limits\n\nJeder Schlüssel darf standardmäßig **60 Anfragen pro Minute** senden. Das Monatslimit in Euro stellst du je Schlüssel ein. Plattform-Funktionen zählen zur Anfragegrenze, kosten in der Beta aber nichts.\n\n## Datenschutz\n\nFür Text, den du an die KI-Rechenleistung schickst, läuft standardmäßig die **Datenschleuse**: Namen, Adressen, Kontodaten und ähnliche Angaben werden durch Platzhalter ersetzt, bevor ein Text die KI erreicht, und in der Antwort wieder eingesetzt. Du kannst sie je Schlüssel abschalten.\n\n## Stand\n\nDie Schnittstelle ist in der **Beta**. Funktionen, die KI-Rechenleistung verbrauchen (Texte erzeugen, analysieren, transkribieren), und solche, die Nachrichten versenden, gehören noch nicht zu den Plattform-Funktionen und folgen mit eigener Abrechnung.","contact":{"name":"KI-Markt","url":"https://ki-markt.com"}},"servers":[{"url":"https://ki-markt.com/v1","description":"KI-Markt"}],"security":[{"bearer":[]}],"tags":[{"name":"KI-Gateway","description":"OpenAI-kompatible Schnittstelle für KI-Rechenleistung: **Chat** (auch mit Streaming und Werkzeugaufrufen), **Embeddings**, **Transkription**, **Bilder** und **Videos**. Du wählst eine Größe (`toni-leicht`, `toni-mittel`, `toni-schwer`) statt eines Modells. Abgerechnet wird nach Nutzung vom KI-Guthaben, bevor ein Text die KI erreicht, läuft die Datenschleuse. Wer schon mit der OpenAI-Bibliothek arbeitet, tauscht nur Adresse und Schlüssel."},{"name":"Bücher","description":"Der Bereich **books** verwaltet die Bücher deines Unternehmens und ihre Kapitel. Ein **Buch** trägt Titel, Untertitel, Autor, Beschreibung, Status und weitere Metadaten wie Sprache, Preis oder ISBN. Jedes **Kapitel** gehört zu genau einem Buch und hat Titel, Text, eine Position (`order`) und einen Status. Alle Funktionen arbeiten nur mit den Büchern deines Unternehmens. Fehler wie eine unbekannte ID kommen als HTTP-200-Antwort, deren `data` die Felder `success: false` und `error` enthält.\n\n**Typischer Ablauf**\n\n1. Mit `create_book` ein Buch anlegen und die zurückgegebene `id` merken; bestehende Bücher findest du mit `get_books` oder `search_books`.\n2. Mit `create_chapter` Kapitel hinzufügen und Texte später mit `update_chapter` überarbeiten; `get_book_chapters` zeigt den aktuellen Stand.\n3. Mit `reorder_chapters` die Reihenfolge der Kapitel in einem Aufruf festlegen.\n4. Mit `update_book` Metadaten wie Status, Preis, Sprache oder Veröffentlichungsdatum pflegen.\n5. Mit `export_book_as_pdf` einen Export anstoßen oder mit `copy_book` eine Kopie als neuen Entwurf anlegen; `delete_book` entfernt ein Buch endgültig.\n\n\n_15 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/books/{funktion}`_"},{"name":"Hörbücher","description":"Der Bereich **audiobooks** verwaltet die Hörbücher deines Unternehmens und ihre Kapitel. Ein **Hörbuch** hat Titel, Untertitel, Beschreibung, Status sowie optional Preis, Währung und Sprache. Jedes **Kapitel** gehört zu genau einem Hörbuch und trägt den Text, der gesprochen werden soll, eine Position (`order`) und einen Status. Alle Funktionen arbeiten nur mit Hörbüchern deines Unternehmens. Fehler wie eine unbekannte ID kommen als HTTP-200-Antwort, deren `data` die Felder `success: false` und `error` enthält.\n\n**Typischer Ablauf**\n\n1. Mit `create_audiobook` ein Hörbuch anlegen und die zurückgegebene `id` merken; bestehende findest du mit `get_audiobooks` oder `search_audiobooks`.\n2. Mit `create_audiobook_chapter` Kapitel samt Text hinzufügen; `get_audiobook_chapters` zeigt Reihenfolge, Status und Textlänge.\n3. Texte mit `update_audiobook_chapter` überarbeiten und Metadaten wie Preis, Sprache oder Status mit `update_audiobook` pflegen.\n4. Mit `create_chapter_audio` den Text eines Kapitels für die Sprachausgabe aufbereiten und den Status prüfen (`ready` oder `error`).\n5. Nicht mehr benötigte Kapitel oder Hörbücher entfernst du mit `delete_audiobook_chapter` bzw. `delete_audiobook`.\n\n\n_11 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/audiobooks/{funktion}`_"},{"name":"Podcasts","description":"Der Bereich **podcasts** verwaltet die Podcasts deines Unternehmens und ihre Episoden. Ein **Podcast** ist die Sendung (Titel, Untertitel, Beschreibung, Sprache, Rhythmus, Kategorien, Website), eine **Episode** gehört immer zu genau einem Podcast und hat Nummer, Fortschrittsstatus, Veröffentlichungsdatum, Dauer sowie optional Zusammenfassung, Shownotes und Transkript. Du kannst Podcasts und Episoden auflisten, suchen, anlegen, ändern, kopieren und löschen, Kennzahlen abrufen, den RSS-Feed-Link ermitteln und Episoden zur Veröffentlichung einplanen. Alle Zugriffe sind auf die Firma beschränkt, zu der dein Schlüssel gehört.\n\n**Typischer Ablauf**\n\n1. Mit `get_podcasts` oder `search_podcasts` den gewünschten Podcast finden und dessen `id` notieren – oder mit `create_podcast` einen neuen anlegen.\n2. Mit `create_episode` eine Episode unter dem Podcast anlegen und mit `update_episode` Beschreibung, Zusammenfassung, Shownotes und Status pflegen.\n3. Mit `get_episodes` (alle Episoden eines Podcasts, nach Nummer) oder `search_episodes` (firmenweite Suche) den Stand prüfen; `get_episode` liefert zusätzlich Zusammenfassung, Shownotes und Transkript.\n4. Mit `schedule_episode` ein Veröffentlichungsdatum setzen; die Episode erhält dabei den Status `scheduled`.\n5. Mit `get_rss_feed` den Feed-Link für Podcast-Verzeichnisse holen und mit `get_episode_stats` die Verteilung der Episoden nach Status und die Laufzeiten ansehen.\n6. Mit `copy_podcast` eine Sendung als Vorlage duplizieren (optional samt Episodentexten) oder mit `delete_podcast` / `delete_episode` Objekte endgültig entfernen.\n\n\n_16 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/podcasts/{funktion}`_"},{"name":"Podcast-Gäste","description":"Der Bereich **podcast_guests** gibt dir einen lesenden Überblick über die Gästeorganisation deiner Podcasts: Welche **Gäste** (Interviewpartner) sind eingeladen, wo stehen sie im Ablauf, wann und wo ist ihr Aufnahmetermin, und an welchen **Aufnahmetagen** sind noch Slots frei? Jeder Gast durchläuft die Phasen `eingeladen`, `vorbereitung`, `aufnahme`, `nachbereitung` oder `abgesagt`. Außerdem kannst du in Anfragen, Adressbuch und Ansprechpartnern deiner Firma nach möglichen Interviewpartnern suchen. Alle Funktionen dieses Bereichs sind rein lesend.\n\n**Typischer Ablauf**\n\n1. Mit `get_podcast_gaeste` ohne `podcast_id` alle Podcasts der Firma samt Gästezahl abrufen und die `id` des gewünschten Podcasts wählen.\n2. Mit `get_podcast_gaeste` und `podcast_id` die Gäste dieses Podcasts ansehen, bei Bedarf mit `status` auf eine Phase eingegrenzt.\n3. Mit `get_podcast_aufnahmetage` die kommenden Aufnahmetage inklusive Standort, Gastgeber und freier Slots prüfen.\n4. Mit `find_podcast_gast_kandidaten` nach einer Person suchen (Name, Firma oder E-Mail) und Kontaktdaten aus den Treffern übernehmen.\n\n\n_3 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/podcast_guests/{funktion}`_"},{"name":"Magazin und Storyfilter","description":"Der Bereich **Magazin und Storyfilter** verwaltet zwei Arten von Objekten deines Unternehmens: **Stories** (Magazin-Beiträge mit Titel, Untertitel, Text, Status, Zielkanal und Hashtags) und **Storyfilter** (Vorgaben für das Erzählen: Zielperson, Wunsch, Probleme, Mentor, Wertversprechen und Nutzen nach dem Heldenreise-Schema A–G). Eine Story kann über `storyfilter_id` einem Storyfilter und über `linguistic_id` einem Sprachprofil zugeordnet sein. Alle Funktionen arbeiten ausschließlich mit den Objekten des Unternehmens, zu dem dein Schlüssel gehört; fremde IDs liefern „nicht gefunden oder kein Zugriff“.\n\n**Typischer Ablauf**\n\n1. Mit `get_storyfilters` die vorhandenen Storyfilter auflisten und die ID des passenden Filters merken – oder mit `create_storyfilter` einen neuen anlegen.\n2. Mit `create_story` eine Story anlegen und dabei `storyfilter_id` und `target_channel` setzen; der Status bleibt leer, wenn du ihn nicht angibst.\n3. Mit `get_story` die gespeicherte Fassung abrufen und mit `update_story` Text, Titel oder Status ändern (z. B. von `draft` auf `review`).\n4. Mit `get_stories` (optional mit `status`-Filter) die Stories seitenweise auflisten, um Bestand und Stand zu prüfen.\n5. Mit `update_storyfilter` einen Filter nachschärfen oder mit `delete_story` eine nicht mehr benötigte Story endgültig entfernen (mit `\"bestaetigt\": true`).\n\n\n_8 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/stories/{funktion}`_"},{"name":"Landingpages","description":"Der Bereich **Landingpages** umfasst drei Objektarten deines Unternehmens: **Templates** (Vorlagen mit Name, Slug, Variante, Farben und Meta-Angaben), **Portale** (veröffentlichbare Landingpage-Portale mit Titel, Slug, Domain, Sprache und Status, optional mit einem Template und einem Keyword verknüpft) und **Keywords** (Suchbegriffe mit Suchvolumen, CPC, Intention und Gruppe). Ein Portal verweist über `template_id` auf ein Template und über `keyword_id` auf ein Keyword. Zusätzlich liefern zwei Funktionen Suchvolumen und Klickpreis (CPC) zu einem Suchbegriff. Alle Funktionen arbeiten nur mit Objekten deines Unternehmens. Hinweis: Templates besitzen im Datenmodell kein Statusfeld und kein Katalog-Kennzeichen; ein angegebener `status` wird bei Templates nicht gespeichert, und `status` ist in Template-Antworten `null`.\n\n**Typischer Ablauf**\n\n1. Mit `search_keywords` nach einem Suchbegriff suchen (bestehende Keywords plus Vorschläge der Suchmaschine) und mit `create_keyword` den passenden Begriff speichern.\n2. Mit `get_keyword_search_volume` und `get_keyword_cpc` Suchvolumen und Klickpreis ermitteln und per `update_keyword` am Keyword hinterlegen.\n3. Mit `create_portal` ein Portal anlegen und `keyword_id` (und, wenn vorhanden, `template_id`) mitgeben.\n4. Mit `update_portal` Titel, Slug, Domain, Sprache oder Meta-Angaben nachziehen und den Status auf `public` setzen, wenn das Portal erscheinen soll.\n5. Mit `get_portals` und `get_portal` den Bestand prüfen; mit `delete_portal` (mit `\"bestaetigt\": true`) nicht mehr benötigte Portale entfernen.\n\n\n_18 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/landingpages/{funktion}`_"},{"name":"Landingcards","description":"Der Bereich **Landingcards** verwaltet digitale Visitenkarten (Bio-Landingpages) für Mitglieder deines Unternehmens. Eine Landingcard besteht aus dem **Profil** des Mitglieds (Kontaktdaten, Bio, Social-Links), einem **Landingcard-Template** (Farben, Texte, Abschnitte) und einem **QR-Code**, der auf die Karte verweist. Die meisten Funktionen wirken auf das Mitglied, dem dein Schlüssel zugeordnet ist: `member_id` wird vom Server aus dem Schlüssel gesetzt und kann nicht übergeben werden. Nur `bulk_create_landingcards` nimmt eine Liste von Mitglieds-IDs deines Unternehmens entgegen. Alternativ adressierst du eine Karte über die ID ihres Templates (`template_id`).\n\n**Typischer Ablauf**\n\n1. Mit `get_landingcards` die vorhandenen Karten deines Unternehmens auflisten; die `templateId` einer Karte brauchst du für die weiteren Aufrufe.\n2. Mit `create_landingcard` die Karte des eigenen Mitglieds anlegen (Profil in `profile`, Gestaltung in `template`) – dabei werden Slug und QR-Code erzeugt.\n3. Mit `get_landingcard` die gespeicherte Karte prüfen und mit `update_landingcard` Profil oder Gestaltung nachziehen.\n4. Mit `activate_landingcard` bzw. `deactivate_landingcard` die Karte ein- oder ausschalten; für viele Mitglieder auf einmal gibt es `bulk_create_landingcards`.\n5. Mit `get_landingcard_analytics` die Kennzahlen abrufen und mit `regenerate_landingcard_qrcode` einen neuen QR-Code erzeugen, falls nötig.\n\n\n_10 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/landingcards/{funktion}`_"},{"name":"QR-Codes","description":"Mit diesem Bereich verwaltest du die **QR-Codes** deines Unternehmens. Ein QR-Code hat einen Namen, eine **Ziel-URL** (`target`), einen Aktivstatus und einen automatisch vergebenen `short_code`. Das erzeugte QR-Bild kodiert nicht die Ziel-URL selbst, sondern eine Kurzlink-Adresse der Plattform mit diesem `short_code`; das Ziel kannst du später ändern, ohne das Bild neu zu drucken. Optional hängt ein QR-Code an einem Dokument (`document_id`). Beim Anlegen liest die Plattform außerdem Titel und Beschreibung der Zielseite aus (`meta_title`, `meta_description`). Alle Funktionen arbeiten nur mit den QR-Codes der Firma, zu der dein Schlüssel gehört.\n\n**Typischer Ablauf**\n\n1. Mit `create_qrcode` einen QR-Code mit Name und Ziel-URL anlegen; die Antwort enthält `id`, `short_code` und das Bild als Base64.\n2. Mit `get_qrcodes` die vorhandenen QR-Codes seitenweise auflisten (ohne Bild) und über `status` nach aktiv/inaktiv filtern.\n3. Mit `get_qrcode` Details eines Codes holen oder mit `get_qr_image` nur das Bild laden, um es zu drucken oder einzubetten.\n4. Mit `update_qrcode` Name, Ziel-URL oder Aktivstatus ändern, zum Beispiel um einen Code abzuschalten (`is_active` = false).\n5. Mit `copy_qrcode` einen Code als Vorlage für einen weiteren duplizieren.\n6. Mit `get_qrcode_stats` einen Überblick über alle Codes holen; `get_qrcode_analytics` und `get_qrcode_scans` liefern derzeit noch keine Scan-Zahlen.\n\n\n_10 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/qrcodes/{funktion}`_"},{"name":"Kundenstimmen","description":"Dieser Bereich verwaltet die **Kundenstimmen** (Testimonials) deines Unternehmens. Ein Testimonial besteht aus Angaben zur Person (`name`, `company`, `city`, `ceo`, `position`), dem Zitat (`quote`), optional einer Geschichte aus `problem`, `solution` und `future`, zwei Bildbeschreibungen, einer Bewertung `rating` (1–5) sowie den Einordnungen `variante` (`schmerzloesung`, `process`, `zitat`) und `state` (`past`, `present`, `future`). Du kannst Stimmen von Hand anlegen und pflegen, aus einer Webseite automatisch importieren, nach Bewertung filtern, auswerten und als JSON exportieren. Alle Funktionen betreffen nur die Testimonials deiner Firma.\n\n**Typischer Ablauf**\n\n1. Mit `scrape_testimonials_from_url` vorhandene Kundenstimmen von einer Webseite importieren oder mit `create_testimonial` eine Stimme von Hand anlegen.\n2. Mit `get_testimonials` die Stimmen seitenweise auflisten, optional ab einer Mindestbewertung.\n3. Mit `get_testimonial` einzelne Einträge prüfen und mit `update_testimonial` korrigieren, zum Beispiel Bewertung, Zitat oder `state`.\n4. Mit `filter_by_rating` die besten Stimmen (nach Bewertung sortiert) für Webseite oder Anzeigen auswählen.\n5. Mit `get_testimonial_statistics` Anzahl, Durchschnittsbewertung und Verteilung nach Variante prüfen; mit `export_testimonials` alles als JSON sichern.\n6. Mit `delete_testimonial` nicht mehr benötigte Einträge endgültig entfernen.\n\n\n_9 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/testimonials/{funktion}`_"},{"name":"Angebote im Katalog","description":"Dieser Bereich verwaltet die **Angebote im Katalog** deines Unternehmens, also die Leistungen und Pakete, die du anbietest (nicht die Angebotsbelege der Buchhaltung). Ein Angebot hat einen Titel, einen Status (`state`, zum Beispiel `present` oder `future`), optional ein Interview, aus dem es stammt (`interview_id`), und einen frei strukturierten Inhalt (`content`) als JSON-Objekt. Für die Zusammenfassung erkennt die Plattform darin die Schlüssel `title`, `description`, `price`, `needs`, `targetAudience` und `processSteps`. Alle Funktionen betreffen nur die Angebote deiner Firma.\n\n**Typischer Ablauf**\n\n1. Mit `list_offer_states` die bekannten Statuswerte nachschlagen.\n2. Mit `create_offer` ein Angebot mit Titel, Status und strukturiertem `content` anlegen.\n3. Mit `get_offers` die Angebote auflisten oder per `search`, `state` und `interview_id` eingrenzen; `get_offer` liefert danach den vollständigen Inhalt.\n4. Mit `update_offer` Titel, Status oder Inhalt ändern; mit `copy_offer` eine Variante als Entwurf anlegen.\n5. Mit `summarize_offer` die wichtigsten Felder (Überschrift, Beschreibung, Preis, Bedarf, Zielgruppe, Prozessschritte) kompakt abrufen.\n6. Mit `delete_offer` nicht mehr benötigte Angebote endgültig entfernen.\n\n\n_8 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/offers/{funktion}`_"},{"name":"Expertise","description":"Dieser Bereich verwaltet die **Expertise-Einträge** deines Unternehmens: strukturiertes Fachwissen, das du zum Beispiel aus Interviews gewinnst. Ein Eintrag hat einen Titel, einen Status (`active`, `draft`, `archived`), optional ein Interview, aus dem er stammt (`interview_id`), und einen frei strukturierten Inhalt (`content`) als JSON-Objekt. Für die Zusammenfassung erkennt die Plattform darin die Schlüssel `title`, `initial_expertise_identification`, `process_analysis`, `experience_and_learning_curve` und `recommendations_and_methods`. Alle Funktionen betreffen nur die Einträge deiner Firma.\n\n**Typischer Ablauf**\n\n1. Mit `list_expertise_states` die bekannten Statuswerte nachschlagen.\n2. Mit `create_expertise` einen Eintrag mit Titel, Status und strukturiertem `content` anlegen.\n3. Mit `get_expertises` die Einträge auflisten oder per `search`, `state` und `interview_id` eingrenzen; `get_expertise` liefert danach den vollständigen Inhalt.\n4. Mit `update_expertise` Titel, Status oder Inhalt pflegen; mit `copy_expertise` eine Variante als Entwurf anlegen.\n5. Mit `summarize_expertise` die Kernabschnitte (Ausgangslage, Prozessanalyse, Erfahrung, Empfehlungen) kompakt abrufen.\n6. Mit `delete_expertise` nicht mehr benötigte Einträge endgültig entfernen.\n\n\n_8 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/expertise/{funktion}`_"},{"name":"Glossar","description":"Dieser Bereich verwaltet das **Glossar** deines Unternehmens: Fachbegriffe mit Definition, zweisprachig in Deutsch und Englisch. Ein Eintrag besteht aus Begriff und Definition (`term_de`, `definition_de`, optional `term_en`, `definition_en`), Beispielen, Synonymen und Einordnungen wie Kategorie (`category`), Status (`status`) und Priorität (`priority`). Neben den Einträgen deiner Firma kann es **globale** Begriffe ohne Firmenzuordnung geben; sie werden nur mit `include_global` in der Liste mitgeliefert. Das Glossar hilft, Begriffe in Texten, Angeboten und Antworten einheitlich zu verwenden.\n\n**Typischer Ablauf**\n\n1. Mit `get_glossary_entries` vorhandene Begriffe durchsuchen (`search`, `category`, `status`) und prüfen, ob ein Begriff schon existiert.\n2. Mit `create_glossary_entry` einen neuen Begriff mit deutscher Definition und optional englischer Übersetzung anlegen.\n3. Mit `get_glossary_entry` einen Eintrag samt Beispielen und Synonymen abrufen.\n4. Mit `update_glossary_entry` Definition, Status, Kategorie oder Synonyme nachpflegen.\n5. Mit `summarize_glossary_entry` eine kompakte Kurzfassung (Kurzdefinition, Synonyme) abrufen.\n6. Mit `delete_glossary_entry` überholte Einträge endgültig entfernen.\n\n\n_6 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/glossary/{funktion}`_"},{"name":"Handbuch","description":"Das Betriebshandbuch besteht aus **Handbuch-Seiten** (SOPs, Richtlinien, Workflows, Schulungsunterlagen). Jede Seite hat Titel, Slug, Kategorie, Seitentyp, Status, einen Hauptinhalt und optional strukturierte Teile wie Schritte, Checkliste und Kennzahlen. Seiten gehören zu deinem Unternehmen; daneben gibt es **systemweite Seiten** (`is_internal`), die für alle Unternehmen sichtbar sind. Die Seiten lassen sich zusätzlich in einen Suchindex für die semantische Suche aufnehmen. Alle Funktionen liefern die Hülle `success`, `data`, `message`, `timestamp`, `meta`; im Fehlerfall `success: false` mit `error`.\n\n**Typischer Ablauf**\n\n1. Mit `get_handbook_pages` oder `search_handbook_pages` einen Überblick holen bzw. nach einem Stichwort suchen.\n2. Mit `get_handbook_page` eine Seite samt `content`, `technical_info` und `common_issues` vollständig laden.\n3. Mit `create_handbook_page` eine neue Seite anlegen (Standard: Status `draft`, Typ `sop`, Kategorie `02-sops`) und bei Bedarf mit `auto_index` direkt indexieren.\n4. Mit `update_handbook_page` einzelne Felder ändern; mit `reindex: true` oder `reindex_handbook_page` den Suchindex nachziehen.\n5. Mit `semantic_search_handbook` natürlichsprachig suchen, mit `summarize_handbook_page` eine Zusammenfassung samt Verbesserungsideen erzeugen.\n6. Mit `copy_handbook_page` Vorlagen duplizieren, mit `delete_handbook_page` nicht mehr benötigte Seiten entfernen.\n\n\n_11 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/handbook/{funktion}`_"},{"name":"Sprachprofile","description":"Ein **Sprachprofil** (Linguistic-Profil) beschreibt, wie eine Person spricht und kommuniziert: Wortschatz, Satzbau, Kommunikationsstil und limbischer Typ. Es entsteht aus der Auswertung eines Interviews und gehört zu deinem Unternehmen. Jedes Profil hat einen Status, eine Sprache (`lang`), einen Verweis auf das Quell-Interview (`interview_id`) und die Person (`member_id`) sowie zwei Inhaltsfelder: `analysis` (Rohauswertung) und `profile_data` (aufbereitete Profildaten). Die Listenansicht enthält nur die Kopfdaten, die Einzelansicht zusätzlich die Inhalte. Alle Antworten haben die Hülle `success`, `data`, `message`, `timestamp`, `meta`.\n\n**Typischer Ablauf**\n\n1. Mit `get_speech_profiles` die Profile deines Unternehmens auflisten (neueste zuerst).\n2. Mit `get_speech_profile` ein Profil samt `analysis` und `profile_data` laden.\n3. Mit `summarize_speech_profile` eine Kurzfassung (Titel, dominanter Typ, Kommunikationsstil, Bedürfnisse, Wortschatzniveau) abrufen.\n4. Mit `update_speech_profile` Name, Status, Sprache oder Inhalte korrigieren.\n5. Mit `delete_speech_profile` nicht mehr benötigte Profile entfernen.\n\n\n_5 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/speechanalysis/{funktion}`_"},{"name":"Interviews","description":"Der Bereich verwaltet **Interviews** und deren **Vorlagen (Templates)**. Eine Vorlage enthält Einleitung, Ausleitung, einen Auftrags-Prompt und eine Liste von Fragen (je Frage `question`, `wanttoreach`, `prompt`). Aus einer Vorlage entsteht ein **Interview** mit Gesprächspartner, Sprache, Status, später Transkript und Insights. Neben deinen eigenen Vorlagen gibt es einen **Katalog** globaler Vorlagen, den du lesen und mit `duplicate_template` als Kopie übernehmen kannst. Die Qualitätsauswertung eines Interviews (Bewertung, beantwortete und fehlende Fragen) liest du mit `get_interview_insights`. Alle Antworten haben die Hülle `success`, `data`, `message`, `timestamp`, `meta`.\n\n**Typischer Ablauf**\n\n1. Mit `get_templates` den Katalog und die eigenen Vorlagen ansehen; mit `duplicate_template` oder `create_template` eine eigene Vorlage bereitstellen.\n2. Mit `create_interview` ein Interview anlegen und optional über `template_id` die Fragen der Vorlage übernehmen.\n3. Mit `update_interview` Status, Gesprächspartner oder das Transkript pflegen, sobald das Gespräch stattgefunden hat.\n4. Mit `get_interview` den Stand abrufen, mit `get_interview_insights` die Qualitätsauswertung und Insights lesen.\n5. Mit `export_template_as_json` und `import_template_from_json` Vorlagen sichern oder zwischen Unternehmen übertragen.\n6. Mit `delete_interview` bzw. `delete_template` Überholtes entfernen.\n\n\n_14 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/interviews/{funktion}`_"},{"name":"Akademie","description":"Der Bereich **Academy** verwaltet die Lerninhalte deiner Firma in drei Ebenen: **Kurse** enthalten **Module**, Module enthalten **Lektionen**. Die Verknüpfung läuft über IDs (ein Modul trägt `course_id`, eine Lektion `module_id`). Alle Kurse gehören zur Firma deines Schlüssels; Module und Lektionen sind über ihren Kurs geschützt und für andere Firmen nicht erreichbar. Du kannst Kurse, Module und Lektionen anlegen, ändern, löschen und auflisten sowie eine Kurs-Zusammenfassung abrufen. Eine Einzelabfrage für Lektionen gibt es nicht.\n\n**Typischer Ablauf**\n\n1. Kurs anlegen mit `create_course` (mindestens `title`; Texte wie Beschreibung und Lernziele im Objekt `content`) und die zurückgegebene `id` merken.\n2. Module anlegen mit `create_module` (`course_id`, `title`, fortlaufende `order`) und die Struktur mit `get_course_modules` prüfen.\n3. Lektionen anlegen mit `create_lesson` (`module_id`, `title`, `order`, optional `description` und `learnings`); mit `get_module_lessons` oder `get_module` die Reihenfolge kontrollieren.\n4. Inhalte nachschärfen mit `update_course`, `update_module` und `update_lesson`; Kursdaten wie Status, Preis oder Dauer setzt du über `data`, Texte über `data.content`.\n5. Überblick holen mit `get_courses` (Liste, Filter `status` und `search`) bzw. `summarize_course` (Kenndaten eines Kurses).\n6. Aufräumen von unten nach oben: erst `delete_lesson`, dann `delete_module`, zuletzt `delete_course` (jeweils mit `bestaetigt: true`), weil Unterobjekte nicht mitgelöscht werden.\n\n\n_16 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/academy/{funktion}`_"},{"name":"Werbekampagnen","description":"Der Bereich **Kampagnen** verwaltet die Werbekampagnen deiner Firma. Eine Kampagne hat einen Provider (z. B. `google`), einen Typ (z. B. `search`), einen Aktivstatus, optional ein Budget und Verknüpfungen zu Keyword, Landingpage-Vorlage und Dokument. Suchkampagnen aus `create_google_search_campaign` bestehen zusätzlich aus **Anzeigengruppen** und **Anzeigen** mit den KI-Texten; für diese Unterebenen gibt es in diesem Bereich keine eigenen Funktionen, sie werden mit der Kampagne erzeugt bzw. neu getextet. Alle Funktionen arbeiten nur mit Kampagnen deiner Firma.\n\n**Typischer Ablauf**\n\n1. Bestand prüfen mit `get_campaigns` (Filter `status`, `provider`) bzw. `get_campaign_stats` für die Gesamtzahlen.\n2. Neue Suchkampagne mit KI-Texten anlegen: `create_google_search_campaign` mit Vorlage, Keyword und Dokument; alternativ eine leere Kampagne mit `create_campaign` (auf Wunsch mit `is_active: false`).\n3. Details kontrollieren mit `get_campaign` und bei Bedarf Name, Budget oder Verknüpfungen mit `update_campaign` anpassen; Anzeigentexte erneuerst du mit `update_google_search_campaign`.\n4. Variante testen: `copy_campaign` kopiert die Kampagnendaten (ohne Anzeigengruppen und Anzeigen) und kann die Kopie inaktiv anlegen.\n5. Schalten mit `activate_campaign` (verlangt `bestaetigt: true`) bzw. anhalten mit `pause_campaign`.\n6. Entfernen mit `delete_campaign` (verlangt `bestaetigt: true`); zum bloßen Stoppen reicht `pause_campaign`.\n\n\n_12 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/campaigns/{funktion}`_"},{"name":"CRM","description":"Der Bereich **CRM** gibt dir Zugriff auf die Leads, Termine und Firmen deines Unternehmens. Ein **Lead** ist ein Interessent oder Kontakt mit Namen, Kontaktdaten, Firma, Status und Pipeline. Einem Lead können **Termine** (Erstgespräche, Interviews, Nachfass-Termine) zugeordnet sein. **Pipelines** und **Workflows** beschreiben deinen Vertriebsprozess; **E-Mail-Vorlagen** sind die dafür hinterlegten Textbausteine. Alle Funktionen arbeiten ausschließlich mit den Daten deines Unternehmens, der Mandant kommt aus dem API-Schlüssel. Fehler der Funktionen kommen mit HTTP 200 und `data.success: false` samt Text in `data.error` zurück.\n\n**Typischer Ablauf**\n\n1. Mit `list_pipelines` die Pipelines und ihre IDs ermitteln, bei Bedarf mit `list_workflows` und `list_email_templates` den Prozess und die Vorlagen kennenlernen.\n2. Einen Kontakt mit `create_lead` anlegen oder mit `get_leads` (Filter `search`, `status`, `pipeline_id`) einen vorhandenen finden und mit `get_lead` bzw. `summarize_lead` die Details laden.\n3. Mit `check_availability` freie Zeiten an einem Tag prüfen und dann mit `schedule_appointment` einen Termin zum Lead anlegen.\n4. Den Fortschritt pflegen: `change_lead_status` für Status und Pipeline, `update_lead` für weitere Felder, `update_appointment` oder `cancel_appointment` für Terminänderungen.\n5. `trigger_website_crawl` startet die Analyse einer Firmen-Website; Firma, Lead und Termin legst du einzeln mit den Lead- und Termin-Funktionen an.\n6. Nicht mehr benötigte Leads entfernst du mit `delete_lead` (nur mit `\"bestaetigt\": true`).\n\n\n_16 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/crm/{funktion}`_"},{"name":"Wissensgraph","description":"Der Bereich **Wissensgraph** bildet die Daten deines Unternehmens als Netz ab: **Datensätze** (Leads, Termine, Bücher, Angebote, Mitglieder, Kurse …) liegen in **Sammlungen** und sind über **Kanten** miteinander verbunden. Jeder Datensatz ist über `sammlung/schlüssel` eindeutig adressierbar. Du kannst Bestände zählen, gezielt filtern, Nachbarn und Verbindungen erkunden und das Unternehmenswissen (Handbuch, Angebote, Interviews, Dateien) durchsuchen. Alle Funktionen lesen nur und liefern ausschließlich Daten deines Unternehmens. Fehler kommen mit HTTP 200 und `data.success: false` samt Text in `data.error`.\n\n**Typischer Ablauf**\n\n1. Mit `get_company_summary` prüfen, welche Sammlungen Daten haben und wie viele.\n2. Mit `search_graph` Datensätze einer Sammlung filtern (z. B. `leads` mit Status `won`) und dabei `_key` und `_className` der Treffer erhalten.\n3. Mit `get_entity_context` die Nachbarn eines Treffers anzeigen oder mit `find_connections` die kürzeste Verbindung zu einem zweiten Datensatz ermitteln.\n4. Für Fragen zum Unternehmenswissen `wissen_suche` verwenden und von einem Treffer mit `wissen_graph` zu verknüpften Inhalten springen.\n5. Mit `get_activity_timeline` die zuletzt geänderten Datensätze sehen; `get_company_knowledge_graph` liefert die Gesamtzahl der erreichbaren Knoten nach Typ.\n\n\n_8 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/arango/{funktion}`_"},{"name":"Geschäftsmodell","description":"Das **Geschäftsmodell** beschreibt, wie dein Unternehmen Geld verdient und Angebote kalkuliert. Es besteht aus dem Business-Model-Canvas, der Produkttreppe, einer limbischen Einordnung und dem **Angebotsprofil** (Geschäftsart, Preislogik, Angebotsbausteine, Standard-Laufzeiten, KI-Hinweise, Kalkulationsgrundlage, Story-Sätze). Pro Unternehmen gilt das aktive Modell, gibt es keines, das neueste. Ein Modell hat einen Status (`draft`, `under_review`, `active`, `archived`). Der Bereich liest das Modell und ändert Angebotsprofil und Canvas gezielt; archivierte Modelle sind nicht änderbar. Alle Antworten haben die Hülle `success`, `data`, `message`, `timestamp`, `meta`.\n\n**Typischer Ablauf**\n\n1. Mit `get_business_model` das aktuelle Modell lesen und prüfen, welche Felder gefüllt sind.\n2. Mit `update_business_model` Regeln, Preise, Kosten oder Hinweise ergänzen; `ai_hints` wird angehängt, `offer_components` und Positionen der Kalkulation werden per Name zusammengeführt.\n3. Mit der Rückgabe von `update_business_model` (`changed`, `offer_profile`) kontrollieren, was sich geändert hat.\n4. Bei Bedarf erneut `get_business_model` aufrufen, um den Gesamtstand zu sehen.\n\n\n_2 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/business_model/{funktion}`_"},{"name":"Produkte","description":"Der Bereich durchsucht den **Produktkatalog** deines Unternehmens. Produkte stehen im Wissensgraph mit Titel, Beschreibung und Datenblatt-Daten; zusätzlich gibt es normalisierte **Zahlenattribute** (`attrs`) wie Akkulaufzeit, Gewicht, Flächenleistung oder Preis in festen Einheiten. Die Suche kombiniert eine semantische Textsuche (`query`) mit harten Zahlenfiltern (`filters`). Der Bereich liest nur; Produkte ändern oder anlegen kannst du hier nicht. Die Antwort enthält Treffer mit Stammdaten, `attrs` und einer gekürzten Fassung der technischen Daten.\n\n**Typischer Ablauf**\n\n1. Eine Freitextsuche mit `search_products` und `query` starten, z. B. nach einer Produktart oder Eigenschaft.\n2. Schwellen wie „mindestens 3 Stunden Laufzeit“ als `filters` angeben (z. B. `akkulaufzeit_min` mit `gte` und `180`), nicht aus dem Text schätzen.\n3. `total_matched`, `count` und `truncated` prüfen, um zu sehen, ob es mehr Treffer gibt als geliefert; bei Bedarf `limit` erhöhen (maximal 20) oder die Filter verschärfen.\n4. Produkte ohne Wert im gefilterten Attribut gelten als „unbekannt“ und werden von Filtern ausgeschlossen.\n\n\n_1 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/products/{funktion}`_"},{"name":"Content-Ideen","description":"Der Bereich **sparring** verwaltet **Content-Ideen**, die aus Sparring-Gesprächen entstehen oder manuell angelegt werden. Eine Content-Idee hat einen Titel, einen Ziel-Typ (`podcast_episode`, `book_chapter`, `blog_post`, `social_media`, `landingpage`), ein Haupt-Keyword, Bewertungen wie Dringlichkeit, Impact und Composite-Score sowie einen Status (`active`, `in_progress`, `archived`) und einen Produktionsstand. Du kannst Ideen auflisten, nach Score priorisieren, neu anlegen, genehmigen und archivieren. Alle Ideen sind auf die Firma deines Schlüssels beschränkt.\n\n**Typischer Ablauf**\n\n1. Mit `create_content_idea` eine neue Idee festhalten – oder mit `get_content_ideas` bzw. `get_idea_backlog` bestehende Ideen sichten.\n2. Mit `get_idea_backlog` die besten Ideen nach Composite-Score priorisieren; doppelte Ideen sind dort ausgeblendet.\n3. Mit `approve_idea` eine ausgewählte Idee genehmigen; sie wechselt auf den Status `in_progress`.\n4. Mit `archive_idea` Ideen ablegen, die nicht mehr relevant sind, optional mit Begründung.\n\n\n_5 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/sparring/{funktion}`_"},{"name":"Agenten und Anwendungen","description":"Der Baukasten ist die Werkstatt für **Anwendungen** (kleine Bildschirmabläufe aus festen Bausteinen) und **Agenten** (Abläufe, die Schritt für Schritt ohne Menschen laufen). **Anwendung:** Der *Canvas* ist der **Entwurf**; jede Änderung zählt als Revision (`revision`) und lässt sich mit `canvas_rueckgaengig` zurücknehmen. Eine **Version** entsteht erst beim Veröffentlichen und ist danach unveränderlich. **Agent:** Auch er hat einen Entwurf (Briefing, Schritte, Werkzeuge, Auslöser, Budget) und wird zu einer unveränderlichen Version. Ein **Schritt** ist eine Station des Ablaufs mit eindeutigem `key` und einem Typ: `werkzeug` und `entscheidung` laufen fest, `ki`, `datei` und `browser` sind geführt, `frei` lässt die KI selbst Werkzeuge wählen, `frage` holt einen Menschen. Ein **Lauf** ist ein einzelner Durchgang durch die Schritte; er hat einen Status (`wartet_auf_start`, `laeuft`, `wartet`, `angehalten`, `fertig`, `fehler`, `abgebrochen`). Ein **Auslöser** legt fest, wann ein Lauf entsteht: von Hand, nach Zeitplan oder durch ein Ereignis der Plattform (etwa `lead.neu`). **Leitplanken** sind kurze Regeln („Nie …“, „Immer …“), die in jeden KI-Schritt eingehen; dazu kommen feste Schranken: Ein Agent darf nur Werkzeuge nutzen, die in seiner Liste stehen, schreibende Werkzeuge lösen eine Rückfrage aus, und ein **Budget** begrenzt Schritte, KI-Aufrufe und Minuten. **Veröffentlichen, Freigeben und Pausieren gibt es in dieser API nicht** – das macht ein Mensch in der Oberfläche (`#/anwendungen`, `#/agenten`). Echte Läufe starten nur veröffentlichte, aktive Agenten; zum Testen gibt es die Simulation des Entwurfs. Fehler wie eine unbekannte ID oder ein ungültiger Baustein kommen als HTTP-200-Antwort, deren `data` `success: false` und einen Satz in `error` enthält.\n\n**Typischer Ablauf**\n\n1. Anwendung bauen: `canvas_oeffnen` legt den Entwurf an, `canvas_setzen` füllt ihn mit Bausteinen, danach ändern `canvas_baustein_einfuegen`, `canvas_baustein_aendern`, `canvas_baustein_entfernen` und `canvas_baustein_verschieben` gezielt einzelne Bausteine; mit `canvas_lesen` prüfst du den Stand. Veröffentlichen muss ein Mensch in der Oberfläche.\n2. Agent anlegen: `agent_entwurf` erzeugt den Entwurf, `agent_briefing` setzt Ziel, Wissen, Leitplanken und KI-Stufe. Mit `baukasten_werkzeuge` suchst du die Werkzeug-Namen, die ein Agent oder Canvas verwenden darf.\n3. Ablauf festlegen: `agent_setzen` schreibt Schritte, Werkzeugliste, Auslöser und Budget in einem Zug; danach feilst du mit `agent_schritt_einfuegen`, `agent_schritt_aendern` und `agent_schritt_entfernen` an einzelnen Schritten und kontrollierst mit `agent_lesen`.\n4. Testen ohne Wirkung: `agent_starten` mit `simulation: true` lässt den gespeicherten Entwurf probelaufen (Lesendes echt, Schreibendes wird nur angezeigt); den Fortschritt liefert `agent_lauf_status`.\n5. Echter Betrieb: Nach dem Veröffentlichen durch einen Menschen startet `agent_starten` ohne Simulation einen Lauf. Fragt der Agent nach, zeigt `agent_rueckfragen` die offene Frage; beantwortet wird sie von einem Menschen in der Oberfläche unter „Agenten“. Danach läuft der Lauf weiter (`agent_fortsetzen`, `agent_lauf_status`).\n6. Eingreifen: `agent_anhalten` stoppt einen Lauf nach dem laufenden Schritt, `agent_fortsetzen` setzt einen angehaltenen oder gescheiterten Lauf fort; `agenten_liste` zeigt Status, letzten Lauf und nächsten Termin aller Agenten.\n\n\n_25 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/baukasten/{funktion}`_"},{"name":"Agent-Teams","description":"Agent-Teams fassen mehrere Chat-Agenten und Avatare zu einer Gruppe mit gemeinsamem **Ziel** und festen **Rollen** zusammen. Ein **Team** hat einen Typ (`optimization`, `implementation`, `process`, `strategy`, `custom`), einen Status und eine Mitgliederliste. Teams werden zusätzlich als Knoten im Wissensgraphen geführt, dessen Umgebung die Team-Oberfläche zeigt. Eine **Sitzung** (die Team-Oberfläche) ist ein Gesprächsraum des Teams; die Unterhaltung selbst führst du im Team-Chat der Oberfläche. Ergebnisse stehen jeweils im Feld `data` der Antwort; dort enthält jede Funktion zusätzlich `success`, `message`, `timestamp` und `meta`.\n\n**Typischer Ablauf**\n\n1. Mit `list_teams` die vorhandenen Teams des Unternehmens auflisten.\n2. Mit `create_team` ein neues Team mit Name, Typ, Ziel und Mitgliedern anlegen.\n3. Details, Wissens-Kontext und Team-Chat öffnest du in der Oberfläche unter „Agenten“ (diese Funktionen sind in der öffentlichen API noch nicht freigegeben).\n\n\n_2 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/agent_teams/{funktion}`_"},{"name":"Reflexionen und Benachrichtigungen","description":"Der Bereich hat zwei Teile. **Reflexionen** sind festgehaltene Erkenntnisse aus Gesprächen und Notizen: mit Titel, Beschreibung, Emotion, Dringlichkeit, Status, Thema, vermuteter Ursache und empfohlener Handlung. Sie lassen sich anlegen, ändern, besprechen, löschen, statistisch auswerten und mit einer Tiefenanalyse anreichern. **Benachrichtigungen** sind die Meldungen der Glocke einer Person: auflisten, zählen, als gelesen markieren, anlegen und löschen. Benachrichtigungen gehören immer zu der Person, zu der der Schlüssel gehört.\n\n**Typischer Ablauf**\n\n1. Lege mit `create_reflection` eine Erkenntnis an und lies sie mit `get_reflection` oder `get_reflections` wieder aus.\n2. Ändere Status, Thema oder Handlungsempfehlung mit `update_reflection`.\n3. `get_reflection_statistics` fasst alle Reflexionen des Unternehmens als Zahlen zusammen.\n4. Frage mit `get_notification_count` ab, ob etwas Neues in der Glocke liegt, und liste es mit `get_notifications` auf.\n5. Hake Meldungen mit `mark_notification_read` ab oder entferne sie mit `delete_notification`; eigene Hinweise legst du mit `create_notification` an.\n\n\n_11 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/reflections/{funktion}`_"},{"name":"E-Mail-Vorlagen und Webhooks","description":"Dieser Bereich verbindet deine Abläufe mit E-Mail, Webhooks und Dateien. Du verwaltest E-Mail-Vorlagen für Mails an Leads (Typ Allgemein, Rückruf oder Follow-up), lässt dir eine Vorlage für einen Lead als Vorschau füllen, listest die freigegebenen Webhook-Ziele und erzeugst Dateien im Ausgabe-Ordner deiner Firma. Eigene Werte setzt du in Vorlagen als `{wert.name}`. Das Senden von Mails und Webhooks gehört nicht zu diesem Bereich der API.\n\n**Typischer Ablauf**\n\n1. Mit `list_email_vorlagen` die vorhandenen Vorlagen und die erwarteten `{wert.…}`-Platzhalter ansehen.\n2. Mit `create_email_vorlage` eine neue Vorlage anlegen oder mit `update_email_vorlage` eine eigene ändern.\n3. Mit `preview_email_vorlage` prüfen, wie die Mail für einen Lead aussieht; es wird nichts gesendet.\n4. Mit `list_webhook_ziele` die freigegebenen Webhook-Ziele ansehen. Neue Ziele legt ein Mensch im Dashboard unter Agenten → Verbindungen an.\n5. Mit `create_datei` Auswertungen als docx, xlsx, csv, json, md oder txt im Ordner „Ausgabe“ ablegen.\n\n\n_6 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/verbindungen/{funktion}`_"},{"name":"Kundensupport","description":"Der Kundensupport führt Kundennachrichten, Kommentare unter Postings, eure Antworten und die Postings selbst aus E-Mail und Social-Media-Kanälen zusammen. Dieser Bereich durchsucht den Bestand deiner Firma nur lesend und nach Bedeutung statt nach exakten Wörtern. `kundensupport_suche` findet ähnliche Themen mit Kanal, Datum, Text und eurer Antwort, `kundensupport_muster` gruppiert wiederkehrende Fragen der letzten Tage. Alle Treffer stammen ausschließlich aus dem Bestand deiner eigenen Firma.\n\n**Typischer Ablauf**\n\n1. Mit `kundensupport_muster` zu einem Thema prüfen, ob dieselbe Frage mehrfach vorkommt und unter welchen Postings.\n2. Mit `kundensupport_suche` einzelne Fälle zu einem Thema finden, bei Bedarf mit `art` oder `kanal` eingegrenzt.\n3. Die Gesprächsnummer aus einem Treffer nutzen, um das Gespräch im Dashboard unter Kundensupport zu öffnen.\n4. Antworten und Postings mit den Treffern vergleichen, um Lücken in den Antworttexten zu erkennen.\n\n\n_2 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/kundensupport/{funktion}`_"},{"name":"Rechnungen lesen","description":"Dieser Bereich liest die Rechnungen deiner Firma aus der Buchhaltung, ohne etwas zu verändern. `offene_rechnungen` zeigt alle Rechnungen mit offenem Restbetrag, die am längsten fälligen zuerst. `rechnung_lesen` liefert eine einzelne Rechnung mit Kunde, Positionen, Status und offenem Betrag. Gemeint sind Rechnungen und Anzahlungsrechnungen; Angebote, Gutschriften und Mahnungen gehören nicht dazu. Beträge kommen als fertig formatierter Text in Euro, zum Beispiel `1.190,00 €`.\n\n**Typischer Ablauf**\n\n1. Mit `offene_rechnungen` den Überblick holen: Gesamtsumme der offenen Beträge und die am längsten fälligen Rechnungen.\n2. Mit `rechnung_lesen` und der Rechnungsnummer aus der Liste Positionen und Status einer Rechnung nachsehen.\n3. Zahlungserinnerungen, Mahnungen und Zahlungseingänge bearbeitest du im Dashboard unter Buchhaltung; dieser Bereich ändert nichts.\n\n\n_2 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/buchhaltung/{funktion}`_"},{"name":"Telefon-Agenten","description":"Toni Call lässt Agenten deiner Firma telefonieren, eingehend und ausgehend, über ein eigenes Telefonkonto oder eine Nebenstelle der Firma. Ein Telefon-Agent ist ein Chat-Agent, dem der Kanal „Telefon“ oder eine Rufnummer zugeordnet ist. Dieser Bereich der API liefert nur die Übersicht: `list_telefon_agenten` nennt die aktiven Agenten mit ID, Name und Nummern. Anrufe starten und Gespräche einsehen gehört nicht zu diesem Bereich.\n\n**Typischer Ablauf**\n\n1. Im Dashboard einen Agenten anlegen und ihm den Kanal „Telefon“ oder eine Rufnummer zuordnen.\n2. Mit `list_telefon_agenten` die aktiven Telefon-Agenten deiner Firma abrufen.\n3. Die `id` des gewünschten Agenten notieren; sie wird überall dort gebraucht, wo ein Anruf einem bestimmten Agenten zugewiesen wird.\n\n\n_1 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/toni_call/{funktion}`_"},{"name":"Angebote und Rechnungsentwürfe","description":"Dieser Bereich legt Entwürfe in der Buchhaltung deiner Firma an. `create_invoice_draft` erzeugt einen Rechnungsentwurf für einen Kunden (Lead) aus den Positionen, die du angibst. `draft_quote` erstellt einen Angebotsentwurf aus einem Briefing und den bisherigen Gesprächen mit dem Kunden. Beides sind reine Entwürfe: nichts wird gestellt, nummeriert oder versendet. Prüfen, Stellen und Versenden macht ein Mensch im Beleg-Editor unter Buchhaltung.\n\n**Typischer Ablauf**\n\n1. Den Kunden als Lead bestimmen und seine `lead_id` aus dem CRM notieren; für Angebote genügt auch der Firmenname.\n2. Rechnung: `create_invoice_draft` mit `lead_id` und den Positionen (Titel, Menge, Nettopreis, Steuersatz) aufrufen.\n3. Angebot: `draft_quote` mit einem Briefing und `lead_id` oder `company_name` aufrufen.\n4. Bei der Fehlermeldung „Mehrere Kunden passen dazu“ den Aufruf mit der `lead_id` aus der Kandidatenliste wiederholen.\n5. Den Entwurf über den `link` im Beleg-Editor prüfen, stellen und senden.\n\n\n_2 Funktionen. Aufruf: `POST https://ki-markt.com/v1/platform/quote/{funktion}`_"}],"paths":{"/models":{"get":{"tags":["KI-Gateway"],"operationId":"models","summary":"Größen und Preise auflisten","description":"Liefert alle Größen, die dein Schlüssel nutzen darf, mit Art (`text`, `embedding`, `transkription`, `bild`, `video`, `sprache`), Beschreibung und Preis. Hat der Schlüssel eine Auswahl erlaubter Größen, siehst du nur diese. Größen mit `nur_nach_aufladung: true` werden mit der ersten Aufladung freigeschaltet. Die Preisliste ohne Schlüssel gibt es unter `GET https://ki-markt.com/v1/preise`.\n\n**Beispiel**\n\n```bash\ncurl https://ki-markt.com/v1/models \\\n  -H \"Authorization: Bearer $KM_KEY\"\n```\n\n```python\nimport os, requests\n\nantwort = requests.get(\n    \"https://ki-markt.com/v1/models\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/models\", {\n  headers: { Authorization: `Bearer ${process.env.KM_KEY}` },\n});\nconsole.log(await antwort.json());\n```","responses":{"200":{"description":"Liste der Größen","content":{"application/json":{"example":{"object":"list","data":[{"id":"toni-leicht","object":"model","created":1762560000,"owned_by":"ki-markt","art":"text","name":"Leicht","beschreibung":"Alltag, kurze Aufgaben","preis":{"einheit":"je 1 Mio. Token","eingabe_eur":0.4,"ausgabe_eur":1.8},"kontext":128000,"nur_nach_aufladung":false},{"id":"toni-transkription","object":"model","created":1762560000,"owned_by":"ki-markt","art":"transkription","name":"Transkription","beschreibung":"Audio zu Text","preis":{"einheit":"je Minute","eur":0.015},"nur_nach_aufladung":false}]}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"402":{"description":"Das KI-Guthaben ist aufgebraucht oder die Größe wird erst nach der ersten Aufladung freigeschaltet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Der Schlüssel darf diese Größe oder von dieser Adresse nicht nutzen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit des Schlüssels erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/chat/completions":{"post":{"tags":["KI-Gateway"],"operationId":"chat","summary":"Antwort erzeugen (Chat)","description":"Erzeugt die nächste Antwort in einem Gespräch, im Format der OpenAI-Chat-Schnittstelle.\n\n**Größe wählen:** `toni-leicht` für Alltag und kurze Aufgaben, `toni-mittel` für Texte, Mails und Werkzeuge, `toni-schwer` für Planung und lange Dokumente. Welche Maschine dahinter arbeitet, entscheiden wir und tauschen sie, wenn es eine bessere gibt.\n\n**Nachrichten:** `messages` ist eine Liste von 1 bis 500 Nachrichten mit `role` (`system`, `user`, `assistant`, `tool`) und `content`. Die Antwort kommt als `choices[0].message`. Mehrere Antworten (`n` größer 1) werden nicht unterstützt.\n\n**Streaming:** Mit `\"stream\": true` kommt die Antwort als Server-Sent-Events: Zeilen `data: {…}` mit Textstücken in `choices[0].delta.content`, am Ende `data: [DONE]`.\n\n**Weitere Felder** werden an die Größe weitergereicht: `temperature`, `top_p`, `max_tokens` (oder `max_completion_tokens`), `stop`, `seed`, `tools`, `tool_choice`, `response_format`. Je nach Größe ignorieren wir einzelne Stichproben-Parameter wie `temperature`.\n\n**Abrechnung:** nach tatsächlichen Token, getrennt für Eingabe und Ausgabe, in `usage` zurückgemeldet. Bei Streaming wird auch abgerechnet, wenn du die Verbindung vorzeitig schließt.\n\n**Datenschleuse:** Ist sie am Schlüssel an (Standard), ersetzen wir Namen, Adressen und Kontodaten durch Platzhalter wie `«PERSON_1»`, bevor der Text die KI erreicht, und setzen sie in der Antwort zurück. Fällt die Schleuse aus, antwortet die Schnittstelle mit `503` und sendet nichts.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/chat/completions \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model\": \"toni-mittel\",\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"Du bist ein freundlicher Assistent eines Handwerksbetriebs. Antworte kurz und auf Deutsch.\"\n        },\n        {\n            \"role\": \"user\",\n            \"content\": \"Schreibe zwei Sätze, mit denen wir einen Kunden an den Termin morgen um 9 Uhr erinnern.\"\n        }\n    ],\n    \"temperature\": 0.3,\n    \"max_tokens\": 200\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/chat/completions\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"model\": \"toni-mittel\",\n        \"messages\": [\n            {\n                \"role\": \"system\",\n                \"content\": \"Du bist ein freundlicher Assistent eines Handwerksbetriebs. Antworte kurz und auf Deutsch.\",\n            },\n            {\n                \"role\": \"user\",\n                \"content\": \"Schreibe zwei Sätze, mit denen wir einen Kunden an den Termin morgen um 9 Uhr erinnern.\",\n            },\n        ],\n        \"temperature\": 0.3,\n        \"max_tokens\": 200,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/chat/completions\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"model\": \"toni-mittel\",\n      \"messages\": [\n          {\n              \"role\": \"system\",\n              \"content\": \"Du bist ein freundlicher Assistent eines Handwerksbetriebs. Antworte kurz und auf Deutsch.\"\n          },\n          {\n              \"role\": \"user\",\n              \"content\": \"Schreibe zwei Sätze, mit denen wir einen Kunden an den Termin morgen um 9 Uhr erinnern.\"\n          }\n      ],\n      \"temperature\": 0.3,\n      \"max_tokens\": 200\n  }),\n});\nconsole.log(await antwort.json());\n```","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["model","messages"],"properties":{"model":{"type":"string","enum":["toni-leicht","toni-mittel","toni-schwer"],"description":"Die Größe. `toni-leicht` ist schnell und günstig, `toni-mittel` der Standard für Texte und Werkzeuge, `toni-schwer` für Planung und lange Dokumente."},"messages":{"type":"array","minItems":1,"maxItems":500,"description":"Das Gespräch bisher, älteste Nachricht zuerst. Eine `system`-Nachricht am Anfang legt Rolle und Ton fest.","items":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","enum":["system","user","assistant","tool"]},"content":{"type":"string"}}}},"stream":{"type":"boolean","default":false,"description":"Antwort Stück für Stück als Server-Sent-Events senden."},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Kreativität: niedrig (0 bis 0,3) für sachliche Antworten, höher für abwechslungsreiche. Bei manchen Größen wirkungslos."},"max_tokens":{"type":"integer","description":"Obergrenze für die Länge der Antwort in Token."},"tools":{"type":"array","description":"Funktionen, die das Modell aufrufen darf (OpenAI-Format). Die Antwort enthält dann `tool_calls`.","items":{"type":"object"}},"response_format":{"type":"object","description":"Zum Beispiel `{\"type\":\"json_object\"}`, damit die Antwort gültiges JSON ist."}}},"example":{"model":"toni-mittel","messages":[{"role":"system","content":"Du bist ein freundlicher Assistent eines Handwerksbetriebs. Antworte kurz und auf Deutsch."},{"role":"user","content":"Schreibe zwei Sätze, mit denen wir einen Kunden an den Termin morgen um 9 Uhr erinnern."}],"temperature":0.3,"max_tokens":200}}}},"responses":{"200":{"description":"Antwort im Format von OpenAI. Mit `stream: true` ein Strom von Server-Sent-Events.","content":{"application/json":{"example":{"id":"chatcmpl-5f3c1a8e2b7d4c09a1e6b3d2","object":"chat.completion","created":1762600000,"model":"toni-mittel","choices":[{"index":0,"message":{"role":"assistant","content":"Guten Tag, wir erinnern Sie gern an unseren Termin morgen um 9 Uhr. Falls etwas dazwischenkommt, melden Sie sich bitte kurz bei uns."},"finish_reason":"stop"}],"usage":{"prompt_tokens":61,"completion_tokens":38,"total_tokens":99}}}}},"400":{"description":"`messages` fehlt oder ist ungültig, `n` größer 1.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"404":{"description":"Die Größe gibt es nicht (`model_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"503":{"description":"Die Datenschleuse ist nicht erreichbar, nichts wurde gesendet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"402":{"description":"Das KI-Guthaben ist aufgebraucht oder die Größe wird erst nach der ersten Aufladung freigeschaltet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Der Schlüssel darf diese Größe oder von dieser Adresse nicht nutzen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit des Schlüssels erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/embeddings":{"post":{"tags":["KI-Gateway"],"operationId":"embeddings","summary":"Texte in Vektoren umwandeln","description":"Wandelt Texte in Zahlenvektoren um, mit denen du Texte nach Bedeutung vergleichen und durchsuchen kannst (semantische Suche, Gruppieren, Empfehlungen).\n\n`input` ist ein Text oder eine Liste mit bis zu **256 Texten**, jeder höchstens 32.000 Zeichen und nicht leer. Die Antwort enthält je Text einen Eintrag in `data` mit `index` (Reihenfolge der Eingabe) und `embedding` (Liste von Zahlen). Abgerechnet wird nach geschätzten Token (etwa 4 Zeichen je Token).\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/embeddings \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model\": \"toni-embedding\",\n    \"input\": [\n        \"Wie lange dauert die Lieferung?\",\n        \"Wann kommt meine Bestellung an?\"\n    ]\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/embeddings\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"model\": \"toni-embedding\",\n        \"input\": [\n            \"Wie lange dauert die Lieferung?\",\n            \"Wann kommt meine Bestellung an?\",\n        ],\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/embeddings\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"model\": \"toni-embedding\",\n      \"input\": [\n          \"Wie lange dauert die Lieferung?\",\n          \"Wann kommt meine Bestellung an?\"\n      ]\n  }),\n});\nconsole.log(await antwort.json());\n```","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["model","input"],"properties":{"model":{"type":"string","enum":["toni-embedding"],"description":"Immer `toni-embedding`."},"input":{"oneOf":[{"type":"string"},{"type":"array","maxItems":256,"items":{"type":"string"}}],"description":"Ein Text oder eine Liste von bis zu 256 Texten."}}},"example":{"model":"toni-embedding","input":["Wie lange dauert die Lieferung?","Wann kommt meine Bestellung an?"]}}}},"responses":{"200":{"description":"Vektoren in der Reihenfolge der Eingabe","content":{"application/json":{"example":{"object":"list","model":"toni-embedding","data":[{"object":"embedding","index":0,"embedding":[0.0123,-0.0456,0.0789,"…"]},{"object":"embedding","index":1,"embedding":[0.0111,-0.0399,0.0812,"…"]}],"usage":{"prompt_tokens":16,"total_tokens":16}}}}},"400":{"description":"`input` fehlt oder ist ungültig.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"502":{"description":"Der Dienst ist gerade nicht erreichbar.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"402":{"description":"Das KI-Guthaben ist aufgebraucht oder die Größe wird erst nach der ersten Aufladung freigeschaltet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Der Schlüssel darf diese Größe oder von dieser Adresse nicht nutzen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit des Schlüssels erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/audio/transcriptions":{"post":{"tags":["KI-Gateway"],"operationId":"transkription","summary":"Sprache in Text umwandeln","description":"Wandelt eine Audiodatei in Text um. Die Datei sendest du als `multipart/form-data` im Feld `file` (höchstens **25 MB**, gängige Formate wie mp3, m4a, wav, webm).\n\n**Antwortformat** über `response_format`: `json` (Standard, nur `text`), `text` (reiner Text) oder `verbose_json` (zusätzlich Sprache, Dauer und Abschnitte mit Zeitangaben). Mit `language` (zum Beispiel `de`) gibst du die gesprochene Sprache vor, das verbessert die Erkennung.\n\n**Abrechnung:** nach der Länge des Audios in Minuten, mindestens eine Sekunde.\n\n**Beispiel**\n\n```bash\ncurl https://ki-markt.com/v1/audio/transcriptions \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -F model=toni-transkription \\\n  -F language=de \\\n  -F file=@besprechung.m4a\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/audio/transcriptions\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    data={\"model\": \"toni-transkription\", \"language\": \"de\"},\n    files={\"file\": open(\"besprechung.m4a\", \"rb\")},\n    timeout=300,\n)\nprint(antwort.json()[\"text\"])\n```\n\n```javascript\nconst form = new FormData();\nform.append(\"model\", \"toni-transkription\");\nform.append(\"language\", \"de\");\nform.append(\"file\", new Blob([await fs.promises.readFile(\"besprechung.m4a\")]), \"besprechung.m4a\");\n\nconst antwort = await fetch(\"https://ki-markt.com/v1/audio/transcriptions\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${process.env.KM_KEY}` },\n  body: form,\n});\nconsole.log((await antwort.json()).text);\n```","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["model","file"],"properties":{"model":{"type":"string","enum":["toni-transkription"],"description":"Immer `toni-transkription`."},"file":{"type":"string","format":"binary","description":"Die Audiodatei, höchstens 25 MB."},"language":{"type":"string","example":"de","description":"Sprachcode der gesprochenen Sprache (ISO 639-1), zum Beispiel `de` oder `en`. Ohne Angabe erkennt der Dienst die Sprache selbst."},"response_format":{"type":"string","enum":["json","text","verbose_json"],"default":"json","description":"`json` liefert `{\"text\": …}`, `text` den reinen Text, `verbose_json` zusätzlich Sprache, Dauer und Abschnitte."}}}}}},"responses":{"200":{"description":"Erkannter Text","content":{"application/json":{"example":{"text":"Wir treffen uns am Dienstag um neun Uhr auf der Baustelle und gehen die Liste noch einmal durch."}}}},"400":{"description":"Datei fehlt (`missing_file`) oder `response_format` ist ungültig.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"413":{"description":"Die Datei ist größer als 25 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"402":{"description":"Das KI-Guthaben ist aufgebraucht oder die Größe wird erst nach der ersten Aufladung freigeschaltet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Der Schlüssel darf diese Größe oder von dieser Adresse nicht nutzen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit des Schlüssels erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/images/generations":{"post":{"tags":["KI-Gateway"],"operationId":"bild","summary":"Bild erzeugen","description":"Erzeugt ein Bild aus einer Beschreibung. Pro Aufruf entsteht **ein** Bild (`n` muss 1 sein). Die Beschreibung (`prompt`) darf bis zu 4.000 Zeichen lang sein; je konkreter (Motiv, Stil, Licht, Bildausschnitt), desto besser das Ergebnis.\n\n`size` gibt das Format als `BreitexHöhe` an, zum Beispiel `1024x1024` (quadratisch), `1792x1024` (quer) oder `1024x1792` (hoch). Wir wählen das nächstliegende Seitenverhältnis aus 1:1, 16:9, 9:16, 4:3, 3:4, 3:2 und 2:3.\n\nDie Antwort enthält das Bild als **Base64-kodiertes WebP** in `data[0].b64_json`. Der Aufruf wartet, bis das Bild fertig ist (bis etwa zwei Minuten). Abgerechnet wird je Bild.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/images/generations \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model\": \"toni-bild\",\n    \"prompt\": \"Eine helle Werkstatt mit einem Handwerker, der ein Tablet hält, Fotostil, weiches Tageslicht\",\n    \"size\": \"1024x1024\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/images/generations\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"model\": \"toni-bild\",\n        \"prompt\": \"Eine helle Werkstatt mit einem Handwerker, der ein Tablet hält, Fotostil, weiches Tageslicht\",\n        \"size\": \"1024x1024\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/images/generations\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"model\": \"toni-bild\",\n      \"prompt\": \"Eine helle Werkstatt mit einem Handwerker, der ein Tablet hält, Fotostil, weiches Tageslicht\",\n      \"size\": \"1024x1024\"\n  }),\n});\nconsole.log(await antwort.json());\n```","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["model","prompt"],"properties":{"model":{"type":"string","enum":["toni-bild"],"description":"Immer `toni-bild`."},"prompt":{"type":"string","maxLength":4000,"description":"Beschreibung des Bildes: Motiv, Stil, Licht, Ausschnitt. Höchstens 4.000 Zeichen."},"size":{"type":"string","example":"1024x1024","description":"Format als `BreitexHöhe`, zum Beispiel `1024x1024`, `1792x1024` oder `1024x1792`. Es wird das nächstliegende Seitenverhältnis verwendet."},"n":{"type":"integer","enum":[1],"default":1,"description":"Anzahl Bilder. Muss 1 sein."}}},"example":{"model":"toni-bild","prompt":"Eine helle Werkstatt mit einem Handwerker, der ein Tablet hält, Fotostil, weiches Tageslicht","size":"1024x1024"}}}},"responses":{"200":{"description":"Das Bild als Base64-kodiertes WebP","content":{"application/json":{"example":{"created":1762600000,"data":[{"b64_json":"UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAUAmJaQAA3AA/vuUAAA="}]}}}},"400":{"description":"`prompt` fehlt oder ist zu lang, `n` ist nicht 1.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"402":{"description":"Das KI-Guthaben ist aufgebraucht oder die Größe wird erst nach der ersten Aufladung freigeschaltet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Der Schlüssel darf diese Größe oder von dieser Adresse nicht nutzen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit des Schlüssels erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/videos":{"post":{"tags":["KI-Gateway"],"operationId":"videoStart","summary":"Video-Auftrag starten","description":"Startet die Erzeugung eines kurzen Videos. Weil das einige Minuten dauert, ist der Ablauf **asynchron**:\n\n1. `POST /videos` legt den Auftrag an und antwortet sofort mit `202` und einer `id` (`vid_…`).\n2. `GET /videos/{id}` liefert den Stand: `queued`/`in_progress`, `completed` oder `failed`.\n3. Ist der Stand `completed`, lädt `GET /videos/{id}/content` die fertige Videodatei.\n\n`seconds` legt die Länge fest (2 bis 5 Sekunden, Standard 5), `aspect_ratio` das Format (`16:9` quer, `9:16` oder `vertical` hochkant). Der Prompt darf bis zu 2.000 Zeichen lang sein. Das Guthaben muss für die volle Länge reichen. **Abgerechnet wird erst, wenn das Video fertig ist.**\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/videos \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model\": \"toni-video\",\n    \"prompt\": \"Eine Drohne fliegt langsam über ein Hallendach bei Sonnenaufgang\",\n    \"seconds\": 5,\n    \"aspect_ratio\": \"16:9\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/videos\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"model\": \"toni-video\",\n        \"prompt\": \"Eine Drohne fliegt langsam über ein Hallendach bei Sonnenaufgang\",\n        \"seconds\": 5,\n        \"aspect_ratio\": \"16:9\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/videos\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"model\": \"toni-video\",\n      \"prompt\": \"Eine Drohne fliegt langsam über ein Hallendach bei Sonnenaufgang\",\n      \"seconds\": 5,\n      \"aspect_ratio\": \"16:9\"\n  }),\n});\nconsole.log(await antwort.json());\n```","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["model","prompt"],"properties":{"model":{"type":"string","enum":["toni-video"],"description":"Immer `toni-video`."},"prompt":{"type":"string","maxLength":2000,"description":"Beschreibung der Szene und der Bewegung. Höchstens 2.000 Zeichen."},"seconds":{"type":"integer","minimum":2,"maximum":5,"default":5,"description":"Länge in Sekunden, 2 bis 5."},"aspect_ratio":{"type":"string","enum":["16:9","9:16","vertical"],"default":"16:9","description":"`16:9` quer, `9:16` oder `vertical` hochkant."}}},"example":{"model":"toni-video","prompt":"Eine Drohne fliegt langsam über ein Hallendach bei Sonnenaufgang","seconds":5,"aspect_ratio":"16:9"}}}},"responses":{"202":{"description":"Auftrag angenommen","content":{"application/json":{"example":{"id":"vid_3f9a1c2d4e5b6a7c8d9e","object":"video","model":"toni-video","status":"queued","seconds":5,"created_at":1762600000}}}},"400":{"description":"`prompt` fehlt oder ist zu lang.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"402":{"description":"Das KI-Guthaben ist aufgebraucht oder die Größe wird erst nach der ersten Aufladung freigeschaltet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Der Schlüssel darf diese Größe oder von dieser Adresse nicht nutzen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit des Schlüssels erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/videos/{id}":{"get":{"tags":["KI-Gateway"],"operationId":"videoStatus","summary":"Stand eines Video-Auftrags abfragen","description":"Liefert den Stand eines Auftrags: `in_progress` (läuft), `completed` (fertig, Datei unter `/videos/{id}/content` abrufbar) oder `failed`. Frage nicht öfter als alle paar Sekunden ab; die Erzeugung dauert in der Regel einige Minuten. Die `id` gehört zu deinem Unternehmen, fremde Aufträge sind nicht abrufbar.\n\n**Beispiel**\n\n```bash\ncurl https://ki-markt.com/v1/videos/vid_3f9a1c2d4e5b6a7c8d9e \\\n  -H \"Authorization: Bearer $KM_KEY\"\n```\n\n```python\nimport os, requests\n\nantwort = requests.get(\n    \"https://ki-markt.com/v1/videos/vid_3f9a1c2d4e5b6a7c8d9e\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/videos/vid_3f9a1c2d4e5b6a7c8d9e\", {\n  headers: { Authorization: `Bearer ${process.env.KM_KEY}` },\n});\nconsole.log(await antwort.json());\n```","parameters":[{"name":"id","in":"path","required":true,"description":"Die `id` aus der Antwort von `POST /videos`.","schema":{"type":"string","pattern":"^vid_[a-f0-9]{20}$","example":"vid_3f9a1c2d4e5b6a7c8d9e"}}],"responses":{"200":{"description":"Stand des Auftrags","content":{"application/json":{"example":{"id":"vid_3f9a1c2d4e5b6a7c8d9e","object":"video","model":"toni-video","status":"completed","seconds":5}}}},"404":{"description":"Den Auftrag gibt es nicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"402":{"description":"Das KI-Guthaben ist aufgebraucht oder die Größe wird erst nach der ersten Aufladung freigeschaltet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Der Schlüssel darf diese Größe oder von dieser Adresse nicht nutzen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit des Schlüssels erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/videos/{id}/content":{"get":{"tags":["KI-Gateway"],"operationId":"videoInhalt","summary":"Fertiges Video herunterladen","description":"Liefert die fertige Videodatei, sobald der Auftrag `completed` ist. Vorher antwortet die Schnittstelle mit einem Fehler.\n\n**Beispiel**\n\n```bash\ncurl https://ki-markt.com/v1/videos/vid_3f9a1c2d4e5b6a7c8d9e/content \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -o video.mp4\n```","parameters":[{"name":"id","in":"path","required":true,"description":"Die `id` des Video-Auftrags.","schema":{"type":"string","pattern":"^vid_[a-f0-9]{20}$"}}],"responses":{"200":{"description":"Die Videodatei","content":{"video/mp4":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Den Auftrag gibt es nicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"402":{"description":"Das KI-Guthaben ist aufgebraucht oder die Größe wird erst nach der ersten Aufladung freigeschaltet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Der Schlüssel darf diese Größe oder von dieser Adresse nicht nutzen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit des Schlüssels erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/get_books":{"post":{"tags":["Bücher"],"operationId":"books__get_books","summary":"Bücher auflisten","description":"Liefert die Bücher deines Unternehmens, neueste zuerst (Sortierung nach Erstellungsdatum absteigend). Ohne Angaben kommen die ersten 10 Bücher zurück. Mit `offset` und `limit` blätterst du seitenweise, mit `status` filterst du auf einen exakten Status. In `data` steht `items` (Liste der Bücher), `count` (Anzahl der Einträge in dieser Antwort, nicht die Gesamtzahl), `limit` und `offset`. Jedes Buch enthält `id`, `title`, `subtitle`, `author`, `status`, `company_id`, `chapters_count`, `created_at` und `updated_at`. Für Beschreibung, Sprache, Preis und ISBN rufst du anschließend `get_book` auf.\n\n**Gut zu wissen**\n\n- `count` ist die Länge der aktuellen Seite. Um alles zu holen, erhöhe `offset` um `limit`, bis weniger Einträge als `limit` zurückkommen.\n- Der Statusfilter vergleicht exakt. Das Datenfeld kennt laut Klassendefinition die Werte `draft` und `publish`; die Funktion selbst erlaubt zusätzlich `published` und `in_progress`.\n- Die Gesamtzahl und die Verteilung nach Status liefert `get_book_statistics`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/get_books \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"limit\": 2,\n    \"offset\": 0,\n    \"status\": \"draft\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/get_books\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"limit\": 2,\n        \"offset\": 0,\n        \"status\": \"draft\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/get_books\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"limit\": 2,\n      \"offset\": 0,\n      \"status\": \"draft\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","description":"Höchstzahl der Bücher in der Antwort (ganze Zahl). Standard ist 10. Eine Obergrenze erzwingt der Code nicht; wähle für große Bestände sinnvolle Seitengrößen, etwa 20 bis 50.","example":2},"offset":{"type":"integer","description":"Anzahl der Bücher, die am Anfang übersprungen werden (ganze Zahl, Standard 0). Zusammen mit `limit` ergibt das die Seiteneinteilung, z. B. `offset` 10 und `limit` 10 für die zweite Seite.","example":0},"status":{"type":"string","enum":["draft","published","in_progress"],"description":"Optionaler Filter auf den Status eines Buchs. Erlaubt laut Schema: `draft`, `published`, `in_progress`. Der Vergleich ist exakt; ohne Angabe (oder bei leerem Wert) werden alle Status geliefert.","example":"draft"}}},"example":{"limit":2,"offset":0,"status":"draft"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"get_books","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":412,"title":"Der ruhige Betrieb","subtitle":"Wie kleine Teams Abläufe entschlacken","author":"Mira Falkenberg","status":"draft","company_id":20481,"chapters_count":3,"created_at":"2026-02-02T09:15:30+01:00","updated_at":"2026-03-17T16:48:02+01:00"},{"id":398,"title":"Kundenbriefe, die gelesen werden","subtitle":null,"author":"Mira Falkenberg","status":"draft","company_id":20481,"chapters_count":0,"created_at":"2026-02-02T09:15:30+01:00","updated_at":"2026-03-17T16:48:02+01:00"}],"count":2,"limit":2,"offset":0},"message":null,"timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/get_book":{"post":{"tags":["Bücher"],"operationId":"books__get_book","summary":"Buch abrufen","description":"Liefert ein einzelnes Buch deines Unternehmens anhand seiner ID. Die Antwort enthält die Felder der Listenansicht (`id`, `title`, `subtitle`, `author`, `status`, `company_id`, `chapters_count`, `created_at`, `updated_at`) und zusätzlich `isbn`, `description`, `lang` und `price`. Die Kapitel selbst sind nicht enthalten; dafür gibt es `get_book_chapters`. Gehört die ID nicht zu einem Buch deines Unternehmens oder existiert sie nicht, kommt `success: false` mit der Meldung „Buch nicht gefunden oder kein Zugriff“ zurück. Die ID erhältst du aus `get_books`, `search_books` oder `create_book`.\n\n**Gut zu wissen**\n\n- Fehler wie eine unbekannte ID kommen als reguläre Antwort mit `data.success: false` und `data.error`, nicht als HTTP-Fehlerstatus.\n- `price` wird so zurückgegeben, wie es gespeichert ist (Textfeld), z. B. `\"24.90\"`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/get_book \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"book_id\": 412\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/get_book\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"book_id\": 412,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/get_book\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"book_id\": 412\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"book_id":{"type":"integer","description":"ID des Buchs als ganze Zahl (Pflicht). Die ID ist die Objekt-ID aus `get_books`, `search_books` oder `create_book`. Bücher anderer Unternehmen werden wie nicht vorhanden behandelt.","example":412}},"required":["book_id"]},"example":{"book_id":412}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"get_book","stufe":"lesen","data":{"success":true,"data":{"id":412,"title":"Der ruhige Betrieb","subtitle":"Wie kleine Teams Abläufe entschlacken","author":"Mira Falkenberg","status":"draft","company_id":20481,"chapters_count":3,"created_at":"2026-02-02T09:15:30+01:00","updated_at":"2026-03-17T16:48:02+01:00","isbn":null,"description":"Ein Praxisbuch über schlanke Prozesse im Handwerk.","lang":"de","price":"24.90"},"message":null,"timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/search_books":{"post":{"tags":["Bücher"],"operationId":"books__search_books","summary":"Bücher suchen","description":"Durchsucht die Bücher deines Unternehmens nach einem Suchbegriff in Titel, Untertitel und Beschreibung (Teilstring-Suche, Groß-/Kleinschreibung wird von der Datenbank-Sortierung bestimmt). Die Treffer sind nach Titel aufsteigend sortiert. Ohne `limit` kommen höchstens 10 Treffer. In `data` stehen `items` (Bücher im Format von `get_books`, also ohne Beschreibung und Preis), `count` (Anzahl in dieser Antwort), `limit` und `offset`. Für Details zu einem Treffer folgt `get_book`. Zum bloßen Durchblättern aller Bücher, neueste zuerst, nimmst du `get_books`.\n\n**Gut zu wissen**\n\n- Der Suchbegriff wird vor der Suche HTML-maskiert (z. B. wird `&` zu `&amp;`). Begriffe mit Sonderzeichen wie `&` oder Anführungszeichen finden deshalb nur, wenn die Zeichen im Text genauso gespeichert sind.\n- Die Platzhalter `%` und `_` im Suchbegriff wirken als SQL-Platzhalter.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/search_books \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\": \"Abläufe\",\n    \"limit\": 5\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/search_books\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"query\": \"Abläufe\",\n        \"limit\": 5,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/search_books\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"query\": \"Abläufe\",\n      \"limit\": 5\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Suchbegriff als Text (Pflicht). Gesucht wird als Teilstring in Titel, Untertitel und Beschreibung. Leere Werte werden abgelehnt.","example":"Abläufe"},"limit":{"type":"integer","description":"Höchstzahl der Treffer (ganze Zahl, Standard 10). Eine Obergrenze erzwingt der Code nicht.","example":5},"offset":{"type":"integer","description":"Anzahl der Treffer, die übersprungen werden (ganze Zahl, Standard 0), für das seitenweise Blättern durch die Trefferliste."}},"required":["query"]},"example":{"query":"Abläufe","limit":5}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"search_books","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":412,"title":"Der ruhige Betrieb","subtitle":"Wie kleine Teams Abläufe entschlacken","author":"Mira Falkenberg","status":"draft","company_id":20481,"chapters_count":3,"created_at":"2026-02-02T09:15:30+01:00","updated_at":"2026-03-17T16:48:02+01:00"}],"count":1,"limit":5,"offset":0},"message":null,"timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/create_book":{"post":{"tags":["Bücher"],"operationId":"books__create_book","summary":"Buch anlegen","description":"Legt ein neues Buch für dein Unternehmen an. Pflicht ist nur der Titel. Das Buch wird als veröffentlichtes Datenobjekt im Ordner für Bücher gespeichert und deinem Unternehmen zugeordnet; der Titel dient dabei zugleich als Objektschlüssel. Ohne Statusangabe bleibt der Status leer. Zurück kommt das neue Buch in der ausführlichen Ansicht (`id`, `title`, `subtitle`, `author`, `status`, `company_id`, `chapters_count`, `created_at`, `updated_at`, `isbn`, `description`, `lang`, `price`) mit der Meldung „Buch erfolgreich erstellt“. Die zurückgegebene `id` brauchst du für `create_chapter`, `update_book` und alle weiteren Aufrufe. Weitere Eigenschaften wie ISBN, Preis oder Sprache setzt du anschließend mit `update_book`.\n\n**Gut zu wissen**\n\n- Schlägt das Anlegen fehl, lautet die Antwort nur allgemein „Fehler beim Erstellen des Buchs“; Details stehen nicht in der Antwort.\n- Der Status wird nicht gegen die Liste geprüft. Das Datenfeld selbst kennt `draft` und `publish`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/create_book \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Der ruhige Betrieb\",\n    \"subtitle\": \"Wie kleine Teams Abläufe entschlacken\",\n    \"author\": \"Mira Falkenberg\",\n    \"status\": \"draft\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/create_book\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"title\": \"Der ruhige Betrieb\",\n        \"subtitle\": \"Wie kleine Teams Abläufe entschlacken\",\n        \"author\": \"Mira Falkenberg\",\n        \"status\": \"draft\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/create_book\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"title\": \"Der ruhige Betrieb\",\n      \"subtitle\": \"Wie kleine Teams Abläufe entschlacken\",\n      \"author\": \"Mira Falkenberg\",\n      \"status\": \"draft\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Titel des Buchs (Pflicht, nicht leer). Er wird als Titel gespeichert und dient als Grundlage des Objektschlüssels.","example":"Der ruhige Betrieb"},"subtitle":{"type":"string","description":"Optionaler Untertitel als Text. Wird nur gesetzt, wenn er im Körper vorkommt.","example":"Wie kleine Teams Abläufe entschlacken"},"author":{"type":"string","description":"Optionaler Name des Autors oder der Autorin als Text. Er erscheint in allen Buchansichten.","example":"Mira Falkenberg"},"description":{"type":"string","description":"Optionale Beschreibung oder Klappentext als Text. Die Beschreibung wird auch von `search_books` durchsucht."},"status":{"type":"string","enum":["draft","published","in_progress"],"description":"Optionaler Status. Laut Schema `draft`, `published` oder `in_progress`. Ohne Angabe bleibt der Status leer; setze am besten `draft`, wenn du mit einem Entwurf beginnst.","example":"draft"}},"required":["title"]},"example":{"title":"Der ruhige Betrieb","subtitle":"Wie kleine Teams Abläufe entschlacken","author":"Mira Falkenberg","status":"draft"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"create_book","stufe":"schreiben","data":{"success":true,"data":{"id":412,"title":"Der ruhige Betrieb","subtitle":"Wie kleine Teams Abläufe entschlacken","author":"Mira Falkenberg","status":"draft","company_id":20481,"chapters_count":0,"created_at":"2026-02-02T09:15:30+01:00","updated_at":"2026-03-17T16:48:02+01:00","isbn":null,"description":null,"lang":null,"price":null},"message":"Buch erfolgreich erstellt","timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/update_book":{"post":{"tags":["Bücher"],"operationId":"books__update_book","summary":"Buchdaten ändern","description":"Ändert Metadaten eines bestehenden Buchs. Im Objekt `data` übergibst du nur die Felder, die sich ändern sollen; alle anderen bleiben unverändert, Felder mit dem Wert `null` werden ignoriert. Zusätzlich zu den im Schema aufgeführten Feldern übernimmt der Dienst weitere Buchfelder wie `publisher`, `descriptionShort`, `descriptionLong`, `pagecount`, `discountPrice`, `availabilityStatus` oder `edition`. `publishDate` und `preorderDate` werden als Datum (ISO 8601) gelesen; ein ungültiges Datum führt zu einem Fehler. Zurück kommt das geänderte Buch in der ausführlichen Ansicht mit der Meldung „Buch aktualisiert“. Kapitel änderst du nicht hier, sondern mit `update_chapter`.\n\n**Gut zu wissen**\n\n- Die Funktion ändert nur das Buch selbst; Kapitel, ISBN-Zuweisung und Exporte bleiben unberührt.\n- Das Datenfeld Status kennt laut Klassendefinition nur `draft` und `publish`. `get_book_statistics` zählt nur `draft`, `published` und `in_progress` einzeln.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/update_book \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"book_id\": 412,\n    \"data\": {\n        \"status\": \"in_progress\",\n        \"price\": 24.9,\n        \"currency\": \"EUR\",\n        \"lang\": \"de\",\n        \"publishDate\": \"2026-09-01\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/update_book\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"book_id\": 412,\n        \"data\": {\n            \"status\": \"in_progress\",\n            \"price\": 24.9,\n            \"currency\": \"EUR\",\n            \"lang\": \"de\",\n            \"publishDate\": \"2026-09-01\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/update_book\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"book_id\": 412,\n      \"data\": {\n          \"status\": \"in_progress\",\n          \"price\": 24.9,\n          \"currency\": \"EUR\",\n          \"lang\": \"de\",\n          \"publishDate\": \"2026-09-01\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"book_id":{"type":"integer","description":"ID des zu ändernden Buchs als ganze Zahl (Pflicht). Das Buch muss zu deinem Unternehmen gehören, sonst kommt „Buch nicht gefunden oder kein Zugriff“.","example":412},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern (Pflicht). Dokumentierte Felder: `title`, `subtitle`, `author`, `description`, `status` (`draft`, `published`, `in_progress`), `isbn`, `price` (Zahl), `currency` (z. B. `EUR`), `lang` (z. B. `de`), `category`, `publishDate` und `preorderDate` (jeweils ISO 8601, z. B. `2026-09-01`). Nur enthaltene Felder werden geändert.","properties":{"title":{"type":"string","description":"Buchtitel"},"subtitle":{"type":"string","description":"Untertitel"},"author":{"type":"string","description":"Autor"},"description":{"type":"string","description":"Beschreibung"},"status":{"type":"string","enum":["draft","published","in_progress"],"description":"Status"},"isbn":{"type":"string","description":"ISBN Nummer"},"price":{"type":"number","description":"Preis"},"currency":{"type":"string","description":"Währung (z.B. EUR, USD)"},"lang":{"type":"string","description":"Sprache (z.B. de, en)"},"category":{"type":"string","description":"Kategorie"},"publishDate":{"type":"string","description":"Veröffentlichungsdatum (ISO 8601)"},"preorderDate":{"type":"string","description":"Vorbestellungsdatum (ISO 8601)"}},"example":{"status":"in_progress","price":24.9,"currency":"EUR","lang":"de","publishDate":"2026-09-01"}}},"required":["book_id","data"]},"example":{"book_id":412,"data":{"status":"in_progress","price":24.9,"currency":"EUR","lang":"de","publishDate":"2026-09-01"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"update_book","stufe":"schreiben","data":{"success":true,"data":{"id":412,"title":"Der ruhige Betrieb","subtitle":"Wie kleine Teams Abläufe entschlacken","author":"Mira Falkenberg","status":"in_progress","company_id":20481,"chapters_count":3,"created_at":"2026-02-02T09:15:30+01:00","updated_at":"2026-03-18T10:42:15+01:00","isbn":null,"description":"Ein Praxisbuch über schlanke Prozesse im Handwerk.","lang":"de","price":"24.90"},"message":"Buch aktualisiert","timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/delete_book":{"post":{"tags":["Bücher"],"operationId":"books__delete_book","summary":"Buch löschen","description":"Löscht ein Buch deines Unternehmens unwiderruflich. Die Funktion erfordert `\"bestaetigt\": true` im Körper, sonst wird sie nicht ausgeführt. Bei Erfolg enthält `data.data` die Felder `book_id` und `deleted: true`, die Meldung lautet „Buch gelöscht“. Konnte das Buch nicht gelöscht werden, ist `success` false und `deleted` false. Im Code wird ausschließlich das Buchobjekt entfernt; die zugehörigen Kapitel werden nicht gesondert gelöscht. Willst du sie mitlöschen, rufe vorher `get_book_chapters` und `delete_chapter` für jedes Kapitel auf.\n\n**Gut zu wissen**\n\n- Das Löschen kann nicht rückgängig gemacht werden. Mit `copy_book` legst du bei Bedarf vorher eine Sicherheitskopie an.\n- Kapitel eines gelöschten Buchs bleiben als eigene Objekte bestehen, weil nur das Buch selbst entfernt wird.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/delete_book \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"book_id\": 398,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/delete_book\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"book_id\": 398,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/delete_book\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"book_id\": 398,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"book_id":{"type":"integer","description":"ID des zu löschenden Buchs als ganze Zahl (Pflicht). Das Buch muss zu deinem Unternehmen gehören; andernfalls kommt „Buch nicht gefunden oder kein Zugriff“.","example":398},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["book_id","bestaetigt"]},"example":{"book_id":398,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"delete_book","stufe":"loeschen","data":{"success":true,"data":{"book_id":398,"deleted":true},"message":"Buch gelöscht","timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/copy_book":{"post":{"tags":["Bücher"],"operationId":"books__copy_book","summary":"Buch kopieren","description":"Erstellt aus einem bestehenden Buch eine Kopie als neues Buch mit dem Status `draft`. Übernommen werden Untertitel, Autor, Beschreibung, Kategorie, Sprache, Preis und Währung; ISBN, Veröffentlichungsdaten und weitere Metadaten werden nicht kopiert. Ohne `title` heißt die Kopie „<Originaltitel> (Kopie)“. Standardmäßig werden auch alle Kapitel dupliziert (Titel, Untertitel, Zusammenfassung, Einleitung, Inhalt, Reihenfolge, Status); mit `include_chapters: false` entsteht ein Buch ohne Kapitel. Die Antwort zeigt das neue Buch in der ausführlichen Ansicht mit der Meldung „Buchkopie erstellt“. Die Kopie ist ein eigenständiges Buch: Änderungen am Original wirken nicht auf sie.\n\n**Gut zu wissen**\n\n- In der Antwort steht `chapters_count` für die Kopie immer auf 0, auch wenn Kapitel mitkopiert wurden. Die tatsächliche Zahl liefert ein anschließender Aufruf von `get_book`.\n- Zum Prüfen der kopierten Kapitel nutzt du `get_book_chapters` mit der neuen `id`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/copy_book \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"book_id\": 412,\n    \"title\": \"Der ruhige Betrieb – Überarbeitung\",\n    \"include_chapters\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/copy_book\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"book_id\": 412,\n        \"title\": \"Der ruhige Betrieb – Überarbeitung\",\n        \"include_chapters\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/copy_book\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"book_id\": 412,\n      \"title\": \"Der ruhige Betrieb – Überarbeitung\",\n      \"include_chapters\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"book_id":{"type":"integer","description":"ID des Quellbuchs als ganze Zahl (Pflicht). Das Buch muss zu deinem Unternehmen gehören.","example":412},"title":{"type":"string","description":"Optionaler Titel der Kopie. Ohne Angabe wird der Originaltitel mit dem Zusatz „ (Kopie)“ verwendet.","example":"Der ruhige Betrieb – Überarbeitung"},"include_chapters":{"type":"boolean","description":"Wahrheitswert, ob die Kapitel mitkopiert werden. Standard ist `true`; mit `false` wird nur das Buch ohne Kapitel angelegt.","default":true,"example":true}},"required":["book_id"]},"example":{"book_id":412,"title":"Der ruhige Betrieb – Überarbeitung","include_chapters":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"copy_book","stufe":"schreiben","data":{"success":true,"data":{"id":431,"title":"Der ruhige Betrieb – Überarbeitung","subtitle":"Wie kleine Teams Abläufe entschlacken","author":"Mira Falkenberg","status":"draft","company_id":20481,"chapters_count":0,"created_at":"2026-03-18T10:42:15+01:00","updated_at":"2026-03-18T10:42:15+01:00","isbn":null,"description":"Ein Praxisbuch über schlanke Prozesse im Handwerk.","lang":"de","price":"24.90"},"message":"Buchkopie erstellt","timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/get_book_chapters":{"post":{"tags":["Bücher"],"operationId":"books__get_book_chapters","summary":"Kapitel eines Buchs abrufen","description":"Liefert alle Kapitel eines Buchs, aufsteigend nach dem Feld `order` sortiert. In `data` stehen `items` (die Kapitel) und `count` (Anzahl). Jedes Kapitel enthält `id`, `title`, `subtitle`, `summary`, `content` (kompletter Kapiteltext), `order`, `status` und `book_id`. Eine Seiteneinteilung gibt es nicht; es werden immer alle Kapitel des Buchs geliefert, bei langen Büchern entsprechend große Antworten. Die Kapitel-`id` brauchst du für `update_chapter` und `delete_chapter`, die Reihenfolge änderst du mit `reorder_chapters`.\n\n**Gut zu wissen**\n\n- Die Antwort enthält den vollständigen Text jedes Kapitels. Für eine reine Übersicht reicht bei vielen Kapiteln oft `get_book` mit `chapters_count`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/get_book_chapters \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"book_id\": 412\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/get_book_chapters\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"book_id\": 412,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/get_book_chapters\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"book_id\": 412\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"book_id":{"type":"integer","description":"ID des Buchs als ganze Zahl (Pflicht). Das Buch muss zu deinem Unternehmen gehören, sonst kommt „Buch nicht gefunden oder kein Zugriff“.","example":412}},"required":["book_id"]},"example":{"book_id":412}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"get_book_chapters","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":9101,"title":"Warum Abläufe unsichtbar werden","subtitle":null,"summary":"Wie sich Routinen einschleichen.","content":"<p>Jeder Betrieb hat Routinen, die niemand mehr hinterfragt …</p>","order":1,"status":"draft","book_id":412},{"id":9102,"title":"Der erste Schritt","subtitle":null,"summary":null,"content":"<p>Beginne mit einer Liste aller wiederkehrenden Aufgaben.</p>","order":2,"status":"draft","book_id":412}],"count":2},"message":null,"timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/create_chapter":{"post":{"tags":["Bücher"],"operationId":"books__create_chapter","summary":"Kapitel anlegen","description":"Legt ein neues Kapitel in einem Buch an. Pflicht sind `book_id` und `title`. Ohne `order` wird das Kapitel ans Ende gestellt (Anzahl der vorhandenen Kapitel plus 1); mit `order` bestimmst du die Position selbst, vorhandene Kapitel werden dabei nicht verschoben. `content` ist der Kapiteltext (Formatierung als HTML möglich), `status` ein frei wählbarer Text. Die Antwort enthält das neue Kapitel mit `id`, `title`, `subtitle`, `summary`, `content`, `order`, `status` und `book_id` und die Meldung „Kapitel erstellt“. Danach kannst du den Text mit `update_chapter` ändern oder die Reihenfolge mit `reorder_chapters` anpassen.\n\n**Gut zu wissen**\n\n- Der Kapitel-Status ist ein freies Textfeld ohne feste Werteliste. Nutze innerhalb deines Unternehmens einheitliche Werte, z. B. `draft`.\n- Auch eine `order` von 0 wird übernommen; nur ein fehlender Wert löst die automatische Position am Ende aus.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/create_chapter \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"book_id\": 412,\n    \"title\": \"Warum Abläufe unsichtbar werden\",\n    \"content\": \"<p>Jeder Betrieb hat Routinen, die niemand mehr hinterfragt.</p>\",\n    \"status\": \"draft\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/create_chapter\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"book_id\": 412,\n        \"title\": \"Warum Abläufe unsichtbar werden\",\n        \"content\": \"<p>Jeder Betrieb hat Routinen, die niemand mehr hinterfragt.</p>\",\n        \"status\": \"draft\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/create_chapter\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"book_id\": 412,\n      \"title\": \"Warum Abläufe unsichtbar werden\",\n      \"content\": \"<p>Jeder Betrieb hat Routinen, die niemand mehr hinterfragt.</p>\",\n      \"status\": \"draft\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"book_id":{"type":"integer","description":"ID des Buchs als ganze Zahl (Pflicht), zu dem das Kapitel gehört. Das Buch muss zu deinem Unternehmen gehören.","example":412},"title":{"type":"string","description":"Titel des Kapitels als Text (Pflicht, nicht leer). Er dient auch als Grundlage für den internen Objektschlüssel.","example":"Warum Abläufe unsichtbar werden"},"content":{"type":"string","description":"Optionaler Inhalt des Kapitels als Text oder HTML. Ohne Angabe bleibt das Kapitel leer; der Inhalt lässt sich später mit `update_chapter` ergänzen.","example":"<p>Jeder Betrieb hat Routinen, die niemand mehr hinterfragt.</p>"},"order":{"type":"integer","description":"Optionale Position des Kapitels (ganze Zahl). Ohne Angabe wird das Kapitel hinten angefügt. Bereits vorhandene Positionen werden nicht automatisch umsortiert."},"status":{"type":"string","description":"Optionaler Status des Kapitels als frei wählbarer Text, z. B. `draft`. Ohne Angabe bleibt der Status leer.","example":"draft"}},"required":["book_id","title"]},"example":{"book_id":412,"title":"Warum Abläufe unsichtbar werden","content":"<p>Jeder Betrieb hat Routinen, die niemand mehr hinterfragt.</p>","status":"draft"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"create_chapter","stufe":"schreiben","data":{"success":true,"data":{"id":9101,"title":"Warum Abläufe unsichtbar werden","subtitle":null,"summary":null,"content":"<p>Jeder Betrieb hat Routinen, die niemand mehr hinterfragt.</p>","order":1,"status":"draft","book_id":412},"message":"Kapitel erstellt","timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/update_chapter":{"post":{"tags":["Bücher"],"operationId":"books__update_chapter","summary":"Kapitel ändern","description":"Ändert Felder eines bestehenden Kapitels. Alle zu ändernden Werte gehören in das Objekt `data`; nur dort genannte Felder werden angefasst. Unterstützt sind `title`, `subtitle`, `summary`, `introduction`, `content`, `order` und `status`, andere Schlüssel werden ohne Fehlermeldung ignoriert. Zurück kommt das Kapitel mit `id`, `title`, `subtitle`, `summary`, `content`, `order`, `status` und `book_id` sowie der Meldung „Kapitel aktualisiert“. Die Kapitel-`id` findest du mit `get_book_chapters`. Änderungen der Reihenfolge mehrerer Kapitel erledigst du in einem Zug mit `reorder_chapters`.\n\n**Gut zu wissen**\n\n- `title`, `subtitle` und `status` werden vor dem Speichern HTML-maskiert (`&` wird zu `&amp;`, Anführungszeichen werden umgewandelt). `content`, `summary` und `introduction` bleiben unverändert.\n- Unbekannte Schlüssel in `data` werden stillschweigend übergangen; prüfe die Antwort, ob die Änderung angekommen ist.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/update_chapter \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"chapter_id\": 9101,\n    \"data\": {\n        \"content\": \"<p>Neuer, überarbeiteter Text des Kapitels.</p>\",\n        \"status\": \"ready\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/update_chapter\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"chapter_id\": 9101,\n        \"data\": {\n            \"content\": \"<p>Neuer, überarbeiteter Text des Kapitels.</p>\",\n            \"status\": \"ready\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/update_chapter\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"chapter_id\": 9101,\n      \"data\": {\n          \"content\": \"<p>Neuer, überarbeiteter Text des Kapitels.</p>\",\n          \"status\": \"ready\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"chapter_id":{"type":"integer","description":"ID des Kapitels als ganze Zahl (Pflicht). Das Kapitel muss zu einem Buch deines Unternehmens gehören.","example":9101},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern (Pflicht). Erlaubt sind `title`, `subtitle`, `summary`, `introduction`, `content` (Text oder HTML), `order` (ganze Zahl) und `status` (Text). Nur enthaltene Felder werden geändert.","properties":{"title":{"type":"string","description":"Neuer Titel des Kapitels (optional)"},"subtitle":{"type":"string","description":"Neuer Untertitel des Kapitels (optional)"},"summary":{"type":"string","description":"Neue Zusammenfassung des Kapitels (optional)"},"introduction":{"type":"string","description":"Neue Einleitung des Kapitels (optional)"},"content":{"type":"string","description":"Neuer Hauptinhalt des Kapitels (optional)"},"order":{"type":"integer","description":"Neue Reihenfolge des Kapitels (optional)"},"status":{"type":"string","description":"Neuer Status des Kapitels (optional)"}},"additionalProperties":false,"example":{"content":"<p>Neuer, überarbeiteter Text des Kapitels.</p>","status":"ready"}}},"required":["chapter_id","data"]},"example":{"chapter_id":9101,"data":{"content":"<p>Neuer, überarbeiteter Text des Kapitels.</p>","status":"ready"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"update_chapter","stufe":"schreiben","data":{"success":true,"data":{"id":9101,"title":"Warum Abläufe unsichtbar werden","subtitle":null,"summary":"Wie sich Routinen einschleichen.","content":"<p>Neuer, überarbeiteter Text des Kapitels.</p>","order":1,"status":"ready","book_id":412},"message":"Kapitel aktualisiert","timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/delete_chapter":{"post":{"tags":["Bücher"],"operationId":"books__delete_chapter","summary":"Kapitel löschen","description":"Löscht ein einzelnes Kapitel eines Buchs unwiderruflich. Die Funktion erfordert `\"bestaetigt\": true` im Körper. Vorher wird geprüft, dass das zugehörige Buch zu deinem Unternehmen gehört. Die Antwort enthält in `data.data` nur die `chapter_id` des gelöschten Kapitels und die Meldung „Kapitel gelöscht“. Die übrigen Kapitel behalten ihre bisherige `order`; es entstehen also gegebenenfalls Lücken in der Nummerierung, die du mit `reorder_chapters` schließen kannst. Das Buch selbst bleibt unverändert bestehen.\n\n**Gut zu wissen**\n\n- Das Löschen ist endgültig. Eine Sicherung des Texts holst du vorher mit `get_book_chapters`.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/delete_chapter \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"chapter_id\": 9102,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/delete_chapter\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"chapter_id\": 9102,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/delete_chapter\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"chapter_id\": 9102,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"chapter_id":{"type":"integer","description":"ID des zu löschenden Kapitels als ganze Zahl (Pflicht). Die ID stammt aus `get_book_chapters` oder `create_chapter`; das Kapitel muss zu einem Buch deines Unternehmens gehören.","example":9102},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["chapter_id","bestaetigt"]},"example":{"chapter_id":9102,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"delete_chapter","stufe":"loeschen","data":{"success":true,"data":{"chapter_id":9102},"message":"Kapitel gelöscht","timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/reorder_chapters":{"post":{"tags":["Bücher"],"operationId":"books__reorder_chapters","summary":"Kapitelreihenfolge ändern","description":"Setzt die Reihenfolge mehrerer Kapitel eines Buchs in einem Aufruf neu. In `ordering` übergibst du eine Liste von Paaren aus `chapter_id` und `order`; jedes Kapitel bekommt die angegebene Position als neuen Wert im Feld `order`. Kapitel, die in der Liste fehlen, behalten ihre bisherige Position. Einträge ohne `chapter_id` oder `order` sowie Kapitel, die zu einem anderen Buch gehören, werden stillschweigend übersprungen. Die Antwort enthält nur die `book_id` mit der Meldung „Kapitel-Reihenfolge aktualisiert“; die neue Reihenfolge siehst du anschließend mit `get_book_chapters`.\n\n**Gut zu wissen**\n\n- Die Positionen werden exakt so gesetzt, wie du sie angibst; doppelte oder lückenhafte Werte prüft die Funktion nicht.\n- Verweist ein Eintrag auf eine nicht existierende Kapitel-ID, bricht der Aufruf mit einem Fehler ab. Bereits verarbeitete Einträge davor sind dann schon gespeichert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/reorder_chapters \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"book_id\": 412,\n    \"ordering\": [\n        {\n            \"chapter_id\": 9102,\n            \"order\": 1\n        },\n        {\n            \"chapter_id\": 9101,\n            \"order\": 2\n        }\n    ]\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/reorder_chapters\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"book_id\": 412,\n        \"ordering\": [\n            {\n                \"chapter_id\": 9102,\n                \"order\": 1,\n            },\n            {\n                \"chapter_id\": 9101,\n                \"order\": 2,\n            },\n        ],\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/reorder_chapters\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"book_id\": 412,\n      \"ordering\": [\n          {\n              \"chapter_id\": 9102,\n              \"order\": 1\n          },\n          {\n              \"chapter_id\": 9101,\n              \"order\": 2\n          }\n      ]\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"book_id":{"type":"integer","description":"ID des Buchs als ganze Zahl (Pflicht). Nur Kapitel dieses Buchs werden umsortiert; Kapitel anderer Bücher in der Liste werden ignoriert.","example":412},"ordering":{"type":"array","items":{"type":"object","properties":{"chapter_id":{"type":"integer"},"order":{"type":"integer"}},"required":["chapter_id","order"]},"description":"Liste von Objekten (Pflicht), jeweils mit `chapter_id` (ID des Kapitels) und `order` (neue Position als ganze Zahl). Beispiel: `[{\"chapter_id\": 9101, \"order\": 2}, {\"chapter_id\": 9102, \"order\": 1}]`.","example":[{"chapter_id":9102,"order":1},{"chapter_id":9101,"order":2}]}},"required":["book_id","ordering"]},"example":{"book_id":412,"ordering":[{"chapter_id":9102,"order":1},{"chapter_id":9101,"order":2}]}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"reorder_chapters","stufe":"schreiben","data":{"success":true,"data":{"book_id":412},"message":"Kapitel-Reihenfolge aktualisiert","timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/get_book_statistics":{"post":{"tags":["Bücher"],"operationId":"books__get_book_statistics","summary":"Buchstatistiken abrufen","description":"Liefert aggregierte Kennzahlen über alle Bücher deines Unternehmens. Die Funktion hat keine Parameter. In `data` stehen `total_books` (Anzahl aller Bücher), `by_status` (Anzahl je Status mit den Schlüsseln `draft`, `published` und `in_progress`), `total_chapters` (Summe aller Kapitel) und `average_chapters_per_book` (Durchschnitt, auf zwei Nachkommastellen gerundet, 0 ohne Bücher). Bücher ohne Status werden als `draft` gezählt. Die Statistik liest alle Bücher und Kapitel und kann bei sehr großen Beständen etwas länger dauern.\n\n**Gut zu wissen**\n\n- Bücher mit einem anderen Statuswert (etwa `publish`) zählen zu `total_books`, tauchen aber in keinem Eintrag von `by_status` auf. Die Summe von `by_status` kann daher kleiner sein als `total_books`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/get_book_statistics \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/get_book_statistics\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/get_book_statistics\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"get_book_statistics","stufe":"lesen","data":{"success":true,"data":{"total_books":7,"by_status":{"draft":4,"published":2,"in_progress":1},"total_chapters":31,"average_chapters_per_book":4.43},"message":null,"timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/export_book_as_pdf":{"post":{"tags":["Bücher"],"operationId":"books__export_book_as_pdf","summary":"Buchexport starten","description":"Stellt einen Export des Buchs in die Warteschlange und gibt sofort eine Export-ID zurück; die eigentliche Erzeugung läuft im Hintergrund. Angefordert wird intern das Format RTF mit dem Hinweis, dass ein PDF gewünscht ist. In `data.data` stehen `export_id`, `book_id` und ein Hinweistext, dass der Status über die Export-API abgefragt werden kann. Die Funktion liefert weder die Datei noch den Fortschritt; beides gehört nicht zu dieser Schnittstelle. Obwohl sie nichts verändert, was du im Buch siehst, legt jeder Aufruf einen neuen Exportauftrag an.\n\n**Gut zu wissen**\n\n- Die Antwort bestätigt nur das Einreihen. Ob und wann der Export fertig ist, erfährst du über diese Funktion nicht.\n- Mehrfaches Aufrufen erzeugt mehrere Exportaufträge mit jeweils eigener `export_id`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/export_book_as_pdf \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"book_id\": 412\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/export_book_as_pdf\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"book_id\": 412,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/export_book_as_pdf\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"book_id\": 412\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"book_id":{"type":"integer","description":"ID des zu exportierenden Buchs als ganze Zahl (Pflicht). Das Buch muss zu deinem Unternehmen gehören, sonst kommt „Buch nicht gefunden oder kein Zugriff“.","example":412}},"required":["book_id"]},"example":{"book_id":412}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"export_book_as_pdf","stufe":"lesen","data":{"success":true,"data":{"export_id":"export_65f8a1c2d4e3b7.12345678","book_id":412,"note":"Export gestartet. Status kann über die Export-API abgefragt werden."},"message":null,"timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/books/get_isbn_status":{"post":{"tags":["Bücher"],"operationId":"books__get_isbn_status","summary":"ISBN-Status prüfen","description":"Prüft, ob dem Buch eine ISBN aus dem ISBN-Bestand zugewiesen ist. Ist keine zugewiesen, lautet die Antwort `assigned: false` und `status: \"available\"` mit der Meldung „Keine ISBN zugewiesen“. Bei einer Zuweisung enthält `data.data` `assigned: true`, die `isbn`, den `status` des Eintrags (ohne Wert `assigned`) und die `isbn_id`. Gemeint ist die Zuweisung im ISBN-Bestand, nicht das Textfeld `isbn` am Buch, das `get_book` zeigt und `update_book` setzt. Das Buch muss zu deinem Unternehmen gehören.\n\n**Gut zu wissen**\n\n- Bekannter Fehler im aktuellen Stand: Der Aufruf endet derzeit mit dem Fehler 422 „Die Funktion konnte nicht ausgeführt werden“, weil die interne Klasse für den ISBN-Bestand nicht gefunden wird. Das Textfeld `isbn` am Buch erhältst du stattdessen über `get_book`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/books/get_isbn_status \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"book_id\": 412\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/books/get_isbn_status\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"book_id\": 412,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/books/get_isbn_status\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"book_id\": 412\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"book_id":{"type":"integer","description":"ID des Buchs als ganze Zahl (Pflicht). Das Buch muss zu deinem Unternehmen gehören, sonst kommt „Buch nicht gefunden oder kein Zugriff“.","example":412}},"required":["book_id"]},"example":{"book_id":412}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"books","werkzeug":"get_isbn_status","stufe":"lesen","data":{"success":true,"data":{"assigned":true,"isbn":"978-3-98765-432-1","status":"assigned","isbn_id":5521},"message":"ISBN-Status ermittelt","timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/audiobooks/get_audiobooks":{"post":{"tags":["Hörbücher"],"operationId":"audiobooks__get_audiobooks","summary":"Hörbücher auflisten","description":"Liefert die Hörbücher deines Unternehmens, neueste zuerst (Sortierung nach Erstellungsdatum absteigend). Ohne Angaben kommen die ersten 10. Mit `limit` und `offset` blätterst du, mit `status` filterst du exakt auf einen Statuswert. In `data` stehen `items`, `count` (Länge der aktuellen Antwort, nicht die Gesamtzahl), `limit` und `offset`. Jeder Eintrag enthält `id`, `title`, `subtitle`, `status`, `company_id`, `lang`, `created_at` und `updated_at`. Beschreibung, Preis, Währung und Kapitelanzahl liefert erst `get_audiobook`.\n\n**Gut zu wissen**\n\n- Der Statusfilter ist ein freier Text und wird exakt verglichen. Üblich sind `draft`, `in_production` und `ready`; im Betrieb setzt die Verarbeitung außerdem `processing` und `error`.\n- `count` ist die Anzahl der Einträge dieser Seite. Blättere weiter, bis weniger als `limit` Einträge zurückkommen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/audiobooks/get_audiobooks \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"limit\": 5,\n    \"offset\": 0,\n    \"status\": \"draft\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/audiobooks/get_audiobooks\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"limit\": 5,\n        \"offset\": 0,\n        \"status\": \"draft\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/audiobooks/get_audiobooks\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"limit\": 5,\n      \"offset\": 0,\n      \"status\": \"draft\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","description":"Höchstzahl der Hörbücher in der Antwort (ganze Zahl, Standard 10). Eine Obergrenze erzwingt der Code nicht.","example":5},"offset":{"type":"integer","description":"Anzahl der Einträge, die am Anfang übersprungen werden (ganze Zahl, Standard 0), für das seitenweise Blättern zusammen mit `limit`.","example":0},"status":{"type":"string","description":"Optionaler Filter auf den Status als Text, z. B. `draft`, `in_production`, `ready`, `processing` oder `error`. Der Vergleich ist exakt; ohne Angabe werden alle Status geliefert.","example":"draft"}}},"example":{"limit":5,"offset":0,"status":"draft"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"audiobooks","werkzeug":"get_audiobooks","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":731,"title":"Der ruhige Betrieb (Hörbuch)","subtitle":"Abläufe entschlacken zum Zuhören","status":"draft","company_id":20481,"lang":"de","created_at":"2026-02-10T14:03:11+01:00","updated_at":"2026-03-17T18:20:45+01:00"},{"id":702,"title":"Kundenbriefe, die gelesen werden (Hörbuch)","subtitle":null,"status":"draft","company_id":20481,"lang":"de","created_at":"2026-02-10T14:03:11+01:00","updated_at":"2026-03-17T18:20:45+01:00"}],"count":2,"limit":5,"offset":0},"message":null,"timestamp":"2026-03-18 11:05:41","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/audiobooks/get_audiobook":{"post":{"tags":["Hörbücher"],"operationId":"audiobooks__get_audiobook","summary":"Hörbuch abrufen","description":"Liefert ein einzelnes Hörbuch deines Unternehmens anhand seiner ID. Zusätzlich zu den Feldern der Listenansicht (`id`, `title`, `subtitle`, `status`, `company_id`, `lang`, `created_at`, `updated_at`) enthält die Antwort `description`, `price`, `currency` und `chapters_count` (Zahl der Kapitel). Die Kapitel selbst liefert `get_audiobook_chapters`. Gehört die ID nicht zu einem Hörbuch deines Unternehmens oder existiert sie nicht, kommt `success: false` mit dem Hinweis „Hörbuch nicht gefunden oder kein Zugriff“. Die ID erhältst du aus `get_audiobooks`, `search_audiobooks` oder `create_audiobook`.\n\n**Gut zu wissen**\n\n- Unbekannte oder fremde IDs führen zu einer regulären Antwort mit `data.success: false` und `data.error`, nicht zu einem HTTP-Fehlerstatus.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/audiobooks/get_audiobook \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"audiobook_id\": 731\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/audiobooks/get_audiobook\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"audiobook_id\": 731,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/audiobooks/get_audiobook\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"audiobook_id\": 731\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"audiobook_id":{"type":"integer","description":"ID des Hörbuchs als ganze Zahl (Pflicht). Hörbücher anderer Unternehmen werden wie nicht vorhanden behandelt.","example":731}},"required":["audiobook_id"]},"example":{"audiobook_id":731}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"audiobooks","werkzeug":"get_audiobook","stufe":"lesen","data":{"success":true,"data":{"id":731,"title":"Der ruhige Betrieb (Hörbuch)","subtitle":"Abläufe entschlacken zum Zuhören","status":"draft","company_id":20481,"lang":"de","created_at":"2026-02-10T14:03:11+01:00","updated_at":"2026-03-17T18:20:45+01:00","description":"Das Praxisbuch als Hörbuch für unterwegs.","price":"14.90","currency":"EUR","chapters_count":3},"message":null,"timestamp":"2026-03-18 11:05:41","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/audiobooks/search_audiobooks":{"post":{"tags":["Hörbücher"],"operationId":"audiobooks__search_audiobooks","summary":"Hörbücher suchen","description":"Durchsucht die Hörbücher deines Unternehmens per Teilstring-Suche in Titel und Beschreibung. Die Treffer sind nach Titel aufsteigend sortiert; ohne `limit` kommen höchstens 10. In `data` stehen `items` (Hörbücher im Format von `get_audiobooks`) und `count`. Anders als bei `get_audiobooks` enthält die Antwort weder `limit` noch `offset`. Untertitel werden nicht durchsucht. Für Details zu einem Treffer folgt `get_audiobook`; zum Durchblättern aller Hörbücher nimmst du `get_audiobooks`.\n\n**Gut zu wissen**\n\n- Der Suchbegriff wird vor der Suche HTML-maskiert (`&` wird zu `&amp;`). Sonderzeichen finden daher nur, wenn sie im Text genauso gespeichert sind.\n- Die Platzhalter `%` und `_` im Suchbegriff wirken als SQL-Platzhalter.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/audiobooks/search_audiobooks \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\": \"Betrieb\",\n    \"limit\": 5\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/audiobooks/search_audiobooks\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"query\": \"Betrieb\",\n        \"limit\": 5,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/audiobooks/search_audiobooks\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"query\": \"Betrieb\",\n      \"limit\": 5\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Suchbegriff als Text (Pflicht). Gesucht wird als Teilstring in Titel und Beschreibung. Leere Werte werden abgelehnt.","example":"Betrieb"},"limit":{"type":"integer","description":"Höchstzahl der Treffer (ganze Zahl, Standard 10). Eine Obergrenze erzwingt der Code nicht.","example":5},"offset":{"type":"integer","description":"Anzahl der Treffer, die übersprungen werden (ganze Zahl, Standard 0), um durch längere Trefferlisten zu blättern."}},"required":["query"]},"example":{"query":"Betrieb","limit":5}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"audiobooks","werkzeug":"search_audiobooks","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":731,"title":"Der ruhige Betrieb (Hörbuch)","subtitle":"Abläufe entschlacken zum Zuhören","status":"draft","company_id":20481,"lang":"de","created_at":"2026-02-10T14:03:11+01:00","updated_at":"2026-03-17T18:20:45+01:00"}],"count":1},"message":null,"timestamp":"2026-03-18 11:05:41","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/audiobooks/create_audiobook":{"post":{"tags":["Hörbücher"],"operationId":"audiobooks__create_audiobook","summary":"Hörbuch anlegen","description":"Legt ein neues Hörbuch für dein Unternehmen an. Pflicht ist nur der `title`. Das Hörbuch wird als veröffentlichtes Datenobjekt im Ordner für Hörbücher gespeichert; der Titel dient zugleich als Objektschlüssel. Ohne Statusangabe wird `draft` gesetzt. Zurück kommt das Hörbuch in der ausführlichen Ansicht (`id`, `title`, `subtitle`, `status`, `company_id`, `lang`, `created_at`, `updated_at`, `description`, `price`, `currency`, `chapters_count`) mit der Meldung „Hörbuch erstellt“. Mit der `id` fügst du Kapitel mit `create_audiobook_chapter` hinzu. Preis, Währung, Sprache und Veröffentlichungsdatum setzt du anschließend mit `update_audiobook`.\n\n**Gut zu wissen**\n\n- Der Status wird nicht gegen die Werteliste geprüft. Das Datenfeld kennt laut Klassendefinition `draft` und `publish`; die Verarbeitung setzt später u. a. `processing`, `ready` und `error`.\n- Beim Anlegen lassen sich nur Titel, Untertitel, Beschreibung und Status setzen.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/audiobooks/create_audiobook \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Der ruhige Betrieb (Hörbuch)\",\n    \"subtitle\": \"Abläufe entschlacken zum Zuhören\",\n    \"description\": \"Das Praxisbuch als Hörbuch für unterwegs.\",\n    \"status\": \"draft\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/audiobooks/create_audiobook\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"title\": \"Der ruhige Betrieb (Hörbuch)\",\n        \"subtitle\": \"Abläufe entschlacken zum Zuhören\",\n        \"description\": \"Das Praxisbuch als Hörbuch für unterwegs.\",\n        \"status\": \"draft\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/audiobooks/create_audiobook\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"title\": \"Der ruhige Betrieb (Hörbuch)\",\n      \"subtitle\": \"Abläufe entschlacken zum Zuhören\",\n      \"description\": \"Das Praxisbuch als Hörbuch für unterwegs.\",\n      \"status\": \"draft\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Titel des Hörbuchs als Text (Pflicht, nicht leer). Er dient auch als Grundlage für den internen Objektschlüssel.","example":"Der ruhige Betrieb (Hörbuch)"},"subtitle":{"type":"string","description":"Optionaler Untertitel als Text. Leere Werte werden ignoriert.","example":"Abläufe entschlacken zum Zuhören"},"description":{"type":"string","description":"Optionale Beschreibung als Text. Sie wird von `search_audiobooks` mit durchsucht. Leere Werte werden ignoriert.","example":"Das Praxisbuch als Hörbuch für unterwegs."},"status":{"type":"string","description":"Optionaler Status. Laut Schema `draft`, `in_production` oder `ready`. Ohne Angabe wird `draft` gesetzt.","enum":["draft","in_production","ready"],"example":"draft"}},"required":["title"]},"example":{"title":"Der ruhige Betrieb (Hörbuch)","subtitle":"Abläufe entschlacken zum Zuhören","description":"Das Praxisbuch als Hörbuch für unterwegs.","status":"draft"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"audiobooks","werkzeug":"create_audiobook","stufe":"schreiben","data":{"success":true,"data":{"id":731,"title":"Der ruhige Betrieb (Hörbuch)","subtitle":"Abläufe entschlacken zum Zuhören","status":"draft","company_id":20481,"lang":null,"created_at":"2026-02-10T14:03:11+01:00","updated_at":"2026-03-17T18:20:45+01:00","description":"Das Praxisbuch als Hörbuch für unterwegs.","price":null,"currency":null,"chapters_count":0},"message":"Hörbuch erstellt","timestamp":"2026-03-18 11:05:41","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/audiobooks/update_audiobook":{"post":{"tags":["Hörbücher"],"operationId":"audiobooks__update_audiobook","summary":"Hörbuchdaten ändern","description":"Ändert Felder eines bestehenden Hörbuchs. Im Objekt `data` übergibst du nur die Felder, die sich ändern sollen. Unterstützt sind `title`, `subtitle`, `description`, `status`, `publishDate` (Datum, ISO 8601), `price` (Zahl), `currency` und `lang`. Andere Schlüssel werden ohne Fehlermeldung ignoriert, ein leeres `publishDate` ändert nichts. Zurück kommt das Hörbuch in der ausführlichen Ansicht (mit `description`, `price`, `currency` und `chapters_count`) und der Meldung „Hörbuch aktualisiert“. Kapitel änderst du mit `update_audiobook_chapter`, nicht hier.\n\n**Gut zu wissen**\n\n- `title`, `subtitle`, `status`, `currency` und `lang` werden vor dem Speichern HTML-maskiert (`&` wird zu `&amp;`); `description` bleibt unverändert.\n- Unbekannte Schlüssel in `data` werden stillschweigend übergangen. Prüfe in der Antwort, ob die Änderung übernommen wurde.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/audiobooks/update_audiobook \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"audiobook_id\": 731,\n    \"data\": {\n        \"status\": \"in_production\",\n        \"price\": 14.9,\n        \"currency\": \"EUR\",\n        \"lang\": \"de\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/audiobooks/update_audiobook\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"audiobook_id\": 731,\n        \"data\": {\n            \"status\": \"in_production\",\n            \"price\": 14.9,\n            \"currency\": \"EUR\",\n            \"lang\": \"de\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/audiobooks/update_audiobook\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"audiobook_id\": 731,\n      \"data\": {\n          \"status\": \"in_production\",\n          \"price\": 14.9,\n          \"currency\": \"EUR\",\n          \"lang\": \"de\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"audiobook_id":{"type":"integer","description":"ID des zu ändernden Hörbuchs als ganze Zahl (Pflicht). Es muss zu deinem Unternehmen gehören, sonst kommt „Hörbuch nicht gefunden oder kein Zugriff“.","example":731},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern (Pflicht). Wirksam sind `title`, `subtitle`, `description`, `status` (z. B. `draft`, `in_production`, `ready`), `publishDate` (ISO 8601, z. B. `2026-09-01`), `price` (Zahl), `currency` (z. B. `EUR`) und `lang` (z. B. `de`).","example":{"status":"in_production","price":14.9,"currency":"EUR","lang":"de"}}},"required":["audiobook_id","data"]},"example":{"audiobook_id":731,"data":{"status":"in_production","price":14.9,"currency":"EUR","lang":"de"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"audiobooks","werkzeug":"update_audiobook","stufe":"schreiben","data":{"success":true,"data":{"id":731,"title":"Der ruhige Betrieb (Hörbuch)","subtitle":"Abläufe entschlacken zum Zuhören","status":"in_production","company_id":20481,"lang":"de","created_at":"2026-02-10T14:03:11+01:00","updated_at":"2026-03-18T11:05:41+01:00","description":"Das Praxisbuch als Hörbuch für unterwegs.","price":"14.90","currency":"EUR","chapters_count":3},"message":"Hörbuch aktualisiert","timestamp":"2026-03-18 11:05:41","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/audiobooks/delete_audiobook":{"post":{"tags":["Hörbücher"],"operationId":"audiobooks__delete_audiobook","summary":"Hörbuch löschen","description":"Löscht ein Hörbuch deines Unternehmens unwiderruflich. Die Funktion erfordert `\"bestaetigt\": true` im Körper. Vorher wird geprüft, dass das Hörbuch zu deinem Unternehmen gehört. Die Antwort enthält in `data.data` nur die `audiobook_id` und die Meldung „Hörbuch gelöscht“. Im Code wird ausschließlich das Hörbuchobjekt entfernt; die Kapitel werden nicht gesondert gelöscht. Willst du sie ebenfalls entfernen, rufe vorher `get_audiobook_chapters` und `delete_audiobook_chapter` für jedes Kapitel auf.\n\n**Gut zu wissen**\n\n- Das Löschen kann nicht rückgängig gemacht werden.\n- Kapitel eines gelöschten Hörbuchs bleiben als eigene Objekte bestehen, weil nur das Hörbuch selbst entfernt wird.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/audiobooks/delete_audiobook \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"audiobook_id\": 702,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/audiobooks/delete_audiobook\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"audiobook_id\": 702,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/audiobooks/delete_audiobook\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"audiobook_id\": 702,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"audiobook_id":{"type":"integer","description":"ID des zu löschenden Hörbuchs als ganze Zahl (Pflicht). Das Hörbuch muss zu deinem Unternehmen gehören; andernfalls kommt „Hörbuch nicht gefunden oder kein Zugriff“.","example":702},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["audiobook_id","bestaetigt"]},"example":{"audiobook_id":702,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"audiobooks","werkzeug":"delete_audiobook","stufe":"loeschen","data":{"success":true,"data":{"audiobook_id":702},"message":"Hörbuch gelöscht","timestamp":"2026-03-18 11:05:41","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/audiobooks/get_audiobook_chapters":{"post":{"tags":["Hörbücher"],"operationId":"audiobooks__get_audiobook_chapters","summary":"Kapitel eines Hörbuchs abrufen","description":"Liefert alle Kapitel eines Hörbuchs, aufsteigend nach dem Feld `order` sortiert. In `data` stehen `items` und `count`. Jedes Kapitel enthält `id`, `title`, `status`, `order`, `content_length` (Länge des Kapiteltexts in Zeichen) und `audiobook_id`. Der Kapiteltext selbst ist in der Antwort nicht enthalten. Die Kapitel-`id` brauchst du für `update_audiobook_chapter`, `delete_audiobook_chapter` und `create_chapter_audio`. Eine Seiteneinteilung gibt es nicht, es kommen immer alle Kapitel.\n\n**Gut zu wissen**\n\n- Der Kapitel-Status ist ein freier Text. Die Verarbeitung nutzt `draft`, `ready` und `error`.\n- Der Kapiteltext selbst wird in diesem Bereich nicht ausgegeben; `content_length` zeigt nur, ob ein Kapitel gefüllt ist.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/audiobooks/get_audiobook_chapters \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"audiobook_id\": 731\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/audiobooks/get_audiobook_chapters\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"audiobook_id\": 731,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/audiobooks/get_audiobook_chapters\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"audiobook_id\": 731\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"audiobook_id":{"type":"integer","description":"ID des Hörbuchs als ganze Zahl (Pflicht). Es muss zu deinem Unternehmen gehören, sonst kommt „Hörbuch nicht gefunden oder kein Zugriff“.","example":731}},"required":["audiobook_id"]},"example":{"audiobook_id":731}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"audiobooks","werkzeug":"get_audiobook_chapters","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":8201,"title":"Warum Abläufe unsichtbar werden","status":"draft","order":1,"content_length":5230,"audiobook_id":731},{"id":8202,"title":"Der erste Schritt","status":"draft","order":2,"content_length":0,"audiobook_id":731}],"count":2},"message":null,"timestamp":"2026-03-18 11:05:41","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/audiobooks/create_audiobook_chapter":{"post":{"tags":["Hörbücher"],"operationId":"audiobooks__create_audiobook_chapter","summary":"Hörbuch-Kapitel anlegen","description":"Legt ein neues Kapitel in einem Hörbuch an. Pflicht sind `audiobook_id` und `title`. Ohne `order` (oder bei `order` 0) wird das Kapitel ans Ende gestellt (Anzahl der vorhandenen Kapitel plus 1); bereits vorhandene Kapitel werden nie verschoben. Ohne `status` bekommt das Kapitel `draft`. `content` ist der Text, der später gesprochen werden soll. Die Antwort enthält das Kapitel (`id`, `title`, `status`, `order`, `content_length`, `audiobook_id`) mit der Meldung „Kapitel erstellt“. Mit `create_chapter_audio` bereitest du den Kapiteltext anschließend für die Sprachausgabe auf.\n\n**Gut zu wissen**\n\n- Die Antwort enthält den Kapiteltext nicht, nur dessen Länge in `content_length`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/audiobooks/create_audiobook_chapter \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"audiobook_id\": 731,\n    \"title\": \"Warum Abläufe unsichtbar werden\",\n    \"content\": \"Jeder Betrieb hat Routinen, die niemand mehr hinterfragt.\",\n    \"status\": \"draft\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/audiobooks/create_audiobook_chapter\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"audiobook_id\": 731,\n        \"title\": \"Warum Abläufe unsichtbar werden\",\n        \"content\": \"Jeder Betrieb hat Routinen, die niemand mehr hinterfragt.\",\n        \"status\": \"draft\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/audiobooks/create_audiobook_chapter\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"audiobook_id\": 731,\n      \"title\": \"Warum Abläufe unsichtbar werden\",\n      \"content\": \"Jeder Betrieb hat Routinen, die niemand mehr hinterfragt.\",\n      \"status\": \"draft\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"audiobook_id":{"type":"integer","description":"ID des Hörbuchs als ganze Zahl (Pflicht), zu dem das Kapitel gehört. Es muss zu deinem Unternehmen gehören.","example":731},"title":{"type":"string","description":"Titel des Kapitels als Text (Pflicht, nicht leer). Er dient auch als Grundlage für den internen Objektschlüssel.","example":"Warum Abläufe unsichtbar werden"},"content":{"type":"string","description":"Optionaler Kapiteltext als Text. Er ist die Grundlage für die spätere Sprachausgabe. Leere Werte werden ignoriert.","example":"Jeder Betrieb hat Routinen, die niemand mehr hinterfragt."},"order":{"type":"integer","description":"Optionale Position des Kapitels (ganze Zahl, größer als 0). Ohne Angabe oder bei 0 wird das Kapitel hinten angefügt."},"status":{"type":"string","description":"Optionaler Status als Text; üblich sind `draft`, `ready` und `error`. Ohne Angabe wird `draft` gesetzt.","example":"draft"}},"required":["audiobook_id","title"]},"example":{"audiobook_id":731,"title":"Warum Abläufe unsichtbar werden","content":"Jeder Betrieb hat Routinen, die niemand mehr hinterfragt.","status":"draft"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"audiobooks","werkzeug":"create_audiobook_chapter","stufe":"schreiben","data":{"success":true,"data":{"id":8201,"title":"Warum Abläufe unsichtbar werden","status":"draft","order":1,"content_length":56,"audiobook_id":731},"message":"Kapitel erstellt","timestamp":"2026-03-18 11:05:41","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/audiobooks/update_audiobook_chapter":{"post":{"tags":["Hörbücher"],"operationId":"audiobooks__update_audiobook_chapter","summary":"Hörbuch-Kapitel ändern","description":"Ändert Felder eines bestehenden Hörbuch-Kapitels. Alle zu ändernden Werte gehören in das Objekt `data`; nur dort genannte Felder werden angefasst. Wirksam sind `title`, `content`, `order`, `status` und `summary`, andere Schlüssel werden ohne Fehlermeldung ignoriert. Zurück kommt das Kapitel (`id`, `title`, `status`, `order`, `content_length`, `audiobook_id`) mit der Meldung „Kapitel aktualisiert“. Wird `content` geändert, ändert sich `content_length` entsprechend. Das Kapitel muss zu einem Hörbuch deines Unternehmens gehören. Die Kapitel-`id` findest du mit `get_audiobook_chapters`.\n\n**Gut zu wissen**\n\n- `title` und `status` werden vor dem Speichern HTML-maskiert (`&` wird zu `&amp;`); `content` und `summary` bleiben unverändert.\n- Die Reihenfolge ändert nur das jeweilige Kapitel; andere Kapitel werden nicht verschoben.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/audiobooks/update_audiobook_chapter \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"chapter_id\": 8201,\n    \"data\": {\n        \"content\": \"Neuer, überarbeiteter Kapiteltext zum Einsprechen.\",\n        \"status\": \"draft\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/audiobooks/update_audiobook_chapter\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"chapter_id\": 8201,\n        \"data\": {\n            \"content\": \"Neuer, überarbeiteter Kapiteltext zum Einsprechen.\",\n            \"status\": \"draft\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/audiobooks/update_audiobook_chapter\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"chapter_id\": 8201,\n      \"data\": {\n          \"content\": \"Neuer, überarbeiteter Kapiteltext zum Einsprechen.\",\n          \"status\": \"draft\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"chapter_id":{"type":"integer","description":"ID des Kapitels als ganze Zahl (Pflicht). Das zugehörige Hörbuch muss zu deinem Unternehmen gehören.","example":8201},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern (Pflicht). Wirksam sind `title`, `content` (Text), `order` (ganze Zahl), `status` (Text, z. B. `draft`, `ready`, `error`) und `summary` (Text). Nur enthaltene Felder werden geändert.","example":{"content":"Neuer, überarbeiteter Kapiteltext zum Einsprechen.","status":"draft"}}},"required":["chapter_id","data"]},"example":{"chapter_id":8201,"data":{"content":"Neuer, überarbeiteter Kapiteltext zum Einsprechen.","status":"draft"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"audiobooks","werkzeug":"update_audiobook_chapter","stufe":"schreiben","data":{"success":true,"data":{"id":8201,"title":"Warum Abläufe unsichtbar werden","status":"draft","order":1,"content_length":49,"audiobook_id":731},"message":"Kapitel aktualisiert","timestamp":"2026-03-18 11:05:41","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/audiobooks/delete_audiobook_chapter":{"post":{"tags":["Hörbücher"],"operationId":"audiobooks__delete_audiobook_chapter","summary":"Hörbuch-Kapitel löschen","description":"Löscht ein einzelnes Kapitel eines Hörbuchs unwiderruflich. Die Funktion erfordert `\"bestaetigt\": true` im Körper. Vorher wird geprüft, dass das zugehörige Hörbuch zu deinem Unternehmen gehört. Die Antwort enthält in `data.data` die `chapter_id` des gelöschten Kapitels mit der Meldung „Kapitel gelöscht“. Die übrigen Kapitel behalten ihre `order`; die Nummerierung kann dadurch Lücken haben, die du mit `update_audiobook_chapter` je Kapitel schließen kannst. Das Hörbuch selbst bleibt bestehen.\n\n**Gut zu wissen**\n\n- Das Löschen ist endgültig; es gibt keine Funktion zum Wiederherstellen.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/audiobooks/delete_audiobook_chapter \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"chapter_id\": 8202,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/audiobooks/delete_audiobook_chapter\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"chapter_id\": 8202,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/audiobooks/delete_audiobook_chapter\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"chapter_id\": 8202,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"chapter_id":{"type":"integer","description":"ID des zu löschenden Kapitels als ganze Zahl (Pflicht). Die ID stammt aus `get_audiobook_chapters` oder `create_audiobook_chapter`.","example":8202},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["chapter_id","bestaetigt"]},"example":{"chapter_id":8202,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"audiobooks","werkzeug":"delete_audiobook_chapter","stufe":"loeschen","data":{"success":true,"data":{"chapter_id":8202},"message":"Kapitel gelöscht","timestamp":"2026-03-18 11:05:41","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/audiobooks/create_chapter_audio":{"post":{"tags":["Hörbücher"],"operationId":"audiobooks__create_chapter_audio","summary":"Kapiteltext für Sprachausgabe aufbereiten","description":"Bereitet den Text eines einzelnen Kapitels für die Sprachausgabe auf. Eine KI-Textbearbeitung formt den vorhandenen Kapiteltext zu einer gesprochenen Fassung um, abgestimmt auf den angegebenen Limbic-Typ, und **überschreibt** `content` mit dem Ergebnis. Bei Erfolg steht das Kapitel auf `ready`, bei einem Fehler auf `error`. Hat das Kapitel keinen Text, wird es ohne Bearbeitung auf `ready` gesetzt. Zurück kommen `chapter_id` und der neue `status`; bei Erfolg lautet die Meldung „Kapitel-Audio generiert“, sonst „Audio-Generierung fehlgeschlagen“ (dann `success: false`). Die Funktion läuft synchron und kann einige Zeit dauern. Eine Audiodatei liefert die Antwort nicht.\n\n**Gut zu wissen**\n\n- Der ursprüngliche Kapiteltext wird ersetzt und lässt sich nicht wiederherstellen. Dieser Bereich bietet keine Funktion, den Text eines Kapitels auszulesen; sichere ihn bei Bedarf vorher in deinem eigenen System.\n- Sind danach alle Kapitel `ready`, setzt der Dienst das Hörbuch automatisch auf `ready`; steht mindestens ein Kapitel auf `error`, wird das Hörbuch auf `error` gesetzt.\n- Ein unbekannter `limbic_type` führt nicht zu einem Fehler, sondern fällt auf `performer` zurück.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/audiobooks/create_chapter_audio \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"chapter_id\": 8201,\n    \"limbic_type\": \"harmoniser\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/audiobooks/create_chapter_audio\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"chapter_id\": 8201,\n        \"limbic_type\": \"harmoniser\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/audiobooks/create_chapter_audio\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"chapter_id\": 8201,\n      \"limbic_type\": \"harmoniser\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"chapter_id":{"type":"integer","description":"ID des Kapitels als ganze Zahl (Pflicht). Das zugehörige Hörbuch muss zu deinem Unternehmen gehören.","example":8201},"limbic_type":{"type":"string","description":"Optionaler Persönlichkeitstyp, auf den der Text abgestimmt wird. Bekannt sind `traditionalist`, `performer`, `harmoniser`, `stimulant`, `hedonist`, `adventurer` und `disciplined`. Ohne Angabe gilt der am Hörbuch hinterlegte Typ, sonst `performer`; unbekannte Werte fallen auf `performer` zurück.","example":"harmoniser"},"linguistic_id":{"type":"integer","description":"Optionale ID eines linguistischen Profils. Im aktuellen Stand wird der Wert entgegengenommen, hat auf das Ergebnis aber keine Wirkung, weil beim Aufbereiten kein Profil angewendet wird."}},"required":["chapter_id"]},"example":{"chapter_id":8201,"limbic_type":"harmoniser"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"audiobooks","werkzeug":"create_chapter_audio","stufe":"schreiben","data":{"success":true,"data":{"chapter_id":8201,"status":"ready"},"message":"Kapitel-Audio generiert","timestamp":"2026-03-18 11:05:41","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/get_podcasts":{"post":{"tags":["Podcasts"],"operationId":"podcasts__get_podcasts","summary":"Podcasts auflisten","description":"Liefert die Podcasts deiner Firma, die neuesten (nach Erstellungsdatum) zuerst. Standardmäßig kommen 10 Einträge ab Position 0; mit `limit` und `offset` blätterst du durch längere Listen, mit `status` filterst du auf einen exakten Statuswert. Die Antwort enthält `items` (je Podcast `id`, `title`, `subtitle`, `status`, `language`, `frequency`, `company_id`, `episode_count`, `created_at`, `updated_at`) sowie `count` (Zahl der gelieferten Einträge), `limit` und `offset`. `count` ist nicht die Gesamtzahl aller Podcasts – setze `offset` weiter, bis eine Seite weniger Einträge als `limit` liefert. Für Beschreibung, Website, Kategorien und Autor rufst du anschließend `get_podcast` auf.\n\n**Gut zu wissen**\n\n- Der Statusfilter vergleicht exakt (z. B. `active`, `draft`, `archived`); ein leerer Wert filtert nicht.\n- `count` zählt nur die Einträge dieser Seite, nicht alle Treffer.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/get_podcasts \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"limit\": 5,\n    \"status\": \"active\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/get_podcasts\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"limit\": 5,\n        \"status\": \"active\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/get_podcasts\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"limit\": 5,\n      \"status\": \"active\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","description":"Maximale Anzahl der Podcasts in dieser Antwort als ganze Zahl. Standard ist 10. Eine Obergrenze wird nicht erzwungen; wähle für Seiteneinteilung eine feste Größe und erhöhe `offset` entsprechend.","example":5},"offset":{"type":"integer","description":"Anzahl der Podcasts, die am Anfang der Sortierung (neueste zuerst) übersprungen werden. Standard ist 0. Für die zweite Seite mit `limit` 10 setzt du 10."},"status":{"type":"string","description":"Optionaler Filter auf den Status des Podcasts, z. B. `active`, `draft` oder `archived`. Es wird exakt verglichen; nicht vorhandene Werte liefern eine leere Liste. Ohne Angabe werden alle Status geliefert.","example":"active"}}},"example":{"limit":5,"status":"active"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"get_podcasts","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":48210,"title":"Handwerk im Wandel","subtitle":"Gespräche über moderne Betriebe","status":"active","language":"de","frequency":"weekly","company_id":"99001","episode_count":14,"created_at":"2026-03-04T09:12:41+01:00","updated_at":"2026-10-28T16:05:10+01:00"}],"count":1,"limit":5,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:02","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/get_podcast":{"post":{"tags":["Podcasts"],"operationId":"podcasts__get_podcast","summary":"Podcast-Details abrufen","description":"Liefert einen einzelnen Podcast deiner Firma mit allen Detailfeldern. Neben den Feldern der Liste (`id`, `title`, `subtitle`, `status`, `language`, `frequency`, `company_id`, `episode_count`, `created_at`, `updated_at`) enthält die Antwort `description`, `website`, `primary_category`, `secondary_category` und `author`. Ist die ID unbekannt oder gehört der Podcast zu einer anderen Firma, kommt `success: false` mit der Meldung „Podcast nicht gefunden oder kein Zugriff“. Nutze die Funktion, bevor du mit `update_podcast` Felder änderst, um den aktuellen Stand zu kennen, oder um die Website für den RSS-Link (`get_rss_feed`) zu prüfen.\n\n**Gut zu wissen**\n\n- Podcasts anderer Firmen sind nicht sichtbar; die Antwort ist dann derselbe Fehler wie bei einer unbekannten ID.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/get_podcast \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"podcast_id\": 48210\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/get_podcast\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"podcast_id\": 48210,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/get_podcast\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"podcast_id\": 48210\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"podcast_id":{"type":"integer","description":"Numerische ID des Podcasts, wie sie `get_podcasts`, `search_podcasts` oder `create_podcast` als `id` zurückgeben. Pflichtangabe; der Podcast muss zu deiner Firma gehören.","example":48210}},"required":["podcast_id"]},"example":{"podcast_id":48210}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"get_podcast","stufe":"lesen","data":{"success":true,"data":{"id":48210,"title":"Handwerk im Wandel","subtitle":"Gespräche über moderne Betriebe","status":"active","language":"de","frequency":"weekly","company_id":"99001","episode_count":14,"created_at":"2026-03-04T09:12:41+01:00","updated_at":"2026-10-28T16:05:10+01:00","description":"Wöchentliche Interviews mit Inhabern kleiner Betriebe.","website":"https://podcast.example.com","primary_category":"Business","secondary_category":"Technology","author":"Muster GmbH"},"message":null,"timestamp":"2026-11-09 10:16:20","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/search_podcasts":{"post":{"tags":["Podcasts"],"operationId":"podcasts__search_podcasts","summary":"Podcasts suchen","description":"Durchsucht die Podcasts deiner Firma nach einem Begriff in Titel oder Beschreibung (Teilstring-Suche ohne Beachtung der Groß-/Kleinschreibung der Datenbank). Die Treffer sind alphabetisch nach Titel sortiert. Pro Aufruf kommen standardmäßig 10 Treffer; mit `limit` und `offset` steuerst du die Seite. Die Antwort enthält `items` im selben Kurzformat wie `get_podcasts` und `count` (Zahl der gelieferten Treffer dieser Seite). Eine Angabe `limit`/`offset` im Antwortobjekt gibt es hier nicht. Für Details eines Treffers nutzt du `get_podcast`; für Episoden `search_episodes`.\n\n**Gut zu wissen**\n\n- Der Suchbegriff wird vor der Suche HTML-maskiert; Zeichen wie `&` oder Anführungszeichen finden daher Texte, die im Original so gespeichert sind, nicht zuverlässig.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/search_podcasts \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\": \"Handwerk\",\n    \"limit\": 5\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/search_podcasts\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"query\": \"Handwerk\",\n        \"limit\": 5,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/search_podcasts\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"query\": \"Handwerk\",\n      \"limit\": 5\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Suchbegriff, der als Teilstring in Titel oder Beschreibung des Podcasts vorkommen muss. Pflichtangabe; leere Texte werden abgelehnt.","example":"Handwerk"},"limit":{"type":"integer","description":"Maximale Anzahl der Treffer als ganze Zahl. Standard ist 10. Ohne Obergrenze; für große Treffermengen in Seiten abrufen.","example":5},"offset":{"type":"integer","description":"Anzahl der Treffer, die am Anfang der alphabetischen Sortierung übersprungen werden. Standard ist 0."}},"required":["query"]},"example":{"query":"Handwerk","limit":5}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"search_podcasts","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":48210,"title":"Handwerk im Wandel","subtitle":"Gespräche über moderne Betriebe","status":"active","language":"de","frequency":"weekly","company_id":"99001","episode_count":14,"created_at":"2026-03-04T09:12:41+01:00","updated_at":"2026-10-28T16:05:10+01:00"}],"count":1},"message":null,"timestamp":"2026-11-09 10:17:44","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/create_podcast":{"post":{"tags":["Podcasts"],"operationId":"podcasts__create_podcast","summary":"Podcast anlegen","description":"Legt einen neuen Podcast für deine Firma an und veröffentlicht das Objekt im Redaktionssystem. Nur `title` ist Pflicht. Ohne Angabe sind Untertitel und Beschreibung leer, der Status ist `active`, die Sprache `de` und der Rhythmus `weekly`. Die Antwort enthält die Detailansicht des neuen Podcasts (`id`, `title`, `subtitle`, `status`, `language`, `frequency`, `episode_count` = 0, `description`, `website`, Kategorien, `author` usw.) und die Meldung „Podcast erstellt“. Merke dir die `id` für `create_episode`. Website, Kategorien und Autor setzt du anschließend mit `update_podcast`.\n\n**Gut zu wissen**\n\n- Der Titel wird beim Anlegen unverändert gespeichert; Sprache und Rhythmus lassen sich hier nicht wählen, sondern nur nachträglich mit `update_podcast` (`podcastlanguage`, `frequency`) ändern.\n- Es gibt keine Prüfung auf doppelte Titel – ein zweiter Aufruf legt einen zweiten Podcast an.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/create_podcast \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Handwerk im Wandel\",\n    \"subtitle\": \"Gespräche über moderne Betriebe\",\n    \"status\": \"draft\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/create_podcast\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"title\": \"Handwerk im Wandel\",\n        \"subtitle\": \"Gespräche über moderne Betriebe\",\n        \"status\": \"draft\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/create_podcast\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"title\": \"Handwerk im Wandel\",\n      \"subtitle\": \"Gespräche über moderne Betriebe\",\n      \"status\": \"draft\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Titel des Podcasts als Text. Pflichtangabe; er wird auch zur Bildung der internen Objektbezeichnung verwendet.","example":"Handwerk im Wandel"},"subtitle":{"type":"string","description":"Optionaler Untertitel (Kurzbeschreibung) des Podcasts. Ohne Angabe bleibt das Feld leer.","example":"Gespräche über moderne Betriebe"},"description":{"type":"string","description":"Optionale ausführliche Beschreibung der Sendung, wie sie z. B. im Feed erscheint. Ohne Angabe bleibt das Feld leer."},"status":{"type":"string","description":"Optionaler Status des Podcasts. Dokumentiert sind `active`, `draft` und `archived`; Standard ist `active`. Der Wert wird nicht gegen eine Liste geprüft.","example":"draft"}},"required":["title"]},"example":{"title":"Handwerk im Wandel","subtitle":"Gespräche über moderne Betriebe","status":"draft"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"create_podcast","stufe":"schreiben","data":{"success":true,"data":{"id":48215,"title":"Handwerk im Wandel","subtitle":"Gespräche über moderne Betriebe","status":"draft","language":"de","frequency":"weekly","company_id":"99001","episode_count":0,"created_at":"2026-11-09T10:20:00+01:00","updated_at":"2026-11-09T10:20:00+01:00","description":"","website":null,"primary_category":null,"secondary_category":null,"author":null},"message":"Podcast erstellt","timestamp":"2026-11-09 10:20:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/update_podcast":{"post":{"tags":["Podcasts"],"operationId":"podcasts__update_podcast","summary":"Podcast aktualisieren","description":"Ändert einzelne Felder eines Podcasts. Du übergibst in `data` nur die Felder, die sich ändern sollen; alle anderen bleiben unverändert. Unterstützt werden `title`, `subtitle`, `description`, `status`, `podcastlanguage`, `frequency`, `website`, `primary_category` und `secondary_category`. Unbekannte Schlüssel in `data` werden stillschweigend ignoriert, ohne Fehlermeldung. Zurück kommt die Detailansicht des gespeicherten Podcasts mit der Meldung „Podcast aktualisiert“ – prüfe dort, ob deine Änderung übernommen wurde.\n\n**Gut zu wissen**\n\n- Texte (außer `description`) werden vor dem Speichern getrimmt und HTML-maskiert; `&` wird als `&amp;` gespeichert.\n- Übergib Werte als Zeichenketten; andere Typen führen zu einem Fehler.\n- Tippfehler im Feldnamen bleiben ohne Wirkung – vergleiche die Antwort mit deiner Absicht.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/update_podcast \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"podcast_id\": 48210,\n    \"data\": {\n        \"website\": \"https://podcast.example.com\",\n        \"frequency\": \"biweekly\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/update_podcast\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"podcast_id\": 48210,\n        \"data\": {\n            \"website\": \"https://podcast.example.com\",\n            \"frequency\": \"biweekly\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/update_podcast\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"podcast_id\": 48210,\n      \"data\": {\n          \"website\": \"https://podcast.example.com\",\n          \"frequency\": \"biweekly\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"podcast_id":{"type":"integer","description":"Numerische ID des zu ändernden Podcasts. Pflichtangabe; der Podcast muss zu deiner Firma gehören.","example":48210},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern. Erlaubte Schlüssel: `title`, `subtitle`, `description`, `status`, `podcastlanguage`, `frequency`, `website`, `primary_category`, `secondary_category`. Werte sind Zeichenketten, z. B. {\"status\": \"archived\"}. Nicht aufgeführte Schlüssel haben keine Wirkung.","example":{"website":"https://podcast.example.com","frequency":"biweekly"}}},"required":["podcast_id","data"]},"example":{"podcast_id":48210,"data":{"website":"https://podcast.example.com","frequency":"biweekly"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"update_podcast","stufe":"schreiben","data":{"success":true,"data":{"id":48210,"title":"Handwerk im Wandel","subtitle":"Gespräche über moderne Betriebe","status":"active","language":"de","frequency":"biweekly","company_id":"99001","episode_count":14,"created_at":"2026-03-04T09:12:41+01:00","updated_at":"2026-11-09T10:22:15+01:00","description":"Wöchentliche Interviews mit Inhabern kleiner Betriebe.","website":"https://podcast.example.com","primary_category":"Business","secondary_category":"Technology","author":"Muster GmbH"},"message":"Podcast aktualisiert","timestamp":"2026-11-09 10:22:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/delete_podcast":{"post":{"tags":["Podcasts"],"operationId":"podcasts__delete_podcast","summary":"Podcast löschen","description":"Löscht einen Podcast deiner Firma dauerhaft. Der Aufruf verlangt `\"bestaetigt\": true` im Körper. Bei Erfolg enthält `data` nur die gelöschte `podcast_id`, die Meldung lautet „Podcast gelöscht“. Die Episoden liegen in einem eigenen Ordner und werden vom Löschvorgang des Podcasts nicht ausdrücklich mitgelöscht; lösche sie bei Bedarf vorher einzeln mit `delete_episode`. Willst du den Podcast nur ausblenden, setze stattdessen mit `update_podcast` den Status `archived`. Eine Wiederherstellung über die Plattform-API gibt es nicht.\n\n**Gut zu wissen**\n\n- Die Aktion ist nicht umkehrbar; prüfe die ID vorher mit `get_podcast`.\n- Löschen verlangt in der Plattform-API `\"bestaetigt\": true`.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/delete_podcast \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"podcast_id\": 48215,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/delete_podcast\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"podcast_id\": 48215,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/delete_podcast\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"podcast_id\": 48215,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"podcast_id":{"type":"integer","description":"Numerische ID des zu löschenden Podcasts. Pflichtangabe; der Podcast muss zu deiner Firma gehören, sonst kommt „Podcast nicht gefunden oder kein Zugriff“.","example":48215},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["podcast_id","bestaetigt"]},"example":{"podcast_id":48215,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"delete_podcast","stufe":"loeschen","data":{"success":true,"data":{"podcast_id":48215},"message":"Podcast gelöscht","timestamp":"2026-11-09 10:25:31","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/copy_podcast":{"post":{"tags":["Podcasts"],"operationId":"podcasts__copy_podcast","summary":"Podcast kopieren","description":"Erzeugt aus einem bestehenden Podcast eine Kopie. Übernommen werden Untertitel, Beschreibung, Sprache, Rhythmus, beide Kategorien und der Autor; der neue Podcast erhält immer den Status `draft`. Website, Cover und Feed-Kennung werden nicht kopiert. Ohne `title` heißt die Kopie „<Originaltitel> (Kopie)“. Mit `include_episodes: true` werden auch alle Episoden des Originals dupliziert (Titel, Beschreibung, Zusammenfassung, Shownotes), neu durchnummeriert und mit Fortschrittsstatus `draft`; Audiodateien, Transkripte und Veröffentlichungsdaten werden nicht übernommen. Zurück kommt die Detailansicht des neuen Podcasts mit der Meldung „Podcast kopiert“.\n\n**Gut zu wissen**\n\n- Die kopierten Episoden bekommen neue Nummern ab 1 in der Reihenfolge, in der sie gelesen werden – nicht zwingend in der Originalreihenfolge.\n- Der angegebene Titel wird HTML-maskiert gespeichert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/copy_podcast \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"podcast_id\": 48210,\n    \"title\": \"Handwerk im Wandel – Staffel 2\",\n    \"include_episodes\": false\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/copy_podcast\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"podcast_id\": 48210,\n        \"title\": \"Handwerk im Wandel – Staffel 2\",\n        \"include_episodes\": False,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/copy_podcast\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"podcast_id\": 48210,\n      \"title\": \"Handwerk im Wandel – Staffel 2\",\n      \"include_episodes\": false\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"podcast_id":{"type":"integer","description":"Numerische ID des Podcasts, der kopiert werden soll. Pflichtangabe; er muss zu deiner Firma gehören.","example":48210},"title":{"type":"string","description":"Optionaler Titel der Kopie. Ohne Angabe wird „<Originaltitel> (Kopie)“ verwendet.","example":"Handwerk im Wandel – Staffel 2"},"include_episodes":{"type":"boolean","description":"Wahrheitswert. Bei `true` werden auch die Episodentexte des Originals kopiert, bei `false` (Standard) entsteht ein Podcast ohne Episoden.","default":false,"example":false}},"required":["podcast_id"]},"example":{"podcast_id":48210,"title":"Handwerk im Wandel – Staffel 2","include_episodes":false}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"copy_podcast","stufe":"schreiben","data":{"success":true,"data":{"id":48222,"title":"Handwerk im Wandel – Staffel 2","subtitle":"Gespräche über moderne Betriebe","status":"draft","language":"de","frequency":"weekly","company_id":"99001","episode_count":0,"created_at":"2026-11-09T10:30:12+01:00","updated_at":"2026-11-09T10:30:12+01:00","description":"Wöchentliche Interviews mit Inhabern kleiner Betriebe.","website":null,"primary_category":"Business","secondary_category":"Technology","author":"Muster GmbH"},"message":"Podcast kopiert","timestamp":"2026-11-09 10:30:12","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/get_episodes":{"post":{"tags":["Podcasts"],"operationId":"podcasts__get_episodes","summary":"Episoden eines Podcasts","description":"Liefert alle Episoden eines Podcasts deiner Firma, aufsteigend nach Folgennummer sortiert. Es gibt keine Seiteneinteilung – die Liste enthält sämtliche Episoden. Jeder Eintrag in `items` hat `id`, `title`, `number`, `status` (der Fortschrittsstatus der Episode, z. B. `draft`, `ready`, `scheduled`, `published`), `podcast_id`, `publishing_date` (ISO-8601 oder null) und `duration` (Dauer, wie in der Episode hinterlegt). `count` nennt die Zahl der Einträge. Beschreibung, Zusammenfassung, Shownotes und Transkript sind in der Liste nicht enthalten; dafür gibt es `get_episode`.\n\n**Gut zu wissen**\n\n- Für firmenweite Suche über mehrere Podcasts hinweg nutze `search_episodes`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/get_episodes \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"podcast_id\": 48210\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/get_episodes\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"podcast_id\": 48210,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/get_episodes\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"podcast_id\": 48210\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"podcast_id":{"type":"integer","description":"Numerische ID des Podcasts, dessen Episoden geliefert werden. Pflichtangabe; der Podcast muss zu deiner Firma gehören.","example":48210}},"required":["podcast_id"]},"example":{"podcast_id":48210}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"get_episodes","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":51003,"title":"Warum Digitalisierung im Betrieb scheitert","number":1,"status":"published","podcast_id":48210,"publishing_date":"2026-09-01T06:00:00+02:00","duration":1860},{"id":51010,"title":"Nachfolge im Familienbetrieb","number":2,"status":"scheduled","podcast_id":48210,"publishing_date":"2026-11-20T06:00:00+01:00","duration":null}],"count":2},"message":null,"timestamp":"2026-11-09 10:33:40","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/get_episode":{"post":{"tags":["Podcasts"],"operationId":"podcasts__get_episode","summary":"Episode-Details abrufen","description":"Liefert eine einzelne Episode mit allen Detailfeldern. Zusätzlich zu `id`, `title`, `number`, `status`, `podcast_id`, `publishing_date` und `duration` enthält die Antwort `description`, `summary`, `shownotes` und `transcript` (Volltext der Transkription, sofern vorhanden). Die Episode muss zu einem Podcast deiner Firma gehören; sonst kommt `success: false` („Episode nicht gefunden“ bzw. „Podcast nicht gefunden oder kein Zugriff“). Bei Episoden mit langem Transkript kann die Antwort entsprechend groß ausfallen. Zum Ändern der Felder nutzt du `update_episode`.\n\n**Gut zu wissen**\n\n- `status` entspricht dem Fortschrittsstatus der Episode, nicht dem Status des Podcasts.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/get_episode \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"episode_id\": 51010\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/get_episode\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"episode_id\": 51010,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/get_episode\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"episode_id\": 51010\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"episode_id":{"type":"integer","description":"Numerische ID der Episode, wie sie `get_episodes`, `search_episodes` oder `create_episode` als `id` liefern. Pflichtangabe.","example":51010}},"required":["episode_id"]},"example":{"episode_id":51010}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"get_episode","stufe":"lesen","data":{"success":true,"data":{"id":51010,"title":"Nachfolge im Familienbetrieb","number":2,"status":"scheduled","podcast_id":48210,"publishing_date":"2026-11-20T06:00:00+01:00","duration":null,"description":"Wie gelingt die Übergabe an die nächste Generation?","summary":"Ein Gespräch über Übergabe, Finanzierung und Erwartungen.","shownotes":"<ul><li>Checkliste Nachfolge</li></ul>","transcript":null},"message":null,"timestamp":"2026-11-09 10:35:02","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/search_episodes":{"post":{"tags":["Podcasts"],"operationId":"podcasts__search_episodes","summary":"Episoden suchen","description":"Durchsucht alle Episoden deiner Firma – über alle Podcasts hinweg – nach einem Begriff in Titel oder Beschreibung. Die Treffer sind nach Veröffentlichungsdatum absteigend sortiert (neueste zuerst). Standardmäßig kommen 10 Treffer; mit `limit` und `offset` blätterst du. Die Einträge in `items` haben das Kurzformat der Episodenliste (`id`, `title`, `number`, `status`, `podcast_id`, `publishing_date`, `duration`), `count` ist die Zahl der Treffer dieser Seite. Mehr Details zu einem Treffer liefert `get_episode`. Schlagwörter aus Shownotes oder Transkript werden nicht durchsucht.\n\n**Gut zu wissen**\n\n- Gesucht wird nur in Titel und Beschreibung, nicht in Shownotes, Zusammenfassung oder Transkript.\n- Der Suchbegriff wird HTML-maskiert, Sonderzeichen wie `&` können daher Treffer verfehlen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/search_episodes \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\": \"Nachfolge\",\n    \"limit\": 3\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/search_episodes\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"query\": \"Nachfolge\",\n        \"limit\": 3,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/search_episodes\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"query\": \"Nachfolge\",\n      \"limit\": 3\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Suchbegriff, der als Teilstring in Titel oder Beschreibung der Episode vorkommen muss. Pflichtangabe; leere Texte werden abgelehnt.","example":"Nachfolge"},"limit":{"type":"integer","description":"Maximale Anzahl der Treffer als ganze Zahl. Standard ist 10; es gibt keine feste Obergrenze.","example":3},"offset":{"type":"integer","description":"Anzahl der Treffer, die am Anfang der Sortierung (neueste zuerst) übersprungen werden. Standard ist 0."}},"required":["query"]},"example":{"query":"Nachfolge","limit":3}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"search_episodes","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":51010,"title":"Nachfolge im Familienbetrieb","number":2,"status":"scheduled","podcast_id":48210,"publishing_date":"2026-11-20T06:00:00+01:00","duration":null}],"count":1},"message":null,"timestamp":"2026-11-09 10:36:18","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/create_episode":{"post":{"tags":["Podcasts"],"operationId":"podcasts__create_episode","summary":"Episode anlegen","description":"Legt unter einem bestehenden Podcast eine neue Episode an. Pflicht sind `podcast_id` und `title`. Ohne `number` vergibt die Funktion die nächste Nummer (Zahl der vorhandenen Episoden plus 1); die Beschreibung ist ohne Angabe leer. Die Episode startet mit dem Fortschrittsstatus `draft`. Zurück kommt die Detailansicht der neuen Episode (inklusive leerer Felder `summary`, `shownotes`, `transcript`) mit der Meldung „Episode erstellt“. Nach dem Anlegen wird zusätzlich eine Benachrichtigung („Podcast bereit“) an das Mitglied der Firma ausgelöst; scheitert diese, bleibt die Episode trotzdem angelegt. Audio, Shownotes und Status pflegst du mit `update_episode`.\n\n**Gut zu wissen**\n\n- Die automatische Nummer richtet sich nach der Anzahl vorhandener Episoden; bei Lücken in der Nummerierung kann sie doppelt vergeben werden – gib `number` dann selbst an.\n- Beim Anlegen wird eine Benachrichtigung an das Firmenmitglied ausgelöst.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/create_episode \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"podcast_id\": 48210,\n    \"title\": \"Nachfolge im Familienbetrieb\",\n    \"number\": 2\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/create_episode\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"podcast_id\": 48210,\n        \"title\": \"Nachfolge im Familienbetrieb\",\n        \"number\": 2,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/create_episode\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"podcast_id\": 48210,\n      \"title\": \"Nachfolge im Familienbetrieb\",\n      \"number\": 2\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"podcast_id":{"type":"integer","description":"Numerische ID des Podcasts, zu dem die Episode gehört. Pflichtangabe; der Podcast muss zu deiner Firma gehören.","example":48210},"title":{"type":"string","description":"Titel der Episode. Pflichtangabe; er wird auch zur Bildung der internen Objektbezeichnung verwendet.","example":"Nachfolge im Familienbetrieb"},"number":{"type":"integer","description":"Optionale Folgennummer als ganze Zahl. Ohne Angabe wird die Zahl der vorhandenen Episoden des Podcasts plus 1 verwendet.","example":2},"description":{"type":"string","description":"Optionale Beschreibung der Episode. Ohne Angabe bleibt das Feld leer."}},"required":["podcast_id","title"]},"example":{"podcast_id":48210,"title":"Nachfolge im Familienbetrieb","number":2}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"create_episode","stufe":"schreiben","data":{"success":true,"data":{"id":51010,"title":"Nachfolge im Familienbetrieb","number":2,"status":"draft","podcast_id":48210,"publishing_date":null,"duration":null,"description":"","summary":null,"shownotes":null,"transcript":null},"message":"Episode erstellt","timestamp":"2026-11-09 10:40:09","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/update_episode":{"post":{"tags":["Podcasts"],"operationId":"podcasts__update_episode","summary":"Episode aktualisieren","description":"Ändert einzelne Felder einer Episode. In `data` übergibst du nur, was sich ändern soll. Unterstützt werden `title`, `description`, `summary`, `shownotes`, `number`, `status` und `publishing_date`. `status` setzt den Fortschrittsstatus der Episode (gebräuchlich sind `draft`, `ready`, `scheduled`, `published`); es findet keine Prüfung gegen eine feste Liste statt. `publishing_date` akzeptiert gängige Datumsformate (z. B. ISO-8601); ein leerer Wert lässt das Datum unverändert. Unbekannte Schlüssel werden ohne Fehlermeldung ignoriert. Zurück kommt die Detailansicht mit der Meldung „Episode aktualisiert“. Für das Einplanen mit Status-Wechsel in einem Schritt gibt es `schedule_episode`.\n\n**Gut zu wissen**\n\n- `status` ändert nur den Fortschrittsstatus, es wird nichts veröffentlicht oder ausgespielt.\n- Titel und Status werden HTML-maskiert gespeichert; Beschreibung, Zusammenfassung und Shownotes unverändert.\n- Ein ungültiges Datum in `publishing_date` führt zu einem Fehler; nichts wird gespeichert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/update_episode \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"episode_id\": 51010,\n    \"data\": {\n        \"summary\": \"Ein Gespräch über Übergabe, Finanzierung und Erwartungen.\",\n        \"status\": \"ready\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/update_episode\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"episode_id\": 51010,\n        \"data\": {\n            \"summary\": \"Ein Gespräch über Übergabe, Finanzierung und Erwartungen.\",\n            \"status\": \"ready\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/update_episode\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"episode_id\": 51010,\n      \"data\": {\n          \"summary\": \"Ein Gespräch über Übergabe, Finanzierung und Erwartungen.\",\n          \"status\": \"ready\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"episode_id":{"type":"integer","description":"Numerische ID der zu ändernden Episode. Pflichtangabe; die Episode muss zu einem Podcast deiner Firma gehören.","example":51010},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern. Erlaubte Schlüssel: `title`, `description`, `summary`, `shownotes` (Texte), `number` (ganze Zahl), `status` (Fortschrittsstatus als Text) und `publishing_date` (Datum/Zeit, z. B. `2026-11-20T06:00:00+01:00`). Andere Schlüssel haben keine Wirkung.","example":{"summary":"Ein Gespräch über Übergabe, Finanzierung und Erwartungen.","status":"ready"}}},"required":["episode_id","data"]},"example":{"episode_id":51010,"data":{"summary":"Ein Gespräch über Übergabe, Finanzierung und Erwartungen.","status":"ready"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"update_episode","stufe":"schreiben","data":{"success":true,"data":{"id":51010,"title":"Nachfolge im Familienbetrieb","number":2,"status":"ready","podcast_id":48210,"publishing_date":null,"duration":null,"description":"Wie gelingt die Übergabe an die nächste Generation?","summary":"Ein Gespräch über Übergabe, Finanzierung und Erwartungen.","shownotes":null,"transcript":null},"message":"Episode aktualisiert","timestamp":"2026-11-09 10:42:50","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/delete_episode":{"post":{"tags":["Podcasts"],"operationId":"podcasts__delete_episode","summary":"Episode löschen","description":"Löscht eine einzelne Episode endgültig. Der Aufruf verlangt `\"bestaetigt\": true`. Die Episode muss zu einem Podcast deiner Firma gehören. Bei Erfolg enthält `data` nur die gelöschte `episode_id`, die Meldung lautet „Episode gelöscht“. Der zugehörige Podcast und andere Episoden bleiben unverändert; die Folgennummern der übrigen Episoden werden nicht neu vergeben. Wenn du eine Episode lediglich zurückhalten willst, setze mit `update_episode` den Status auf `draft`. Eine Wiederherstellung über die Plattform-API ist nicht möglich.\n\n**Gut zu wissen**\n\n- Nicht umkehrbar – kontrolliere die ID vorher mit `get_episode`.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/delete_episode \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"episode_id\": 51010,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/delete_episode\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"episode_id\": 51010,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/delete_episode\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"episode_id\": 51010,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"episode_id":{"type":"integer","description":"Numerische ID der zu löschenden Episode. Pflichtangabe; unbekannte IDs ergeben die Fehlermeldung „Episode nicht gefunden“.","example":51010},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["episode_id","bestaetigt"]},"example":{"episode_id":51010,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"delete_episode","stufe":"loeschen","data":{"success":true,"data":{"episode_id":51010},"message":"Episode gelöscht","timestamp":"2026-11-09 10:45:12","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/get_episode_stats":{"post":{"tags":["Podcasts"],"operationId":"podcasts__get_episode_stats","summary":"Episoden-Kennzahlen","description":"Berechnet Kennzahlen über alle Episoden eines Podcasts. Die Antwort enthält `total_episodes` (Gesamtzahl), `status_counts` mit den Zählern für `draft`, `ready` und `published`, `total_duration_seconds` (Summe der hinterlegten Dauern) und `average_duration_seconds` (Summe geteilt durch die Gesamtzahl der Episoden, auf zwei Stellen gerundet; 0 bei leerem Podcast). Episoden in anderen Status, z. B. `scheduled`, zählen zu `total_episodes`, tauchen aber in `status_counts` nicht auf. Episoden ohne hinterlegte Dauer gehen mit 0 in die Summe ein und senken dadurch den Durchschnitt.\n\n**Gut zu wissen**\n\n- `status_counts` kennt nur `draft`, `ready` und `published`; die Summe kann kleiner als `total_episodes` sein.\n- Der Durchschnitt wird über alle Episoden gebildet, auch über solche ohne Dauer.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/get_episode_stats \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"podcast_id\": 48210\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/get_episode_stats\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"podcast_id\": 48210,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/get_episode_stats\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"podcast_id\": 48210\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"podcast_id":{"type":"integer","description":"Numerische ID des Podcasts, für den die Kennzahlen berechnet werden. Pflichtangabe; der Podcast muss zu deiner Firma gehören.","example":48210}},"required":["podcast_id"]},"example":{"podcast_id":48210}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"get_episode_stats","stufe":"lesen","data":{"success":true,"data":{"total_episodes":14,"status_counts":{"draft":2,"ready":1,"published":10},"average_duration_seconds":1432.5,"total_duration_seconds":20055},"message":null,"timestamp":"2026-11-09 10:47:33","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/get_rss_feed":{"post":{"tags":["Podcasts"],"operationId":"podcasts__get_rss_feed","summary":"RSS-Feed-Link abrufen","description":"Gibt den Link zum RSS-Feed eines Podcasts zurück. Der Pfad lautet `/podcast/feed/<Kennung>/podcastfeed.rss`, wobei die Kennung dem hinterlegten Slug des Podcasts entspricht, ersatzweise seiner internen Objektbezeichnung. Ist beim Podcast eine Website eingetragen, wird sie (ohne abschließenden Schrägstrich) vorangestellt und `feed_url` ist eine vollständige Adresse; ohne Website kommt nur der relative Pfad zurück. Die Funktion prüft nicht, ob der Feed erreichbar ist oder Episoden enthält. Trage die Website vorher mit `update_podcast` ein, wenn du eine vollständige URL für Verzeichnisse benötigst.\n\n**Gut zu wissen**\n\n- Ohne gepflegte Website ist `feed_url` nur ein Pfad ohne Host.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/get_rss_feed \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"podcast_id\": 48210\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/get_rss_feed\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"podcast_id\": 48210,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/get_rss_feed\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"podcast_id\": 48210\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"podcast_id":{"type":"integer","description":"Numerische ID des Podcasts, dessen Feed-Link ermittelt wird. Pflichtangabe; der Podcast muss zu deiner Firma gehören.","example":48210}},"required":["podcast_id"]},"example":{"podcast_id":48210}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"get_rss_feed","stufe":"lesen","data":{"success":true,"data":{"feed_url":"https://podcast.example.com/podcast/feed/handwerk-im-wandel/podcastfeed.rss"},"message":"RSS-Feed-Link bereitgestellt","timestamp":"2026-11-09 10:49:05","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcasts/schedule_episode":{"post":{"tags":["Podcasts"],"operationId":"podcasts__schedule_episode","summary":"Episode einplanen","description":"Setzt das Veröffentlichungsdatum einer Episode und stellt ihren Fortschrittsstatus auf `scheduled`. `publish_at` wird als Datum mit Uhrzeit interpretiert; ISO-8601 mit Zeitzone (z. B. `2026-11-20T06:00:00+01:00`) ist das sicherste Format. Zurück kommen `episode_id` und das gespeicherte `publishing_date` im ISO-8601-Format mit der Meldung „Episode geplant“. Die Funktion prüft nicht, ob der Zeitpunkt in der Zukunft liegt, und veröffentlicht nicht selbst – sie setzt nur Datum und Status. Das Datum kannst du jederzeit durch einen erneuten Aufruf überschreiben oder mit `update_episode` ändern.\n\n**Gut zu wissen**\n\n- Ein nicht lesbares Datum ergibt `success: false`; die Episode bleibt dann unverändert.\n- Ein Zeitpunkt in der Vergangenheit wird ohne Warnung übernommen.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcasts/schedule_episode \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"episode_id\": 51010,\n    \"publish_at\": \"2026-11-20T06:00:00+01:00\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcasts/schedule_episode\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"episode_id\": 51010,\n        \"publish_at\": \"2026-11-20T06:00:00+01:00\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcasts/schedule_episode\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"episode_id\": 51010,\n      \"publish_at\": \"2026-11-20T06:00:00+01:00\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"episode_id":{"type":"integer","description":"Numerische ID der Episode, die eingeplant werden soll. Pflichtangabe; sie muss zu einem Podcast deiner Firma gehören.","example":51010},"publish_at":{"type":"string","description":"Gewünschter Veröffentlichungszeitpunkt als Text, bevorzugt ISO-8601 mit Zeitzone, z. B. `2026-11-20T06:00:00+01:00`. Pflichtangabe. Ohne Zeitzone wird die Server-Zeitzone angenommen.","example":"2026-11-20T06:00:00+01:00"}},"required":["episode_id","publish_at"]},"example":{"episode_id":51010,"publish_at":"2026-11-20T06:00:00+01:00"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcasts","werkzeug":"schedule_episode","stufe":"schreiben","data":{"success":true,"data":{"episode_id":51010,"publishing_date":"2026-11-20T06:00:00+01:00"},"message":"Episode geplant","timestamp":"2026-11-09 10:51:27","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcast_guests/get_podcast_gaeste":{"post":{"tags":["Podcast-Gäste"],"operationId":"podcast_guests__get_podcast_gaeste","summary":"Podcast-Gäste auflisten","description":"Hat zwei Modi. **Ohne `podcast_id`** liefert die Funktion `podcasts`: alle veröffentlichten Podcasts deiner Firma (alphabetisch nach Titel) mit `id`, `title`, `description` (als Klartext), `cover_url` und `gaeste` (Zahl der Gäste ohne abgesagte). Die Meldung weist darauf hin, für Details eine `podcast_id` anzugeben; `status` wird in diesem Modus ignoriert. **Mit `podcast_id`** kommen die Gäste dieses Podcasts: `anzahl`, `gaeste` und `je_phase` (Anzahl je Phase). Ohne `status` sind alle Phasen enthalten, geordnet nach Phase und darin nach Termin bzw. Einladungsdatum absteigend; mit `status` nur die gewählte Phase. Jeder Gast hat `id`, `vorname`, `nachname`, `firma`, `phase`, `status`, `eingeladen_at`, `beginn`, `standort`, `gastgeber`, `interview_id` und `episode_id`.\n\n**Gut zu wissen**\n\n- `phase` ist die Ablaufphase (`eingeladen`, `vorbereitung`, `aufnahme`, `nachbereitung`, `abgesagt`), `status` der interne Buchungsstatus (z. B. `eingeladen`, `gebucht`, `aufgenommen`, `veroeffentlicht`).\n- Die Phase `aufnahme` gilt ab dem Kalendertag des Termins, nicht erst ab der Uhrzeit.\n- `je_phase` zählt immer alle Phasen, auch wenn du mit `status` filterst.\n- `standort`, `gastgeber` und `beginn` sind null, solange der Gast keinen Termin gebucht hat.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcast_guests/get_podcast_gaeste \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"podcast_id\": 48210,\n    \"status\": \"vorbereitung\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcast_guests/get_podcast_gaeste\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"podcast_id\": 48210,\n        \"status\": \"vorbereitung\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcast_guests/get_podcast_gaeste\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"podcast_id\": 48210,\n      \"status\": \"vorbereitung\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"podcast_id":{"type":"integer","description":"Numerische ID des Podcasts. Ohne Angabe liefert die Funktion stattdessen die Liste aller Podcasts der Firma mit Gästezahl. Ein unbekannter oder fremder Podcast ergibt den Fehler „Podcast nicht gefunden.“","example":48210},"status":{"type":"string","enum":["eingeladen","vorbereitung","aufnahme","nachbereitung","abgesagt"],"description":"Optionaler Filter auf eine Phase: `eingeladen`, `vorbereitung`, `aufnahme`, `nachbereitung` oder `abgesagt`. Wirkt nur zusammen mit `podcast_id`; ohne Filter werden alle Phasen geliefert.","example":"vorbereitung"}}},"example":{"podcast_id":48210,"status":"vorbereitung"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcast_guests","werkzeug":"get_podcast_gaeste","stufe":"lesen","data":{"success":true,"data":{"anzahl":1,"gaeste":[{"id":312,"vorname":"Mira","nachname":"Beispielmann","firma":"Muster Metallbau GmbH","phase":"vorbereitung","status":"gebucht","eingeladen_at":"2026-10-20T11:30:00+02:00","beginn":"2026-11-18T14:00:00+01:00","standort":"Studio Mitte","gastgeber":"Jonas Beispiel","interview_id":7745,"episode_id":null}],"je_phase":{"eingeladen":2,"vorbereitung":1,"aufnahme":0,"nachbereitung":5,"abgesagt":1}},"message":null,"timestamp":"2026-11-09 11:02:14","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcast_guests/get_podcast_aufnahmetage":{"post":{"tags":["Podcast-Gäste"],"operationId":"podcast_guests__get_podcast_aufnahmetage","summary":"Kommende Aufnahmetage","description":"Liefert die Aufnahmetage eines Podcasts, die heute oder später stattfinden, nach Datum und Beginn sortiert. Ein Aufnahmetag ist ein Zeitfenster an einem Standort mit einem Gastgeber, das in Slots fester Länge zerfällt. Die Antwort enthält `anzahl` und `aufnahmetage`; jeder Eintrag hat `id`, `datum` (JJJJ-MM-TT), `von` und `bis` (HH:MM), `slot_minuten`, `standort`, `adresse`, `gastgeber`, `gebucht` (Zahl der bereits gebuchten oder abgeschlossenen Gäste) und `frei` (Zahl der noch buchbaren Slots). Ein Slot ist frei, wenn er in der Zukunft liegt und weder durch eine Gastbuchung noch durch einen Kalendertermin des Gastgebers belegt ist. Tage mit `frei: 0` werden ebenfalls aufgeführt.\n\n**Gut zu wissen**\n\n- Vergangene Aufnahmetage werden nicht geliefert.\n- Die einzelnen Slots werden nicht aufgelistet, nur ihre Anzahl (`frei`).\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcast_guests/get_podcast_aufnahmetage \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"podcast_id\": 48210\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcast_guests/get_podcast_aufnahmetage\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"podcast_id\": 48210,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcast_guests/get_podcast_aufnahmetage\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"podcast_id\": 48210\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"podcast_id":{"type":"integer","description":"Numerische ID des Podcasts, dessen Aufnahmetage geliefert werden. Pflichtangabe; der Podcast muss zu deiner Firma gehören, sonst kommt „Podcast nicht gefunden.“","example":48210}},"required":["podcast_id"]},"example":{"podcast_id":48210}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcast_guests","werkzeug":"get_podcast_aufnahmetage","stufe":"lesen","data":{"success":true,"data":{"anzahl":1,"aufnahmetage":[{"id":58,"datum":"2026-11-18","von":"10:00","bis":"16:00","slot_minuten":60,"standort":"Studio Mitte","adresse":"Beispielstraße 12, 40210 Düsseldorf","gastgeber":"Jonas Beispiel","frei":4,"gebucht":2}]},"message":null,"timestamp":"2026-11-09 11:04:40","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/podcast_guests/find_podcast_gast_kandidaten":{"post":{"tags":["Podcast-Gäste"],"operationId":"podcast_guests__find_podcast_gast_kandidaten","summary":"Gast-Kandidaten suchen","description":"Sucht mögliche Interviewpartner in drei Beständen deiner Firma: Anfragen (Leads, ohne als Spam markierte), Adressbuch-Kontakte und Ansprechpartner. Es werden nur Personen mit E-Mail-Adresse berücksichtigt. Gesucht wird per Teilstring in Vorname, Nachname, Firma, E-Mail und vollem Namen; aus jedem Bestand kommen höchstens 10 Treffer, insgesamt also bis zu 30. Die Antwort enthält `anzahl` und `kandidaten`; jeder Treffer hat `quelle` (`lead`, `contact` oder `rcm_contact`), `quelle_id`, `anrede`, `vorname`, `nachname`, `firma`, `email` (kleingeschrieben), `telefon` und `etikett` (Anfrage, Adressbuch oder Ansprechpartner). Bei weniger als zwei Zeichen im Suchbegriff liefert die Funktion eine leere Liste ohne Fehler.\n\n**Gut zu wissen**\n\n- Mitglieder der Plattform werden bei dieser Suche nicht berücksichtigt.\n- Die Reihenfolge ergibt sich aus den Beständen: zuerst Anfragen (neueste zuerst), dann Adressbuch, dann Ansprechpartner.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/podcast_guests/find_podcast_gast_kandidaten \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\": \"Beispielmann\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/podcast_guests/find_podcast_gast_kandidaten\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"query\": \"Beispielmann\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/podcast_guests/find_podcast_gast_kandidaten\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"query\": \"Beispielmann\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Suchtext (mindestens 2 Zeichen): Teil eines Namens, einer Firma oder einer E-Mail-Adresse. Pflichtangabe. Kürzere Eingaben liefern eine leere Trefferliste. Das Zeichen `%` und `_` werden wörtlich gesucht.","example":"Beispielmann"}},"required":["query"]},"example":{"query":"Beispielmann"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"podcast_guests","werkzeug":"find_podcast_gast_kandidaten","stufe":"lesen","data":{"success":true,"data":{"anzahl":1,"kandidaten":[{"quelle":"lead","quelle_id":40871,"anrede":"Frau","vorname":"Mira","nachname":"Beispielmann","firma":"Muster Metallbau GmbH","email":"mira.beispielmann@example.com","telefon":"+49 211 5550123","etikett":"Anfrage"}]},"message":null,"timestamp":"2026-11-09 11:06:02","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/stories/get_stories":{"post":{"tags":["Magazin und Storyfilter"],"operationId":"stories__get_stories","summary":"Stories auflisten","description":"Listet die Stories deines Unternehmens seitenweise auf. Ohne Angaben kommen die ersten 20 Stories zurück; mit `limit` und `offset` blätterst du weiter. Mit `status` begrenzt du die Liste auf Stories mit genau diesem Status. Die Antwort enthält `items` (je Story: `id`, `title`, `subtitle`, `content`, `status`, `company_id`, `storyfilter_id`, `linguistic_id`, `target_channel`, `hashtags`, `created_at`, `updated_at`), `total` (Anzahl aller passenden Stories, unabhängig von der Seite), `limit` und `offset`. Der vollständige Text steht in jedem Eintrag, bei vielen Stories wird die Antwort deshalb groß – wähle `limit` entsprechend. Eine feste Sortierung ist nicht vorgegeben.\n\n**Gut zu wissen**\n\n- `limit` wird auf mindestens 1, `offset` auf mindestens 0 begrenzt; eine Obergrenze für `limit` gibt es nicht.\n- Der `status`-Filter vergleicht exakt; ein Status ohne Treffer liefert eine leere Liste mit `total` 0.\n- Für eine einzelne Story ist `get_story` schneller.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/stories/get_stories \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"draft\",\n    \"limit\": 10,\n    \"offset\": 0\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/stories/get_stories\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"draft\",\n        \"limit\": 10,\n        \"offset\": 0,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/stories/get_stories\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"draft\",\n      \"limit\": 10,\n      \"offset\": 0\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Optionaler Filter auf den Status der Story, z. B. `draft` oder `published`. Der Wert wird exakt verglichen; ohne Angabe kommen Stories mit jedem Status.","example":"draft"},"limit":{"type":"integer","description":"Maximale Anzahl Stories je Antwort. Standard ist 20, kleinere Werte als 1 werden auf 1 angehoben. Ganzzahl.","example":10},"offset":{"type":"integer","description":"Anzahl der Stories, die am Anfang übersprungen werden, für das Blättern (Standard 0). Zusammen mit `limit` und `total` aus der Antwort kannst du alle Seiten abrufen.","example":0}}},"example":{"status":"draft","limit":10,"offset":0}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"stories","werkzeug":"get_stories","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":4711,"title":"Wie ein Café seine Stammkundschaft verdoppelte","subtitle":"Drei Änderungen, die nichts kosten","content":"<p>Im ersten Jahr kamen die meisten Gäste nur einmal …</p>","status":"draft","company_id":1234,"storyfilter_id":88,"linguistic_id":null,"target_channel":"website_blog","hashtags":"#gastro #stammkunden","created_at":"2026-11-09T10:15:32+01:00","updated_at":"2026-11-09T10:15:32+01:00"}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/stories/get_story":{"post":{"tags":["Magazin und Storyfilter"],"operationId":"stories__get_story","summary":"Story abrufen","description":"Gibt eine einzelne Story anhand ihrer ID zurück. Die Antwort enthält dieselben Felder wie ein Eintrag aus `get_stories`: `id`, `title`, `subtitle`, `content`, `status`, `company_id`, `storyfilter_id`, `linguistic_id`, `target_channel`, `hashtags`, `created_at` und `updated_at` (Zeitstempel im ISO-8601-Format). Gehört die Story nicht zu deinem Unternehmen oder existiert sie nicht, kommt `success: false` mit dem Hinweis „Story nicht gefunden oder kein Zugriff“. Die ID erhältst du aus `get_stories` oder aus der Antwort von `create_story`.\n\n**Gut zu wissen**\n\n- Fehlende Werte erscheinen als `null`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/stories/get_story \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"story_id\": 4711\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/stories/get_story\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"story_id\": 4711,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/stories/get_story\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"story_id\": 4711\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"story_id":{"type":"integer","description":"ID der Story als Ganzzahl. Pflichtangabe; die ID stammt aus `get_stories` oder `create_story`. Die Story muss zu deinem Unternehmen gehören.","example":4711}},"required":["story_id"]},"example":{"story_id":4711}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"stories","werkzeug":"get_story","stufe":"lesen","data":{"success":true,"data":{"id":4711,"title":"Wie ein Café seine Stammkundschaft verdoppelte","subtitle":"Drei Änderungen, die nichts kosten","content":"<p>Im ersten Jahr kamen die meisten Gäste nur einmal …</p>","status":"draft","company_id":1234,"storyfilter_id":88,"linguistic_id":null,"target_channel":"website_blog","hashtags":"#gastro #stammkunden","created_at":"2026-11-09T10:15:32+01:00","updated_at":"2026-11-09T10:15:32+01:00"},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/stories/create_story":{"post":{"tags":["Magazin und Storyfilter"],"operationId":"stories__create_story","summary":"Story anlegen","description":"Legt eine neue Story (einen Magazin-Beitrag) für dein Unternehmen als **Entwurf** an und gibt sie vollständig zurück (`id`, `title`, `subtitle`, `content`, `status`, `company_id`, `storyfilter_id`, `linguistic_id`, `target_channel`, `hashtags`, `created_at`, `updated_at`). Die Angaben stehen im Objekt `data`; Titel und Text speichert die Funktion in deutscher Sprache. Die Story ist beim Anlegen **nicht veröffentlicht** (`status` ist `draft`, wenn du keinen anderen Wert setzt); sie erscheint also noch auf keiner Webseite. Ohne Angabe ist der Zielkanal `magazin_artikel`. Mit `update_story` änderst du die Story später; das Veröffentlichen geschieht in der Oberfläche bzw. durch die Redaktion.\n\n**Gut zu wissen**\n\n- `status` und `target_channel` kannst du setzen; ohne Angabe gelten `draft` und `magazin_artikel`.\n- `storyfilter_id` und `linguistic_id` werden nicht darauf geprüft, ob es das Objekt gibt oder es zu deinem Unternehmen gehört.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/stories/create_story \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"data\": {\n        \"title\": \"Wie ein Café seine Stammkundschaft verdoppelte\",\n        \"subtitle\": \"Drei Änderungen, die nichts kosten\",\n        \"content\": \"<p>Im ersten Jahr kamen die meisten Gäste nur einmal …</p>\",\n        \"status\": \"draft\",\n        \"storyfilter_id\": 88,\n        \"target_channel\": \"magazin_artikel\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/stories/create_story\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"data\": {\n            \"title\": \"Wie ein Café seine Stammkundschaft verdoppelte\",\n            \"subtitle\": \"Drei Änderungen, die nichts kosten\",\n            \"content\": \"<p>Im ersten Jahr kamen die meisten Gäste nur einmal …</p>\",\n            \"status\": \"draft\",\n            \"storyfilter_id\": 88,\n            \"target_channel\": \"magazin_artikel\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/stories/create_story\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"data\": {\n          \"title\": \"Wie ein Café seine Stammkundschaft verdoppelte\",\n          \"subtitle\": \"Drei Änderungen, die nichts kosten\",\n          \"content\": \"<p>Im ersten Jahr kamen die meisten Gäste nur einmal …</p>\",\n          \"status\": \"draft\",\n          \"storyfilter_id\": 88,\n          \"target_channel\": \"magazin_artikel\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"Objekt mit den Feldern der Story. Erlaubt sind: `title` (Titel), `subtitle` (Untertitel), `content` (Text, HTML möglich), `status` (Status der Story; im Datenmodell vorgesehen sind `draft`, `review`, `approved`, `published`, `archived`), `storyfilter_id` (Ganzzahl, ID eines Storyfilters), `linguistic_id` (Ganzzahl, ID eines Sprachprofils), `target_channel` (Zielkanal; vorgesehen sind `website_homepage`, `website_landingpage`, `website_blog`, `email_newsletter`, `email_sequence`, `social_media_post`, `linkedin_post`, `facebook_post`, `instagram_post`, `xing_post`) und `hashtags` (ein Text, z. B. `#gastro #stammkunden`). Felder, die du weglässt oder mit `null` übergibst, bleiben unverändert; ein leerer Text `\"\"` überschreibt den Wert. Die Felder dürfen auch direkt im Körper stehen, ohne `data` – der Server verpackt sie dann selbst.","properties":{"title":{"type":"string","description":"Titel der Story"},"subtitle":{"type":"string","description":"Untertitel der Story"},"content":{"type":"string","description":"Inhalt/Text der Story"},"status":{"type":"string","description":"Status, z.B. draft, published"},"storyfilter_id":{"type":"integer","description":"ID des zugeordneten Storyfilters"},"linguistic_id":{"type":"integer","description":"ID des Linguistic-Profils"},"target_channel":{"type":"string","description":"Zielkanal, z.B. linkedin, blog"},"hashtags":{"type":"string","description":"Hashtags fuer die Story"}},"example":{"title":"Wie ein Café seine Stammkundschaft verdoppelte","subtitle":"Drei Änderungen, die nichts kosten","content":"<p>Im ersten Jahr kamen die meisten Gäste nur einmal …</p>","status":"draft","storyfilter_id":88,"target_channel":"magazin_artikel"}}},"required":["data"]},"example":{"data":{"title":"Wie ein Café seine Stammkundschaft verdoppelte","subtitle":"Drei Änderungen, die nichts kosten","content":"<p>Im ersten Jahr kamen die meisten Gäste nur einmal …</p>","status":"draft","storyfilter_id":88,"target_channel":"magazin_artikel"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"stories","werkzeug":"create_story","stufe":"schreiben","data":{"success":true,"data":{"id":4711,"title":"Wie ein Café seine Stammkundschaft verdoppelte","subtitle":"Drei Änderungen, die nichts kosten","content":"<p>Im ersten Jahr kamen die meisten Gäste nur einmal …</p>","status":"draft","company_id":1234,"storyfilter_id":88,"linguistic_id":null,"target_channel":"magazin_artikel","hashtags":"#gastro #stammkunden","created_at":"2026-11-09T10:15:32+01:00","updated_at":"2026-11-09T10:15:32+01:00"},"message":"Story erstellt","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/stories/update_story":{"post":{"tags":["Magazin und Storyfilter"],"operationId":"stories__update_story","summary":"Story ändern","description":"Ändert Felder einer bestehenden Story. Es werden nur die Felder überschrieben, die du in `data` angibst und die nicht `null` sind; alle anderen Felder bleiben, wie sie sind. So lässt sich z. B. nur der Status von `draft` auf `review` setzen. Die Antwort enthält die gespeicherte Story in der Form von `get_story` und die Meldung „Story aktualisiert“. Die Story muss zu deinem Unternehmen gehören, sonst kommt „Story nicht gefunden oder kein Zugriff“. Zum Leeren eines Textfeldes übergib einen leeren Text; `null` wird ignoriert.\n\n**Gut zu wissen**\n\n- Ein Wert `null` löscht nichts – er wird übersprungen.\n- Mit nur `story_id` und leerem `data` ändert sich nichts; im Körper muss `data` jedoch vorhanden und nicht leer sein.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/stories/update_story \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"story_id\": 4711,\n    \"data\": {\n        \"status\": \"review\",\n        \"hashtags\": \"#gastro #stammkunden #cafe\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/stories/update_story\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"story_id\": 4711,\n        \"data\": {\n            \"status\": \"review\",\n            \"hashtags\": \"#gastro #stammkunden #cafe\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/stories/update_story\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"story_id\": 4711,\n      \"data\": {\n          \"status\": \"review\",\n          \"hashtags\": \"#gastro #stammkunden #cafe\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"story_id":{"type":"integer","description":"ID der zu ändernden Story als Ganzzahl. Pflichtangabe; die Story muss zu deinem Unternehmen gehören.","example":4711},"data":{"type":"object","description":"Objekt mit den Feldern der Story. Erlaubt sind: `title` (Titel), `subtitle` (Untertitel), `content` (Text, HTML möglich), `status` (Status der Story; im Datenmodell vorgesehen sind `draft`, `review`, `approved`, `published`, `archived`), `storyfilter_id` (Ganzzahl, ID eines Storyfilters), `linguistic_id` (Ganzzahl, ID eines Sprachprofils), `target_channel` (Zielkanal; vorgesehen sind `website_homepage`, `website_landingpage`, `website_blog`, `email_newsletter`, `email_sequence`, `social_media_post`, `linkedin_post`, `facebook_post`, `instagram_post`, `xing_post`) und `hashtags` (ein Text, z. B. `#gastro #stammkunden`). Felder, die du weglässt oder mit `null` übergibst, bleiben unverändert; ein leerer Text `\"\"` überschreibt den Wert. Die Felder dürfen auch direkt im Körper stehen, ohne `data` – der Server verpackt sie dann selbst.","properties":{"title":{"type":"string","description":"Titel der Story"},"subtitle":{"type":"string","description":"Untertitel der Story"},"content":{"type":"string","description":"Inhalt/Text der Story"},"status":{"type":"string","description":"Status, z.B. draft, published"},"storyfilter_id":{"type":"integer","description":"ID des zugeordneten Storyfilters"},"linguistic_id":{"type":"integer","description":"ID des Linguistic-Profils"},"target_channel":{"type":"string","description":"Zielkanal, z.B. linkedin, blog"},"hashtags":{"type":"string","description":"Hashtags fuer die Story"}},"example":{"status":"review","hashtags":"#gastro #stammkunden #cafe"}}},"required":["story_id","data"]},"example":{"story_id":4711,"data":{"status":"review","hashtags":"#gastro #stammkunden #cafe"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"stories","werkzeug":"update_story","stufe":"schreiben","data":{"success":true,"data":{"id":4711,"title":"Wie ein Café seine Stammkundschaft verdoppelte","subtitle":"Drei Änderungen, die nichts kosten","content":"<p>Im ersten Jahr kamen die meisten Gäste nur einmal …</p>","status":"review","company_id":1234,"storyfilter_id":88,"linguistic_id":null,"target_channel":"website_blog","hashtags":"#gastro #stammkunden #cafe","created_at":"2026-11-09T10:15:32+01:00","updated_at":"2026-11-09T11:02:10+01:00"},"message":"Story aktualisiert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/stories/delete_story":{"post":{"tags":["Magazin und Storyfilter"],"operationId":"stories__delete_story","summary":"Story löschen","description":"Löscht eine Story endgültig. Es gibt keinen Papierkorb in dieser API, die Aktion ist nicht umkehrbar; deshalb musst du im Körper `\"bestaetigt\": true` senden, sonst lehnt der Server mit einem Fehler ab. Die Antwort enthält nur die ID der gelöschten Story (`story_id`) und die Meldung „Story gelöscht“. Gehört die Story nicht zu deinem Unternehmen oder existiert sie nicht, kommt `success: false` mit „Story nicht gefunden oder kein Zugriff“. Prüfe die ID vorher mit `get_story`.\n\n**Gut zu wissen**\n\n- Der Schlüssel braucht das Recht „löschen“.\n- Ein zugeordneter Storyfilter bleibt erhalten.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/stories/delete_story \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"story_id\": 4711,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/stories/delete_story\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"story_id\": 4711,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/stories/delete_story\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"story_id\": 4711,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"story_id":{"type":"integer","description":"ID der zu löschenden Story als Ganzzahl. Pflichtangabe; die Story muss zu deinem Unternehmen gehören und wird unwiderruflich entfernt.","example":4711},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["story_id","bestaetigt"]},"example":{"story_id":4711,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"stories","werkzeug":"delete_story","stufe":"loeschen","data":{"success":true,"data":{"story_id":4711},"message":"Story gelöscht","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/stories/get_storyfilters":{"post":{"tags":["Magazin und Storyfilter"],"operationId":"stories__get_storyfilters","summary":"Storyfilter auflisten","description":"Gibt alle Storyfilter deines Unternehmens als Liste zurück. Die Funktion hat keine Parameter und keine Seiteneinteilung – es kommen immer alle Filter. Jeder Eintrag enthält `id`, `title`, `description`, `core_message` (Kernbotschaft), `character`, `wish`, `external_problem`, `internal_problem`, `mentor`, `value_proposition`, `customer_benefit` und `expected_results`. Beachte: In der Antwort heißen die Felder ohne Präfix (`character`, `wish` …), beim Anlegen und Ändern verwendest du dagegen `a_character`, `b_wish`, `d_external_problem`, `e_internal_problem` und `g_mentor_character`. Die `data` der Antwort ist direkt die Liste, nicht ein Objekt mit `items`.\n\n**Gut zu wissen**\n\n- Die IDs aus dieser Liste setzt du als `storyfilter_id` an einer Story ein.\n- Die Feldnamen in der Antwort weichen von denen beim Schreiben ab (siehe Beschreibung).\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/stories/get_storyfilters \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/stories/get_storyfilters\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/stories/get_storyfilters\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"stories","werkzeug":"get_storyfilters","stufe":"lesen","data":{"success":true,"data":[{"id":88,"title":"Inhaberin eines kleinen Cafés","description":"Zielgruppe: selbstständige Gastronomen mit bis zu zehn Mitarbeitenden","core_message":"Wer seine Gäste kennt, muss weniger werben.","character":"Inhaberin, die jeden Gast beim Namen kennt","wish":"Planbare Umsätze auch in der Nebensaison","external_problem":"Gäste kommen einmal und kehren nicht zurück","internal_problem":"Sorge, den Laden nicht halten zu können","mentor":"Erfahrener Gastro-Berater mit eigenem Café","value_proposition":"Ein einfaches Stammgast-System ohne Software-Kosten","customer_benefit":"Mehr wiederkehrende Gäste mit wenig Aufwand","expected_results":"Deutlich mehr Stammgäste innerhalb eines Quartals"}],"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/stories/create_storyfilter":{"post":{"tags":["Magazin und Storyfilter"],"operationId":"stories__create_storyfilter","summary":"Storyfilter anlegen","description":"Legt einen neuen Storyfilter für dein Unternehmen an und gibt ihn zurück (Felder wie in `get_storyfilters`: `id`, `title`, `description`, `core_message`, `character`, `wish`, `external_problem`, `internal_problem`, `mentor`, `value_proposition`, `customer_benefit`, `expected_results`). Die Angaben stehen im Objekt `data`. Ein Storyfilter beschreibt, für wen und mit welcher Botschaft erzählt wird (Heldenreise-Schema); die ID setzt du anschließend an Stories ein (`create_story`, `update_story`). Die Antwort trägt die Meldung „Storyfilter erstellt“.\n\n**Gut zu wissen**\n\n- Im Schema wird `title` verlangt, der Server prüft aber nur das Vorhandensein von `data`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/stories/create_storyfilter \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"data\": {\n        \"title\": \"Inhaberin eines kleinen Cafés\",\n        \"core_message\": \"Wer seine Gäste kennt, muss weniger werben.\",\n        \"d_external_problem\": \"Gäste kommen einmal und kehren nicht zurück\",\n        \"value_proposition\": \"Ein einfaches Stammgast-System ohne Software-Kosten\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/stories/create_storyfilter\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"data\": {\n            \"title\": \"Inhaberin eines kleinen Cafés\",\n            \"core_message\": \"Wer seine Gäste kennt, muss weniger werben.\",\n            \"d_external_problem\": \"Gäste kommen einmal und kehren nicht zurück\",\n            \"value_proposition\": \"Ein einfaches Stammgast-System ohne Software-Kosten\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/stories/create_storyfilter\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"data\": {\n          \"title\": \"Inhaberin eines kleinen Cafés\",\n          \"core_message\": \"Wer seine Gäste kennt, muss weniger werben.\",\n          \"d_external_problem\": \"Gäste kommen einmal und kehren nicht zurück\",\n          \"value_proposition\": \"Ein einfaches Stammgast-System ohne Software-Kosten\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"Objekt mit den Feldern des Storyfilters. Erlaubt sind: `title`, `description`, `core_message` (Kernbotschaft), `a_character` (Charakter, Heldenreise A), `b_wish` (Wunsch, B), `d_external_problem` (äußeres Problem, D), `e_internal_problem` (inneres Problem, E), `g_mentor_character` (Mentor, G), `value_proposition` (Wertversprechen), `customer_benefit` (Kundennutzen) und `expected_results` (erwartete Ergebnisse). Alle Werte sind Texte. Felder, die du weglässt oder mit `null` übergibst, bleiben unverändert; ein leerer Text überschreibt den Wert. Die Felder dürfen auch direkt im Körper stehen, ohne `data` – der Server verpackt sie dann selbst.","properties":{"title":{"type":"string","description":"Titel des Storyfilters"},"description":{"type":"string","description":"Beschreibung"},"core_message":{"type":"string","description":"Kernbotschaft"},"a_character":{"type":"string","description":"Charakter (Heldenreise A)"},"b_wish":{"type":"string","description":"Wunsch (Heldenreise B)"},"d_external_problem":{"type":"string","description":"Aeusseres Problem (D)"},"e_internal_problem":{"type":"string","description":"Inneres Problem (E)"},"g_mentor_character":{"type":"string","description":"Mentor-Charakter (G)"},"value_proposition":{"type":"string","description":"Wertversprechen"},"customer_benefit":{"type":"string","description":"Kundennutzen"},"expected_results":{"type":"string","description":"Erwartete Ergebnisse"}},"example":{"title":"Inhaberin eines kleinen Cafés","core_message":"Wer seine Gäste kennt, muss weniger werben.","d_external_problem":"Gäste kommen einmal und kehren nicht zurück","value_proposition":"Ein einfaches Stammgast-System ohne Software-Kosten"}}},"required":["data"]},"example":{"data":{"title":"Inhaberin eines kleinen Cafés","core_message":"Wer seine Gäste kennt, muss weniger werben.","d_external_problem":"Gäste kommen einmal und kehren nicht zurück","value_proposition":"Ein einfaches Stammgast-System ohne Software-Kosten"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"stories","werkzeug":"create_storyfilter","stufe":"schreiben","data":{"success":true,"data":{"id":88,"title":"Inhaberin eines kleinen Cafés","description":"Zielgruppe: selbstständige Gastronomen mit bis zu zehn Mitarbeitenden","core_message":"Wer seine Gäste kennt, muss weniger werben.","character":"Inhaberin, die jeden Gast beim Namen kennt","wish":"Planbare Umsätze auch in der Nebensaison","external_problem":"Gäste kommen einmal und kehren nicht zurück","internal_problem":"Sorge, den Laden nicht halten zu können","mentor":"Erfahrener Gastro-Berater mit eigenem Café","value_proposition":"Ein einfaches Stammgast-System ohne Software-Kosten","customer_benefit":"Mehr wiederkehrende Gäste mit wenig Aufwand","expected_results":"Deutlich mehr Stammgäste innerhalb eines Quartals"},"message":"Storyfilter erstellt","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/stories/update_storyfilter":{"post":{"tags":["Magazin und Storyfilter"],"operationId":"stories__update_storyfilter","summary":"Storyfilter ändern","description":"Ändert Felder eines bestehenden Storyfilters deines Unternehmens. Nur die Felder in `data`, die nicht `null` sind, werden überschrieben; alles andere bleibt unverändert. Die Antwort enthält den gespeicherten Filter (Felder wie in `get_storyfilters`) und die Meldung „Storyfilter aktualisiert“. Gehört der Filter nicht zu deinem Unternehmen oder gibt es ihn nicht, kommt „Storyfilter nicht gefunden oder kein Zugriff“. Die Filter-ID findest du mit `get_storyfilters`.\n\n**Gut zu wissen**\n\n- `null`-Werte werden ignoriert; zum Leeren eines Feldes übergib einen leeren Text.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/stories/update_storyfilter \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"storyfilter_id\": 88,\n    \"data\": {\n        \"customer_benefit\": \"Mehr wiederkehrende Gäste mit wenig Aufwand\",\n        \"expected_results\": \"Deutlich mehr Stammgäste innerhalb eines Quartals\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/stories/update_storyfilter\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"storyfilter_id\": 88,\n        \"data\": {\n            \"customer_benefit\": \"Mehr wiederkehrende Gäste mit wenig Aufwand\",\n            \"expected_results\": \"Deutlich mehr Stammgäste innerhalb eines Quartals\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/stories/update_storyfilter\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"storyfilter_id\": 88,\n      \"data\": {\n          \"customer_benefit\": \"Mehr wiederkehrende Gäste mit wenig Aufwand\",\n          \"expected_results\": \"Deutlich mehr Stammgäste innerhalb eines Quartals\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"storyfilter_id":{"type":"integer","description":"ID des zu ändernden Storyfilters als Ganzzahl. Pflichtangabe; den Filter findest du mit `get_storyfilters`. Er muss zu deinem Unternehmen gehören.","example":88},"data":{"type":"object","description":"Objekt mit den Feldern des Storyfilters. Erlaubt sind: `title`, `description`, `core_message` (Kernbotschaft), `a_character` (Charakter, Heldenreise A), `b_wish` (Wunsch, B), `d_external_problem` (äußeres Problem, D), `e_internal_problem` (inneres Problem, E), `g_mentor_character` (Mentor, G), `value_proposition` (Wertversprechen), `customer_benefit` (Kundennutzen) und `expected_results` (erwartete Ergebnisse). Alle Werte sind Texte. Felder, die du weglässt oder mit `null` übergibst, bleiben unverändert; ein leerer Text überschreibt den Wert. Die Felder dürfen auch direkt im Körper stehen, ohne `data` – der Server verpackt sie dann selbst.","properties":{"title":{"type":"string","description":"Titel des Storyfilters"},"description":{"type":"string","description":"Beschreibung"},"core_message":{"type":"string","description":"Kernbotschaft"},"a_character":{"type":"string","description":"Charakter (Heldenreise A)"},"b_wish":{"type":"string","description":"Wunsch (Heldenreise B)"},"d_external_problem":{"type":"string","description":"Aeusseres Problem (D)"},"e_internal_problem":{"type":"string","description":"Inneres Problem (E)"},"g_mentor_character":{"type":"string","description":"Mentor-Charakter (G)"},"value_proposition":{"type":"string","description":"Wertversprechen"},"customer_benefit":{"type":"string","description":"Kundennutzen"},"expected_results":{"type":"string","description":"Erwartete Ergebnisse"}},"example":{"customer_benefit":"Mehr wiederkehrende Gäste mit wenig Aufwand","expected_results":"Deutlich mehr Stammgäste innerhalb eines Quartals"}}},"required":["storyfilter_id","data"]},"example":{"storyfilter_id":88,"data":{"customer_benefit":"Mehr wiederkehrende Gäste mit wenig Aufwand","expected_results":"Deutlich mehr Stammgäste innerhalb eines Quartals"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"stories","werkzeug":"update_storyfilter","stufe":"schreiben","data":{"success":true,"data":{"id":88,"title":"Inhaberin eines kleinen Cafés","description":"Zielgruppe: selbstständige Gastronomen mit bis zu zehn Mitarbeitenden","core_message":"Wer seine Gäste kennt, muss weniger werben.","character":"Inhaberin, die jeden Gast beim Namen kennt","wish":"Planbare Umsätze auch in der Nebensaison","external_problem":"Gäste kommen einmal und kehren nicht zurück","internal_problem":"Sorge, den Laden nicht halten zu können","mentor":"Erfahrener Gastro-Berater mit eigenem Café","value_proposition":"Ein einfaches Stammgast-System ohne Software-Kosten","customer_benefit":"Mehr wiederkehrende Gäste mit wenig Aufwand","expected_results":"Deutlich mehr Stammgäste innerhalb eines Quartals"},"message":"Storyfilter aktualisiert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/get_templates":{"post":{"tags":["Landingpages"],"operationId":"landingpages__get_templates","summary":"Templates auflisten","description":"Listet Landingpage-Templates seitenweise auf. Aufgelistet werden die Templates deines Unternehmens. Gefiltert wird immer auf eine Variante (Standard `landingpage`; Templates ohne Variante zählen mit), optional auf einen Suchtext in Name oder Slug. Die Antwort enthält `items` (je Template: `id`, `name`, `slug`, `variant`, `status`, `company_id`, `colors`, `meta`, `updated_at`), `total`, `limit` und `offset`; Es gilt die Sortierung nach letzter Änderung, neueste zuerst.\n\n**Gut zu wissen**\n\n- Das Template-Objekt hat im Datenmodell weder ein Feld `status` noch ein Katalog-Kennzeichen: Katalog-Templates gibt es nicht (`include_catalog` hat keine Wirkung), ein `status`-Filter wird nicht ausgewertet, und `status` ist in den Antworten `null`.\n- Der Standardwert von `limit` ist 20; `offset` zählt über Katalog- und eigene Templates zusammen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/get_templates \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"variant\": \"landingpage\",\n    \"include_catalog\": false,\n    \"search\": \"handwerker\",\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/get_templates\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"variant\": \"landingpage\",\n        \"include_catalog\": False,\n        \"search\": \"handwerker\",\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/get_templates\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"variant\": \"landingpage\",\n      \"include_catalog\": false,\n      \"search\": \"handwerker\",\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Optionaler Filter auf den Status des Templates, z. B. `draft` oder `active`. Wird exakt verglichen."},"search":{"type":"string","description":"Optionaler Suchtext; gefunden werden Templates, deren Name oder Slug diesen Text enthalten (Teilstring, Groß-/Kleinschreibung egal).","example":"handwerker"},"variant":{"type":"string","description":"Variante, auf die gefiltert wird. Standard ist `landingpage`; Templates ohne Variante werden immer mitgeliefert. Andere Werte sind z. B. `interview` oder `landingcard`.","example":"landingpage"},"include_catalog":{"type":"boolean","description":"Wahrheitswert. Bei `true` (Standard) werden zuerst die globalen Katalog-Templates aufgeführt, bei `false` nur die Templates deines Unternehmens.","example":false},"limit":{"type":"integer","description":"Maximale Anzahl Einträge je Antwort. Standard 20, Werte unter 1 werden auf 1 angehoben. Ganzzahl.","example":10},"offset":{"type":"integer","description":"Anzahl der Einträge, die am Anfang der zusammengeführten Liste übersprungen werden (Standard 0)."}}},"example":{"variant":"landingpage","include_catalog":false,"search":"handwerker","limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"get_templates","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":301,"name":"Handwerker-Anfrage","slug":"handwerker-anfrage","variant":"landingpage","status":"draft","company_id":1234,"colors":{"body_color":"#1f2937","body_background":"#ffffff","primary":"#1d4ed8","secondary":"#f59e0b"},"meta":{"title":"Handwerker anfragen – schnell und unverbindlich","description":"Beschreibe dein Vorhaben und erhalte innerhalb eines Tages ein Angebot."},"updated_at":"2026-11-09T10:15:32+01:00"}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/get_template":{"post":{"tags":["Landingpages"],"operationId":"landingpages__get_template","summary":"Template abrufen","description":"Gibt ein einzelnes Landingpage-Template deines Unternehmens zurück: `id`, `name`, `slug`, `variant`, `status`, `company_id`, `colors` (`body_color`, `body_background`, `primary`, `secondary`), `meta` (`title`, `description`) und `updated_at`. Gehört das Template nicht zu deinem Unternehmen oder gibt es die ID nicht, kommt „Template nicht gefunden oder kein Zugriff“; auch globale Katalog-Templates anderer Mandanten sind über diese Funktion nicht abrufbar. Die ID findest du mit `get_templates` oder in der Antwort von `create_template`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/get_template \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 301\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/get_template\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 301,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/get_template\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 301\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"ID des Templates als Ganzzahl. Pflichtangabe; das Template muss zu deinem Unternehmen gehören.","example":301}},"required":["template_id"]},"example":{"template_id":301}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"get_template","stufe":"lesen","data":{"success":true,"data":{"id":301,"name":"Handwerker-Anfrage","slug":"handwerker-anfrage","variant":"landingpage","status":"draft","company_id":1234,"colors":{"body_color":"#1f2937","body_background":"#ffffff","primary":"#1d4ed8","secondary":"#f59e0b"},"meta":{"title":"Handwerker anfragen – schnell und unverbindlich","description":"Beschreibe dein Vorhaben und erhalte innerhalb eines Tages ein Angebot."},"updated_at":"2026-11-09T10:15:32+01:00"},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/create_template":{"post":{"tags":["Landingpages"],"operationId":"landingpages__create_template","summary":"Template anlegen","description":"Legt ein neues Template für dein Unternehmen an. Pflicht ist `name`. Der Slug wird aus `slug` oder, wenn er fehlt, aus dem Namen gebildet. Der Slug wird vereinfacht: Kleinbuchstaben, alle Zeichen außer `a–z`, `0–9` und `-` werden zu `-` (auch Umlaute), Bindestriche am Rand entfallen. Bleibt nichts übrig, wird `template-<Zeitstempel>` verwendet. Variante (Standard `landingpage`) und Status (Standard `draft`) lassen sich überschreiben, Farben und Meta-Angaben optional mitgeben. Das Template wird im Ordner `/templates` abgelegt. Bei Erfolg kommt das Template in der Form von `get_template` mit der Meldung „Template erstellt“ zurück.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/create_template \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Handwerker-Anfrage\",\n    \"slug\": \"handwerker-anfrage\",\n    \"colors\": {\n        \"primary\": \"#1d4ed8\",\n        \"secondary\": \"#f59e0b\"\n    },\n    \"meta\": {\n        \"title\": \"Handwerker anfragen – schnell und unverbindlich\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/create_template\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"name\": \"Handwerker-Anfrage\",\n        \"slug\": \"handwerker-anfrage\",\n        \"colors\": {\n            \"primary\": \"#1d4ed8\",\n            \"secondary\": \"#f59e0b\",\n        },\n        \"meta\": {\n            \"title\": \"Handwerker anfragen – schnell und unverbindlich\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/create_template\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"name\": \"Handwerker-Anfrage\",\n      \"slug\": \"handwerker-anfrage\",\n      \"colors\": {\n          \"primary\": \"#1d4ed8\",\n          \"secondary\": \"#f59e0b\"\n      },\n      \"meta\": {\n          \"title\": \"Handwerker anfragen – schnell und unverbindlich\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Interner Name des Templates. Pflichtangabe, Text; HTML-Zeichen werden maskiert.","example":"Handwerker-Anfrage"},"slug":{"type":"string","description":"Optionaler Slug für URLs. Ohne Angabe wird er aus dem Namen abgeleitet. Der Slug wird vereinfacht: Kleinbuchstaben, alle Zeichen außer `a–z`, `0–9` und `-` werden zu `-` (auch Umlaute), Bindestriche am Rand entfallen. Bleibt nichts übrig, wird `template-<Zeitstempel>` verwendet.","example":"handwerker-anfrage"},"status":{"type":"string","description":"Status des neuen Templates als Text, z. B. `draft` oder `active`. Standard ist `draft`."},"variant":{"type":"string","description":"Template-Variante als Text. Standard ist `landingpage`."},"colors":{"type":"object","description":"Objekt mit den Farben des Templates. Erlaubte Schlüssel: `body_color` (Textfarbe), `body_background` (Seitenhintergrund), `primary` (Primärfarbe) und `secondary` (Sekundärfarbe), jeweils als Text, üblicherweise Hex-Wert wie `#1d4ed8`. Nicht angegebene Schlüssel bleiben unverändert beziehungsweise leer.","example":{"primary":"#1d4ed8","secondary":"#f59e0b"}},"meta":{"type":"object","description":"Objekt mit den Meta-Angaben für Suchmaschinen: `title` (Seitentitel) und `description` (Beschreibung). Beide sind Texte; nicht angegebene Schlüssel bleiben unverändert.","example":{"title":"Handwerker anfragen – schnell und unverbindlich"}}},"required":["name"]},"example":{"name":"Handwerker-Anfrage","slug":"handwerker-anfrage","colors":{"primary":"#1d4ed8","secondary":"#f59e0b"},"meta":{"title":"Handwerker anfragen – schnell und unverbindlich"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"create_template","stufe":"schreiben","data":{"success":true,"data":{"id":301,"name":"Handwerker-Anfrage","slug":"handwerker-anfrage","variant":"landingpage","status":"draft","company_id":1234,"colors":{"body_color":"#1f2937","body_background":"#ffffff","primary":"#1d4ed8","secondary":"#f59e0b"},"meta":{"title":"Handwerker anfragen – schnell und unverbindlich","description":"Beschreibe dein Vorhaben und erhalte innerhalb eines Tages ein Angebot."},"updated_at":"2026-11-09T10:15:32+01:00"},"message":"Template erstellt","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/update_template":{"post":{"tags":["Landingpages"],"operationId":"landingpages__update_template","summary":"Template ändern","description":"Ändert Felder eines Templates deines Unternehmens. In `data` stehen die zu ändernden Felder: `name`, `slug`, `status`, `variant`, `colors` und `meta`; nicht genannte Felder bleiben unverändert. Ein neuer Slug ändert auch den internen Schlüssel des Objekts. Der Slug wird vereinfacht: Kleinbuchstaben, alle Zeichen außer `a–z`, `0–9` und `-` werden zu `-` (auch Umlaute), Bindestriche am Rand entfallen. Bleibt nichts übrig, wird `template-<Zeitstempel>` verwendet. Bei Erfolg kommt das gespeicherte Template in der Form von `get_template` mit der Meldung „Template aktualisiert“ zurück. Gehört das Template nicht zu deinem Unternehmen, kommt „Template nicht gefunden oder kein Zugriff“.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/update_template \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 301,\n    \"data\": {\n        \"name\": \"Handwerker-Anfrage v2\",\n        \"colors\": {\n            \"primary\": \"#0f766e\"\n        }\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/update_template\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 301,\n        \"data\": {\n            \"name\": \"Handwerker-Anfrage v2\",\n            \"colors\": {\n                \"primary\": \"#0f766e\",\n            },\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/update_template\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 301,\n      \"data\": {\n          \"name\": \"Handwerker-Anfrage v2\",\n          \"colors\": {\n              \"primary\": \"#0f766e\"\n          }\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"ID des zu ändernden Templates als Ganzzahl. Pflichtangabe; das Template muss zu deinem Unternehmen gehören.","example":301},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern. Erlaubt: `name`, `slug`, `status`, `variant` (jeweils Text), `colors` (Objekt mit `body_color`, `body_background`, `primary`, `secondary`) und `meta` (Objekt mit `title`, `description`). Nur angegebene Felder werden geändert.","example":{"name":"Handwerker-Anfrage v2","colors":{"primary":"#0f766e"}}}},"required":["template_id","data"]},"example":{"template_id":301,"data":{"name":"Handwerker-Anfrage v2","colors":{"primary":"#0f766e"}}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"update_template","stufe":"schreiben","data":{"success":true,"data":{"id":301,"name":"Handwerker-Anfrage v2","slug":"handwerker-anfrage","variant":"landingpage","status":"draft","company_id":1234,"colors":{"body_color":"#1f2937","body_background":"#ffffff","primary":"#0f766e","secondary":"#f59e0b"},"meta":{"title":"Handwerker anfragen – schnell und unverbindlich","description":"Beschreibe dein Vorhaben und erhalte innerhalb eines Tages ein Angebot."},"updated_at":"2026-11-09T10:15:32+01:00"},"message":"Template aktualisiert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/delete_template":{"post":{"tags":["Landingpages"],"operationId":"landingpages__delete_template","summary":"Template löschen","description":"Löscht ein Template deines Unternehmens endgültig. Die Aktion ist nicht umkehrbar; du musst `\"bestaetigt\": true` mitsenden. Die Antwort enthält nur die ID (`template_id`) und die Meldung „Template gelöscht“. Gehört das Template nicht zu deinem Unternehmen, kommt „Template nicht gefunden oder kein Zugriff“. Portale, die dieses Template verwenden, werden nicht angepasst – löse die Verknüpfung vorher mit `update_portal` (`template_id` leer oder 0).\n\n**Gut zu wissen**\n\n- Der Schlüssel braucht das Recht „löschen“.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/delete_template \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 301,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/delete_template\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 301,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/delete_template\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 301,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"ID des zu löschenden Templates als Ganzzahl. Pflichtangabe; das Template muss zu deinem Unternehmen gehören und wird unwiderruflich entfernt.","example":301},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["template_id","bestaetigt"]},"example":{"template_id":301,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"delete_template","stufe":"loeschen","data":{"success":true,"data":{"template_id":301},"message":"Template gelöscht","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/copy_template":{"post":{"tags":["Landingpages"],"operationId":"landingpages__copy_template","summary":"Template kopieren","description":"Erstellt eine Kopie eines Templates deines Unternehmens. Name und Slug der Kopie kannst du vorgeben; ohne Angabe heißt die Kopie „<Name> (Kopie)“ und der Slug „<Slug>-copy“. Kopiert werden Variante, Textfarbe, Hintergrundfarbe, Primär- und Sekundärfarbe sowie Meta-Titel und -Beschreibung; der Status der Kopie ist `draft`. Die Kopie liegt im selben Ordner wie das Original. Bei Erfolg kommt die Kopie in der Form von `get_template` mit der Meldung „Template kopiert“ zurück.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/copy_template \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 301,\n    \"name\": \"Handwerker-Anfrage Variante B\",\n    \"slug\": \"handwerker-anfrage-b\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/copy_template\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 301,\n        \"name\": \"Handwerker-Anfrage Variante B\",\n        \"slug\": \"handwerker-anfrage-b\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/copy_template\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 301,\n      \"name\": \"Handwerker-Anfrage Variante B\",\n      \"slug\": \"handwerker-anfrage-b\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"ID des Quell-Templates als Ganzzahl. Pflichtangabe; das Template muss zu deinem Unternehmen gehören.","example":301},"name":{"type":"string","description":"Optionaler Name der Kopie. Ohne Angabe wird „<Name> (Kopie)“ verwendet.","example":"Handwerker-Anfrage Variante B"},"slug":{"type":"string","description":"Optionaler Slug der Kopie. Ohne Angabe wird der Slug des Originals mit `-copy` verwendet. Der Slug wird vereinfacht: Kleinbuchstaben, alle Zeichen außer `a–z`, `0–9` und `-` werden zu `-` (auch Umlaute), Bindestriche am Rand entfallen. Bleibt nichts übrig, wird `template-<Zeitstempel>` verwendet.","example":"handwerker-anfrage-b"}},"required":["template_id"]},"example":{"template_id":301,"name":"Handwerker-Anfrage Variante B","slug":"handwerker-anfrage-b"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"copy_template","stufe":"schreiben","data":{"success":true,"data":{"id":302,"name":"Handwerker-Anfrage Variante B","slug":"handwerker-anfrage-b","variant":"landingpage","status":"draft","company_id":1234,"colors":{"body_color":"#1f2937","body_background":"#ffffff","primary":"#1d4ed8","secondary":"#f59e0b"},"meta":{"title":"Handwerker anfragen – schnell und unverbindlich","description":"Beschreibe dein Vorhaben und erhalte innerhalb eines Tages ein Angebot."},"updated_at":"2026-11-09T10:15:32+01:00"},"message":"Template kopiert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/get_portals":{"post":{"tags":["Landingpages"],"operationId":"landingpages__get_portals","summary":"Portale auflisten","description":"Listet die Landingpage-Portale deines Unternehmens seitenweise auf, neueste Änderung zuerst. Es erscheinen nur Portale vom Typ `landingpage`. Optional filterst du mit `status`. Die Antwort enthält `items` (je Portal: `id`, `title`, `slug`, `status`, `domain`, `lang`, `company_id`, `template` (das verknüpfte Template oder `null`), `keyword` (das verknüpfte Keyword oder `null`), `meta` mit `title` und `description`, `updated_at`), dazu `total`, `limit` und `offset`. Standardmäßig kommen 20 Portale je Antwort.\n\n**Gut zu wissen**\n\n- Mögliche Status-Werte im Datenmodell sind `draft` und `public`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/get_portals \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"draft\",\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/get_portals\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"draft\",\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/get_portals\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"draft\",\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Optionaler Filter auf den Status des Portals. Im Datenmodell vorgesehen sind `draft` und `public`; der Wert wird exakt verglichen.","example":"draft"},"limit":{"type":"integer","description":"Maximale Anzahl Portale je Antwort. Standard 20, Werte unter 1 werden auf 1 angehoben.","example":10},"offset":{"type":"integer","description":"Anzahl der Portale, die am Anfang übersprungen werden (Standard 0), für das Blättern zusammen mit `total`."}}},"example":{"status":"draft","limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"get_portals","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":401,"title":"Heizungstausch Ratgeber","slug":"heizungstausch-ratgeber","status":"draft","domain":"ratgeber.example.com","lang":"de","company_id":1234,"template":null,"keyword":{"id":501,"keyword":"heizung austauschen kosten","suggest":"heizung austauschen kosten förderung","main_intent":"informational","lang":"de","search_volume":2400,"cpc":1.85,"competition_level":"MEDIUM","group_id":12,"company_id":1234,"updated_at":"2026-11-09T10:15:32+01:00"},"meta":{"title":"Heizungstausch: Kosten und Ablauf","description":"Alles zum Austausch deiner Heizung."},"updated_at":"2026-11-09T10:15:32+01:00"}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/get_portal":{"post":{"tags":["Landingpages"],"operationId":"landingpages__get_portal","summary":"Portal abrufen","description":"Gibt ein einzelnes Landingpage-Portal deines Unternehmens zurück: `id`, `title`, `slug`, `status`, `domain`, `lang`, `company_id`, das verknüpfte `template` und `keyword` (jeweils als vollständiges Objekt oder `null`), `meta` (`title`, `description`) und `updated_at`. Das Portal muss zu deinem Unternehmen gehören, sonst kommt „Portal nicht gefunden oder kein Zugriff“. Die ID erhältst du aus `get_portals` oder `create_portal`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/get_portal \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portal_id\": 401\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/get_portal\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"portal_id\": 401,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/get_portal\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"portal_id\": 401\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"portal_id":{"type":"integer","description":"ID des Portals als Ganzzahl. Pflichtangabe; das Portal muss zu deinem Unternehmen gehören.","example":401}},"required":["portal_id"]},"example":{"portal_id":401}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"get_portal","stufe":"lesen","data":{"success":true,"data":{"id":401,"title":"Heizungstausch Ratgeber","slug":"heizungstausch-ratgeber","status":"draft","domain":"ratgeber.example.com","lang":"de","company_id":1234,"template":null,"keyword":{"id":501,"keyword":"heizung austauschen kosten","suggest":"heizung austauschen kosten förderung","main_intent":"informational","lang":"de","search_volume":2400,"cpc":1.85,"competition_level":"MEDIUM","group_id":12,"company_id":1234,"updated_at":"2026-11-09T10:15:32+01:00"},"meta":{"title":"Heizungstausch: Kosten und Ablauf","description":"Alles zum Austausch deiner Heizung."},"updated_at":"2026-11-09T10:15:32+01:00"},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/create_portal":{"post":{"tags":["Landingpages"],"operationId":"landingpages__create_portal","summary":"Portal anlegen","description":"Legt ein neues Landingpage-Portal an (Typ ist fest `landingpage`). Pflicht ist `title`. Slug (aus dem Titel abgeleitet, wenn nicht angegeben), Status (Standard `draft`), Sprache (Standard `de`) und Domain (Standard leer) sind optional. `keyword_id` und `template_id` verknüpfen vorhandene Objekte deines Unternehmens; mit `create_template: true` (und ohne `template_id`) wird stattdessen ein neues Template „<Titel> Template“ angelegt. `meta` setzt Seitentitel und Beschreibung. Bei Erfolg kommt das Portal in der Form von `get_portal` mit der Meldung „Portal erstellt“ zurück. Der Slug wird vereinfacht: Kleinbuchstaben, alle Zeichen außer `a–z`, `0–9` und `-` werden zu `-` (auch Umlaute), Bindestriche am Rand entfallen. Bleibt nichts übrig, wird `template-<Zeitstempel>` verwendet.\n\n**Gut zu wissen**\n\n- `template_id` hat Vorrang vor `create_template`.\n- Eine verknüpfte Keyword- oder Template-ID, die nicht zu deinem Unternehmen gehört, führt zu „… nicht gefunden oder kein Zugriff“.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/create_portal \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Heizungstausch Ratgeber\",\n    \"domain\": \"ratgeber.example.com\",\n    \"lang\": \"de\",\n    \"keyword_id\": 501,\n    \"meta\": {\n        \"title\": \"Heizungstausch: Kosten und Ablauf\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/create_portal\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"title\": \"Heizungstausch Ratgeber\",\n        \"domain\": \"ratgeber.example.com\",\n        \"lang\": \"de\",\n        \"keyword_id\": 501,\n        \"meta\": {\n            \"title\": \"Heizungstausch: Kosten und Ablauf\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/create_portal\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"title\": \"Heizungstausch Ratgeber\",\n      \"domain\": \"ratgeber.example.com\",\n      \"lang\": \"de\",\n      \"keyword_id\": 501,\n      \"meta\": {\n          \"title\": \"Heizungstausch: Kosten und Ablauf\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Titel des Portals. Pflichtangabe, Text.","example":"Heizungstausch Ratgeber"},"slug":{"type":"string","description":"Optionaler Slug für die URL des Portals. Ohne Angabe wird er aus dem Titel abgeleitet. Der Slug wird vereinfacht: Kleinbuchstaben, alle Zeichen außer `a–z`, `0–9` und `-` werden zu `-` (auch Umlaute), Bindestriche am Rand entfallen. Bleibt nichts übrig, wird `template-<Zeitstempel>` verwendet."},"status":{"type":"string","description":"Status des Portals als Text. Standard `draft`; im Datenmodell vorgesehen sind `draft` und `public`."},"domain":{"type":"string","description":"Optionale Domain oder Subdomain, unter der das Portal erreichbar sein soll, z. B. `ratgeber.example.com`. Standard ist leer.","example":"ratgeber.example.com"},"lang":{"type":"string","description":"Sprachcode des Portals, z. B. `de` oder `en`. Standard ist `de`.","example":"de"},"template_id":{"type":"integer","description":"ID eines vorhandenen Templates deines Unternehmens, das dem Portal zugewiesen wird. Hat Vorrang vor `create_template`."},"keyword_id":{"type":"integer","description":"ID eines vorhandenen Keywords deines Unternehmens (siehe `get_keywords`), mit dem das Portal verknüpft wird.","example":501},"meta":{"type":"object","description":"Objekt mit den Meta-Angaben für Suchmaschinen: `title` (Seitentitel) und `description` (Beschreibung). Beide sind Texte; nicht angegebene Schlüssel bleiben unverändert.","example":{"title":"Heizungstausch: Kosten und Ablauf"}},"create_template":{"type":"boolean","description":"Wahrheitswert. Bei `true` und ohne `template_id` wird zusätzlich ein neues Template mit dem Namen „<Titel> Template“ (Variante `landingpage`) angelegt und dem Portal zugewiesen."}},"required":["title"]},"example":{"title":"Heizungstausch Ratgeber","domain":"ratgeber.example.com","lang":"de","keyword_id":501,"meta":{"title":"Heizungstausch: Kosten und Ablauf"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"create_portal","stufe":"schreiben","data":{"success":true,"data":{"id":401,"title":"Heizungstausch Ratgeber","slug":"heizungstausch-ratgeber","status":"draft","domain":"ratgeber.example.com","lang":"de","company_id":1234,"template":null,"keyword":{"id":501,"keyword":"heizung austauschen kosten","suggest":"heizung austauschen kosten förderung","main_intent":"informational","lang":"de","search_volume":2400,"cpc":1.85,"competition_level":"MEDIUM","group_id":12,"company_id":1234,"updated_at":"2026-11-09T10:15:32+01:00"},"meta":{"title":"Heizungstausch: Kosten und Ablauf","description":"Alles zum Austausch deiner Heizung."},"updated_at":"2026-11-09T10:15:32+01:00"},"message":"Portal erstellt","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/update_portal":{"post":{"tags":["Landingpages"],"operationId":"landingpages__update_portal","summary":"Portal ändern","description":"Ändert Felder eines Portals deines Unternehmens. In `data` stehen die zu ändernden Felder: `title`, `slug`, `status`, `domain`, `lang`, `keyword_id`, `template_id` und `meta`. Nicht genannte Felder bleiben unverändert. Ein leerer Wert bei `keyword_id` oder `template_id` (z. B. `0`) löst die Verknüpfung; ein neuer Slug ändert auch den internen Schlüssel. Bei Erfolg kommt das Portal in der Form von `get_portal` mit der Meldung „Portal aktualisiert“ zurück. Gehört das Portal, Keyword oder Template nicht zu deinem Unternehmen, kommt die entsprechende Fehlermeldung. Der Slug wird vereinfacht: Kleinbuchstaben, alle Zeichen außer `a–z`, `0–9` und `-` werden zu `-` (auch Umlaute), Bindestriche am Rand entfallen. Bleibt nichts übrig, wird `template-<Zeitstempel>` verwendet.\n\n**Gut zu wissen**\n\n- Der Status wird nicht gegen eine Werteliste geprüft; im Datenmodell vorgesehen sind `draft` und `public`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/update_portal \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portal_id\": 401,\n    \"data\": {\n        \"status\": \"public\",\n        \"domain\": \"heizung.example.com\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/update_portal\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"portal_id\": 401,\n        \"data\": {\n            \"status\": \"public\",\n            \"domain\": \"heizung.example.com\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/update_portal\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"portal_id\": 401,\n      \"data\": {\n          \"status\": \"public\",\n          \"domain\": \"heizung.example.com\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"portal_id":{"type":"integer","description":"ID des zu ändernden Portals als Ganzzahl. Pflichtangabe; das Portal muss zu deinem Unternehmen gehören.","example":401},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern. Erlaubt: `title`, `slug`, `status`, `domain`, `lang` (Texte), `keyword_id` und `template_id` (Ganzzahlen, `0` löst die Verknüpfung) sowie `meta` (Objekt mit `title`, `description`). Nur angegebene Felder werden geändert.","example":{"status":"public","domain":"heizung.example.com"}}},"required":["portal_id","data"]},"example":{"portal_id":401,"data":{"status":"public","domain":"heizung.example.com"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"update_portal","stufe":"schreiben","data":{"success":true,"data":{"id":401,"title":"Heizungstausch Ratgeber","slug":"heizungstausch-ratgeber","status":"public","domain":"heizung.example.com","lang":"de","company_id":1234,"template":null,"keyword":{"id":501,"keyword":"heizung austauschen kosten","suggest":"heizung austauschen kosten förderung","main_intent":"informational","lang":"de","search_volume":2400,"cpc":1.85,"competition_level":"MEDIUM","group_id":12,"company_id":1234,"updated_at":"2026-11-09T10:15:32+01:00"},"meta":{"title":"Heizungstausch: Kosten und Ablauf","description":"Alles zum Austausch deiner Heizung."},"updated_at":"2026-11-09T10:15:32+01:00"},"message":"Portal aktualisiert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/delete_portal":{"post":{"tags":["Landingpages"],"operationId":"landingpages__delete_portal","summary":"Portal löschen","description":"Löscht ein Portal deines Unternehmens endgültig. Die Aktion ist nicht umkehrbar; du musst `\"bestaetigt\": true` mitsenden. Die Antwort enthält nur die ID (`portal_id`) und die Meldung „Portal gelöscht“. Ein verknüpftes Template oder Keyword bleibt bestehen. Gehört das Portal nicht zu deinem Unternehmen, kommt „Portal nicht gefunden oder kein Zugriff“.\n\n**Gut zu wissen**\n\n- Der Schlüssel braucht das Recht „löschen“.\n- Prüfe die ID vorher mit `get_portal`; ein gelöschtes Portal lässt sich nicht wiederherstellen.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/delete_portal \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"portal_id\": 401,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/delete_portal\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"portal_id\": 401,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/delete_portal\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"portal_id\": 401,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"portal_id":{"type":"integer","description":"ID des zu löschenden Portals als Ganzzahl. Pflichtangabe; das Portal muss zu deinem Unternehmen gehören und wird unwiderruflich entfernt.","example":401},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["portal_id","bestaetigt"]},"example":{"portal_id":401,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"delete_portal","stufe":"loeschen","data":{"success":true,"data":{"portal_id":401},"message":"Portal gelöscht","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/get_keywords":{"post":{"tags":["Landingpages"],"operationId":"landingpages__get_keywords","summary":"Keywords auflisten","description":"Listet die gespeicherten Keywords deines Unternehmens seitenweise auf, zuletzt geänderte zuerst. Mit `group_id` begrenzt du auf eine Keyword-Gruppe. Die Antwort enthält `items` (je Keyword: `id`, `keyword`, `suggest`, `main_intent`, `lang`, `search_volume`, `cpc`, `competition_level`, `group_id`, `company_id`, `updated_at`), `total`, `limit` und `offset`. Werte, die nicht gepflegt sind, erscheinen als `null` bzw. leer. Standardmäßig kommen 25 Keywords je Antwort. Für eine Suche nach Text nutze `search_keywords`.\n\n**Gut zu wissen**\n\n- Standard für `limit` ist 25 (nicht 20 wie bei Templates und Portalen).\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/get_keywords \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"group_id\": 12,\n    \"limit\": 25,\n    \"offset\": 0\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/get_keywords\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"group_id\": 12,\n        \"limit\": 25,\n        \"offset\": 0,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/get_keywords\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"group_id\": 12,\n      \"limit\": 25,\n      \"offset\": 0\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"group_id":{"type":"integer","description":"Optionaler Filter auf die ID einer Keyword-Gruppe (Ganzzahl). Es werden nur Keywords dieser Gruppe geliefert.","example":12},"limit":{"type":"integer","description":"Maximale Anzahl Keywords je Antwort. Standard 25, Werte unter 1 werden auf 1 angehoben.","example":25},"offset":{"type":"integer","description":"Anzahl der Keywords, die am Anfang übersprungen werden (Standard 0), zum Blättern zusammen mit `total`.","example":0}}},"example":{"group_id":12,"limit":25,"offset":0}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"get_keywords","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":501,"keyword":"heizung austauschen kosten","suggest":"heizung austauschen kosten förderung","main_intent":"informational","lang":"de","search_volume":2400,"cpc":1.85,"competition_level":"MEDIUM","group_id":12,"company_id":1234,"updated_at":"2026-11-09T10:15:32+01:00"}],"total":1,"limit":25,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/search_keywords":{"post":{"tags":["Landingpages"],"operationId":"landingpages__search_keywords","summary":"Keywords suchen","description":"Sucht in deinen gespeicherten Keywords nach einem Text (Teilstring in `keyword` oder `suggest`, Groß-/Kleinschreibung egal) und ergänzt bei Bedarf Vorschläge der Suchmaschinen-Autovervollständigung. Zuerst kommen bis zu `limit` Treffer aus deinem Bestand (Format wie bei `get_keywords`). Sind es weniger als `limit` und ist `include_suggestions` nicht auf `false`, werden die Vorschläge auf Deutsch angehängt – ohne `id` (`null`), ohne Zahlenwerte und mit `\"source\": \"google_suggest\"`. Die Antwort hat nur `items`, keine Seiteneinteilung. Gespeicherte Treffer haben kein Feld `source`.\n\n**Gut zu wissen**\n\n- Vorschläge sind nicht gespeichert; mit `create_keyword` übernimmst du einen davon.\n- Standard für `limit` ist 15.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/search_keywords \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\": \"heizung austauschen\",\n    \"limit\": 5,\n    \"include_suggestions\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/search_keywords\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"query\": \"heizung austauschen\",\n        \"limit\": 5,\n        \"include_suggestions\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/search_keywords\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"query\": \"heizung austauschen\",\n      \"limit\": 5,\n      \"include_suggestions\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Suchtext. Pflichtangabe. Treffer sind Keywords, deren Begriff oder Vorschlagstext diesen Text enthalten; derselbe Text dient als Ausgangspunkt für die Vorschläge.","example":"heizung austauschen"},"limit":{"type":"integer","description":"Maximale Gesamtzahl Einträge (gespeicherte Treffer plus Vorschläge). Standard 15, Werte unter 1 werden auf 1 angehoben.","example":5},"include_suggestions":{"type":"boolean","description":"Wahrheitswert. Bei `true` (Standard) wird die Liste mit Vorschlägen der Autovervollständigung aufgefüllt, solange weniger als `limit` Einträge vorhanden sind; bei `false` kommen nur gespeicherte Keywords.","example":true}},"required":["query"]},"example":{"query":"heizung austauschen","limit":5,"include_suggestions":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"search_keywords","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":501,"keyword":"heizung austauschen kosten","suggest":"heizung austauschen kosten förderung","main_intent":"informational","lang":"de","search_volume":2400,"cpc":1.85,"competition_level":"MEDIUM","group_id":12,"company_id":1234,"updated_at":"2026-11-09T10:15:32+01:00"},{"id":null,"keyword":"heizung austauschen förderung 2026","suggest":"heizung austauschen förderung 2026","search_volume":null,"cpc":null,"competition_level":null,"intent":null,"source":"google_suggest"}]},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/create_keyword":{"post":{"tags":["Landingpages"],"operationId":"landingpages__create_keyword","summary":"Keyword anlegen","description":"Speichert ein neues Keyword für dein Unternehmen. Pflicht ist `keyword`. Optional gibst du Vorschlagstext (`suggest`), Suchvolumen, CPC, Hauptintention (Standard `informational`), Sprache (Standard `de`), Konkurrenzgrad und Gruppe an. Bei Erfolg kommt das Keyword in der Form wie bei `get_keywords` mit der Meldung „Keyword erstellt“ zurück. Suchvolumen und CPC kannst du mit `get_keyword_search_volume` und `get_keyword_cpc` ermitteln. Es wird nicht geprüft, ob derselbe Begriff schon existiert – Dubletten sind möglich.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/create_keyword \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"keyword\": \"heizung austauschen kosten\",\n    \"search_volume\": 2400,\n    \"cpc\": 1.85,\n    \"group_id\": 12,\n    \"competition_level\": \"MEDIUM\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/create_keyword\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"keyword\": \"heizung austauschen kosten\",\n        \"search_volume\": 2400,\n        \"cpc\": 1.85,\n        \"group_id\": 12,\n        \"competition_level\": \"MEDIUM\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/create_keyword\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"keyword\": \"heizung austauschen kosten\",\n      \"search_volume\": 2400,\n      \"cpc\": 1.85,\n      \"group_id\": 12,\n      \"competition_level\": \"MEDIUM\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"keyword":{"type":"string","description":"Der Suchbegriff als Text. Pflichtangabe; HTML-Zeichen werden maskiert.","example":"heizung austauschen kosten"},"suggest":{"type":"string","description":"Optionaler Vorschlagstext, z. B. die ausführlichere Suchanfrage. Standard ist leer."},"search_volume":{"type":"integer","description":"Monatliches Suchvolumen als Ganzzahl. Optional; ohne Angabe bleibt das Feld leer.","example":2400},"cpc":{"type":"number","description":"Durchschnittlicher Klickpreis als Zahl (die Währung wird nicht gespeichert; bei Abfragen wird USD angenommen). Optional.","example":1.85},"main_intent":{"type":"string","description":"Hauptintention des Suchbegriffs als Text, z. B. `informational`, `commercial` oder `transactional`. Standard ist `informational`."},"group_id":{"type":"integer","description":"ID einer Keyword-Gruppe, der das Keyword zugeordnet wird (Ganzzahl). Optional.","example":12},"lang":{"type":"string","description":"Sprachcode des Keywords, z. B. `de`. Standard ist `de`."},"competition_level":{"type":"string","description":"Konkurrenzgrad als Text, z. B. `LOW`, `MEDIUM` oder `HIGH`. Optional; der Wert wird nicht geprüft.","example":"MEDIUM"}},"required":["keyword"]},"example":{"keyword":"heizung austauschen kosten","search_volume":2400,"cpc":1.85,"group_id":12,"competition_level":"MEDIUM"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"create_keyword","stufe":"schreiben","data":{"success":true,"data":{"id":501,"keyword":"heizung austauschen kosten","suggest":"heizung austauschen kosten förderung","main_intent":"informational","lang":"de","search_volume":2400,"cpc":1.85,"competition_level":"MEDIUM","group_id":12,"company_id":1234,"updated_at":"2026-11-09T10:15:32+01:00"},"message":"Keyword erstellt","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/update_keyword":{"post":{"tags":["Landingpages"],"operationId":"landingpages__update_keyword","summary":"Keyword ändern","description":"Ändert Felder eines Keywords deines Unternehmens. In `data` stehen die zu ändernden Felder: `keyword`, `suggest`, `lang`, `main_intent`, `search_volume`, `cpc`, `competition_level`, `group_id`. Nicht genannte Felder bleiben unverändert; ein Wert `null` wird ignoriert. Bei Erfolg kommt das gespeicherte Keyword in der Form wie bei `get_keywords` mit der Meldung „Keyword aktualisiert“ zurück. Gehört das Keyword nicht zu deinem Unternehmen, kommt „Keyword nicht gefunden oder kein Zugriff“. Für `group_id` ohne Gruppe übergib `0`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/update_keyword \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"keyword_id\": 501,\n    \"data\": {\n        \"main_intent\": \"commercial\",\n        \"competition_level\": \"HIGH\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/update_keyword\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"keyword_id\": 501,\n        \"data\": {\n            \"main_intent\": \"commercial\",\n            \"competition_level\": \"HIGH\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/update_keyword\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"keyword_id\": 501,\n      \"data\": {\n          \"main_intent\": \"commercial\",\n          \"competition_level\": \"HIGH\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"keyword_id":{"type":"integer","description":"ID des zu ändernden Keywords als Ganzzahl. Pflichtangabe; das Keyword muss zu deinem Unternehmen gehören.","example":501},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern. Erlaubt: `keyword`, `suggest`, `lang`, `main_intent`, `competition_level` (Texte), `search_volume` (Ganzzahl), `cpc` (Zahl) und `group_id` (Ganzzahl, `0` löst die Gruppe). Nur angegebene Felder werden geändert.","example":{"main_intent":"commercial","competition_level":"HIGH"}}},"required":["keyword_id","data"]},"example":{"keyword_id":501,"data":{"main_intent":"commercial","competition_level":"HIGH"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"update_keyword","stufe":"schreiben","data":{"success":true,"data":{"id":501,"keyword":"heizung austauschen kosten","suggest":"heizung austauschen kosten förderung","main_intent":"commercial","lang":"de","search_volume":2400,"cpc":1.85,"competition_level":"HIGH","group_id":12,"company_id":1234,"updated_at":"2026-11-09T10:15:32+01:00"},"message":"Keyword aktualisiert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/delete_keyword":{"post":{"tags":["Landingpages"],"operationId":"landingpages__delete_keyword","summary":"Keyword löschen","description":"Löscht ein Keyword deines Unternehmens endgültig. Die Aktion ist nicht umkehrbar; du musst `\"bestaetigt\": true` mitsenden. Die Antwort enthält nur die ID (`keyword_id`) und die Meldung „Keyword gelöscht“. Portale, die dieses Keyword über `keyword_id` verwenden, werden nicht angepasst – setze dort vorher `keyword_id` auf `0` (`update_portal`). Gehört das Keyword nicht zu deinem Unternehmen, kommt „Keyword nicht gefunden oder kein Zugriff“.\n\n**Gut zu wissen**\n\n- Der Schlüssel braucht das Recht „löschen“.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/delete_keyword \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"keyword_id\": 501,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/delete_keyword\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"keyword_id\": 501,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/delete_keyword\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"keyword_id\": 501,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"keyword_id":{"type":"integer","description":"ID des zu löschenden Keywords als Ganzzahl. Pflichtangabe; das Keyword muss zu deinem Unternehmen gehören und wird unwiderruflich entfernt.","example":501},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["keyword_id","bestaetigt"]},"example":{"keyword_id":501,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"delete_keyword","stufe":"loeschen","data":{"success":true,"data":{"keyword_id":501},"message":"Keyword gelöscht","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/get_keyword_search_volume":{"post":{"tags":["Landingpages"],"operationId":"landingpages__get_keyword_search_volume","summary":"Suchvolumen ermitteln","description":"Liefert das monatliche Suchvolumen zu einem Suchbegriff. Gibst du `keyword_id` an und ist am gespeicherten Keyword ein Suchvolumen hinterlegt, kommt dieser Wert sofort zurück (`keyword`, `search_volume`, `source: \"pimcore\"`). Sonst wird der Begriff (aus `keyword` oder dem Text des gespeicherten Keywords) live beim Keyword-Dienst abgefragt; die Antwort enthält dann `keyword`, `search_volume`, `location` und die Rohdaten in `dataforseo_raw` (Liste der Vorschläge des Dienstes). `search_volume` wird dabei aus dem ersten Rohdaten-Eintrag übernommen (aus `keyword_info`, ersatzweise von oberster Ebene) und ist `null`, wenn dort kein Wert steht. Liefert der Dienst nichts, kommt `success: false` („Keine Daten für Suchvolumen verfügbar“). Ohne `keyword` und ohne `keyword_id` kommt „Keyword oder Keyword-ID erforderlich“.\n\n**Gut zu wissen**\n\n- Die Live-Abfrage kann einige Sekunden dauern.\n- Das Ergebnis wird nicht automatisch am Keyword gespeichert – übernimm es mit `update_keyword`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/get_keyword_search_volume \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"keyword_id\": 501,\n    \"location\": \"germany\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/get_keyword_search_volume\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"keyword_id\": 501,\n        \"location\": \"germany\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/get_keyword_search_volume\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"keyword_id\": 501,\n      \"location\": \"germany\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"keyword":{"type":"string","description":"Der Suchbegriff als Text. Wird verwendet, wenn `keyword_id` fehlt oder am gespeicherten Keyword kein Suchvolumen hinterlegt ist. Eines der beiden Felder ist erforderlich."},"keyword_id":{"type":"integer","description":"Alternativ zu `keyword`: ID eines gespeicherten Keywords deines Unternehmens. Ist dort ein Suchvolumen eingetragen, wird es ohne externe Abfrage zurückgegeben.","example":501},"location":{"type":"string","description":"Region der Abfrage. `germany` (Standard) fragt Deutschland ab, `dubai` oder `uae` die Vereinigten Arabischen Emirate; jeder andere Wert führt beide Abfragen zusammen.","example":"germany"}}},"example":{"keyword_id":501,"location":"germany"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"get_keyword_search_volume","stufe":"lesen","data":{"success":true,"data":{"keyword":"heizung austauschen kosten","search_volume":2400,"source":"pimcore"},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingpages/get_keyword_cpc":{"post":{"tags":["Landingpages"],"operationId":"landingpages__get_keyword_cpc","summary":"Klickpreis (CPC) ermitteln","description":"Liefert den durchschnittlichen Klickpreis (CPC) zu einem Suchbegriff. Gibst du `keyword_id` an und ist am gespeicherten Keyword ein CPC hinterlegt, kommt dieser sofort zurück (`keyword`, `cpc` als Zahl, `currency: \"USD\"`, `source: \"pimcore\"`). Sonst wird der Begriff live beim Keyword-Dienst abgefragt; die Antwort enthält dann `keyword`, `cpc`, `currency` (aus den Daten, sonst USD), `location` und die Rohdaten in `dataforseo_raw`. `cpc` stammt aus dem ersten Rohdaten-Eintrag (`keyword_info`, ersatzweise oberste Ebene bzw. `cpc_min`) und ist `null`, wenn dort kein Wert steht. Liefert der Dienst nichts, kommt `success: false` („Keine CPC-Daten verfügbar“); ohne `keyword` und `keyword_id` kommt „Keyword oder Keyword-ID erforderlich“.\n\n**Gut zu wissen**\n\n- Der gespeicherte Wert wird immer als USD ausgewiesen.\n- Das Ergebnis wird nicht automatisch am Keyword gespeichert.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingpages/get_keyword_cpc \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"keyword\": \"heizung austauschen kosten\",\n    \"location\": \"germany\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingpages/get_keyword_cpc\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"keyword\": \"heizung austauschen kosten\",\n        \"location\": \"germany\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingpages/get_keyword_cpc\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"keyword\": \"heizung austauschen kosten\",\n      \"location\": \"germany\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"keyword":{"type":"string","description":"Der Suchbegriff als Text. Wird verwendet, wenn `keyword_id` fehlt oder am gespeicherten Keyword kein CPC hinterlegt ist. Eines der beiden Felder ist erforderlich.","example":"heizung austauschen kosten"},"keyword_id":{"type":"integer","description":"Alternativ zu `keyword`: ID eines gespeicherten Keywords deines Unternehmens. Ist dort ein CPC eingetragen, wird er ohne externe Abfrage zurückgegeben."},"location":{"type":"string","description":"Region der Abfrage. `germany` (Standard) fragt Deutschland ab, `dubai` oder `uae` die Vereinigten Arabischen Emirate; jeder andere Wert führt beide Abfragen zusammen.","example":"germany"}}},"example":{"keyword":"heizung austauschen kosten","location":"germany"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingpages","werkzeug":"get_keyword_cpc","stufe":"lesen","data":{"success":true,"data":{"keyword":"heizung austauschen kosten","cpc":1.85,"currency":"USD","source":"pimcore"},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingcards/get_landingcards":{"post":{"tags":["Landingcards"],"operationId":"landingcards__get_landingcards","summary":"Landingcards auflisten","description":"Listet die Landingcards deines Unternehmens seitenweise auf, zuletzt geänderte zuerst. Berücksichtigt werden Templates der Variante `landingcard`, die einem Mitglied zugeordnet sind; Karten, deren Mitglied nicht zu deinem Unternehmen gehört, werden ausgelassen. Trotz der Funktionsbeschreibung („aktive“ Karten) erscheinen auch ausgeschaltete Karten – erkennbar am Feld `status` (`active` oder `inactive`). Die Antwort enthält `items` (je Karte der vollständige Kartendatensatz, siehe `get_landingcard`), `total` (Zahl der Templates vor dem Auslassen fremder Mitglieder), `limit` und `offset`. Standard sind 25 Karten je Antwort.\n\n**Gut zu wissen**\n\n- `total` kann größer sein als die Zahl der gelieferten `items`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingcards/get_landingcards \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"limit\": 10,\n    \"offset\": 0\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingcards/get_landingcards\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"limit\": 10,\n        \"offset\": 0,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingcards/get_landingcards\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"limit\": 10,\n      \"offset\": 0\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Optionaler Filter auf den Zustand der Karte: `active` zeigt nur eingeschaltete Karten, jeder andere Wert (z. B. `draft`) nur ausgeschaltete. Der Zustand wird am Mitglied geführt (Einschaltung der Landingcard). Mit Filter ist `total` die Zahl der Treffer dieser Seite."},"limit":{"type":"integer","description":"Maximale Anzahl Karten je Antwort. Standard 25, Werte unter 1 werden auf 1 angehoben.","example":10},"offset":{"type":"integer","description":"Anzahl der Karten, die am Anfang übersprungen werden (Standard 0), für das Blättern zusammen mit `total`.","example":0}}},"example":{"limit":10,"offset":0}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingcards","werkzeug":"get_landingcards","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":6201,"templateId":6201,"memberId":7788,"name":"Mira Beispiel","firstName":"Mira","lastName":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550100","mobile":"+49 170 5550101","title":"Vertriebsleiterin","department":"Vertrieb","bio":"Ich begleite Handwerksbetriebe bei der Digitalisierung.","skills":"Vertrieb, Beratung, Digitalisierung","cardUrl":"https://landingcard.app/card/beispiel-gmbh/mira-beispiel/default","status":"active","profilePicture":"/landingcards/mira.jpg","chatAgentId":null,"chatagentid":null,"bio_value_proposition":"Mehr Anfragen ohne mehr Büroarbeit","bio_tagline":"Digital einfach machen","bio_availability_status":"Mo–Fr erreichbar","bio_contact_priority":"E-Mail","bio_cta_primary":"Termin vereinbaren","bio_cta_secondary":"Kontakt speichern","bio_social_links_tier1":"[]","bio_social_links_tier2":"[]","bio_social_links_tier3":"[]","bio_social_follower_counts":"{}","bio_offers":"[]","faq_headline":"","faq_subheadline":"","section_faq":false,"section_reviews":true,"reviews_max_count":3,"reviews_sort_order":"newest","colorPrimary":"#1d4ed8","colorSecondary":"#f59e0b","colorAccent":"","header_bg_color":"","header_font_color":"","primaryFont":"Inter","headingFont":"Inter","body_color":"#1f2937","background_color":"#f8fafc","section_heroshot":true,"heroshot_headline":"Hallo, ich bin Mira","heroshot_subheadline":"Deine Ansprechpartnerin für Digitalisierung","heroshot_backgroundcolor":"#F3F4F6","heroshot_color":"#000000","heroshot_backgroundimage":null,"heroshot_backgroundimagelayer_color":null,"heroshot_backgroundimagelayer_value":null,"heroshot_backgroundimagelayer_config":"","heroshot_modal_image_size":"large","hero_gradient_type":"color","hero_gradient_start":"#3B82F6","hero_gradient_end":"#1D4ED8","section_offers":true,"section_products":false,"section_product_ids":"[]","section_product_layout":"grid","section_product_show_price":false,"products":[],"bio_products":"[]","section_order":"","section_links":true,"section_footer":true,"offers_title":"","offers_subtitle":"","offers_backgroundColor":"#FFFFFF","offers_textColor":"#000000","links_backgroundColor":"#F8FAFC","links_title":"","links_subtitle":"","footer_content":"","footer_backgroundColor":"#F8FAFC","footer_textColor":"#374151","limbic_styleguide_id":null,"linguistic_id":null,"storyfilter_id":null,"social":{"linkedin":"https://www.linkedin.com/in/mira-beispiel","xing":null,"twitter":null,"facebook":null,"instagram":null,"tiktok":null,"website":"https://www.example.com"},"company":{"id":1234,"name":"Beispiel GmbH","companyname":"Beispiel GmbH","slug":"beispiel-gmbh","logo":"/logos/beispiel.png","website":"https://www.example.com","phone":"+49 211 5550000","email":"info@example.com","street":"Musterstraße 1","zip":"40210","city":"Düsseldorf","state":null,"country":"DE","ceotitel":null,"ceofirstname":null,"ceolastname":null,"ceoposition":null,"impressum":null,"dataprotection":null,"agb":null,"refcode":null,"primarycolor":"#1d4ed8","secondarycolor":"#f59e0b","portalId":null},"metaTitle":"Mira Beispiel – Vertrieb bei der Beispiel GmbH","metaDescription":"Digitale Visitenkarte von Mira Beispiel.","stats":{"views":0,"qrScans":0},"created_at":1783579200,"updated_at":1783665600,"faq_1_question":"","faq_1_answer":"","faq_2_question":"","faq_2_answer":"","faq_3_question":"","faq_3_answer":"","faq_4_question":"","faq_4_answer":"","faq_5_question":"","faq_5_answer":"","faq_6_question":"","faq_6_answer":"","faq_7_question":"","faq_7_answer":"","faq_8_question":"","faq_8_answer":"","faq_9_question":"","faq_9_answer":"","faq_10_question":"","faq_10_answer":""}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingcards/get_landingcard":{"post":{"tags":["Landingcards"],"operationId":"landingcards__get_landingcard","summary":"Landingcard abrufen","description":"Gibt den vollständigen Datensatz einer Landingcard zurück. Mit `template_id` wird die Karte dieses Templates gelesen (zusammen mit dem Mitglied, dem das Template gehört); ohne `template_id` die Karte des Mitglieds, dem dein Schlüssel zugeordnet ist. Der Datensatz enthält u. a. `templateId`, `memberId`, Name und Kontaktdaten, `cardUrl`, `status` (`active` oder `inactive`, abhängig vom Archiv-Kennzeichen des Templates), die Textfelder `bio_*`, Farben und Schriften, Abschnitts-Schalter (`section_*`), Kopfbereich (`heroshot_*`), `social`, `company` (Name, Anschrift, Logo …), `metaTitle`/`metaDescription`, `stats` (derzeit immer 0), `created_at` und `updated_at` (Unix-Zeitstempel) sowie die Felder `faq_1_question` bis `faq_10_answer`. Gibt es keine Karte, kommt `success: false` mit „LandingCard nicht gefunden“; eine fremde Template-ID ergibt „Template nicht gefunden oder kein Zugriff“.\n\n**Gut zu wissen**\n\n- Die Karten-URL im Feld `cardUrl` zeigt auf `landingcard.app`.\n- Der Wert `stats` ist ein fester Platzhalter ohne echte Messwerte.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingcards/get_landingcard \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 6201\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingcards/get_landingcard\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 6201,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingcards/get_landingcard\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 6201\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"ID des Landingcard-Templates als Ganzzahl. Optional; ohne Angabe wird die Landingcard des Mitglieds verwendet, dem dein Schlüssel zugeordnet ist. Das Template muss zu deinem Unternehmen gehören.","example":6201}}},"example":{"template_id":6201}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingcards","werkzeug":"get_landingcard","stufe":"lesen","data":{"success":true,"data":{"id":6201,"templateId":6201,"memberId":7788,"name":"Mira Beispiel","firstName":"Mira","lastName":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550100","mobile":"+49 170 5550101","title":"Vertriebsleiterin","department":"Vertrieb","bio":"Ich begleite Handwerksbetriebe bei der Digitalisierung.","skills":"Vertrieb, Beratung, Digitalisierung","cardUrl":"https://landingcard.app/card/beispiel-gmbh/mira-beispiel/default","status":"active","profilePicture":"/landingcards/mira.jpg","chatAgentId":null,"chatagentid":null,"bio_value_proposition":"Mehr Anfragen ohne mehr Büroarbeit","bio_tagline":"Digital einfach machen","bio_availability_status":"Mo–Fr erreichbar","bio_contact_priority":"E-Mail","bio_cta_primary":"Termin vereinbaren","bio_cta_secondary":"Kontakt speichern","bio_social_links_tier1":"[]","bio_social_links_tier2":"[]","bio_social_links_tier3":"[]","bio_social_follower_counts":"{}","bio_offers":"[]","faq_headline":"","faq_subheadline":"","section_faq":false,"section_reviews":true,"reviews_max_count":3,"reviews_sort_order":"newest","colorPrimary":"#1d4ed8","colorSecondary":"#f59e0b","colorAccent":"","header_bg_color":"","header_font_color":"","primaryFont":"Inter","headingFont":"Inter","body_color":"#1f2937","background_color":"#f8fafc","section_heroshot":true,"heroshot_headline":"Hallo, ich bin Mira","heroshot_subheadline":"Deine Ansprechpartnerin für Digitalisierung","heroshot_backgroundcolor":"#F3F4F6","heroshot_color":"#000000","heroshot_backgroundimage":null,"heroshot_backgroundimagelayer_color":null,"heroshot_backgroundimagelayer_value":null,"heroshot_backgroundimagelayer_config":"","heroshot_modal_image_size":"large","hero_gradient_type":"color","hero_gradient_start":"#3B82F6","hero_gradient_end":"#1D4ED8","section_offers":true,"section_products":false,"section_product_ids":"[]","section_product_layout":"grid","section_product_show_price":false,"products":[],"bio_products":"[]","section_order":"","section_links":true,"section_footer":true,"offers_title":"","offers_subtitle":"","offers_backgroundColor":"#FFFFFF","offers_textColor":"#000000","links_backgroundColor":"#F8FAFC","links_title":"","links_subtitle":"","footer_content":"","footer_backgroundColor":"#F8FAFC","footer_textColor":"#374151","limbic_styleguide_id":null,"linguistic_id":null,"storyfilter_id":null,"social":{"linkedin":"https://www.linkedin.com/in/mira-beispiel","xing":null,"twitter":null,"facebook":null,"instagram":null,"tiktok":null,"website":"https://www.example.com"},"company":{"id":1234,"name":"Beispiel GmbH","companyname":"Beispiel GmbH","slug":"beispiel-gmbh","logo":"/logos/beispiel.png","website":"https://www.example.com","phone":"+49 211 5550000","email":"info@example.com","street":"Musterstraße 1","zip":"40210","city":"Düsseldorf","state":null,"country":"DE","ceotitel":null,"ceofirstname":null,"ceolastname":null,"ceoposition":null,"impressum":null,"dataprotection":null,"agb":null,"refcode":null,"primarycolor":"#1d4ed8","secondarycolor":"#f59e0b","portalId":null},"metaTitle":"Mira Beispiel – Vertrieb bei der Beispiel GmbH","metaDescription":"Digitale Visitenkarte von Mira Beispiel.","stats":{"views":0,"qrScans":0},"created_at":1783579200,"updated_at":1783665600,"faq_1_question":"","faq_1_answer":"","faq_2_question":"","faq_2_answer":"","faq_3_question":"","faq_3_answer":"","faq_4_question":"","faq_4_answer":"","faq_5_question":"","faq_5_answer":"","faq_6_question":"","faq_6_answer":"","faq_7_question":"","faq_7_answer":"","faq_8_question":"","faq_8_answer":"","faq_9_question":"","faq_9_answer":"","faq_10_question":"","faq_10_answer":""},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingcards/create_landingcard":{"post":{"tags":["Landingcards"],"operationId":"landingcards__create_landingcard","summary":"Landingcard anlegen","description":"Legt die Landingcard für das Mitglied an, dem dein Schlüssel zugeordnet ist. Zuerst werden die Angaben aus `profile` am Mitglied gespeichert; dabei wird die Landingcard des Mitglieds eingeschaltet, bei Bedarf ein eindeutiger Mitglieds-Slug aus dem Namen gebildet und ein QR-Code erzeugt (das geschieht auch ohne `profile`). Danach wird das Landingcard-Template des Mitglieds gesucht: Gibt es eines, werden die Angaben aus `template` darauf angewendet; sonst wird ein neues Template „<Vorname> <Nachname> - LandingCard“ mit Standardfarben angelegt. Bei Erfolg kommt der Kartendatensatz (siehe `get_landingcard`) mit der Meldung „LandingCard erstellt“ zurück.\n\n**Gut zu wissen**\n\n- Es wird immer die Karte des Mitglieds angelegt, zu dem der Schlüssel gehört – für andere Mitglieder nutze `bulk_create_landingcards`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingcards/create_landingcard \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"profile\": {\n        \"title\": \"Vertriebsleiterin\",\n        \"phone\": \"+49 211 5550100\",\n        \"bio\": \"Ich begleite Handwerksbetriebe bei der Digitalisierung.\",\n        \"website\": \"https://www.example.com\"\n    },\n    \"template\": {\n        \"colorPrimary\": \"#1d4ed8\",\n        \"bio_tagline\": \"Digital einfach machen\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingcards/create_landingcard\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"profile\": {\n            \"title\": \"Vertriebsleiterin\",\n            \"phone\": \"+49 211 5550100\",\n            \"bio\": \"Ich begleite Handwerksbetriebe bei der Digitalisierung.\",\n            \"website\": \"https://www.example.com\",\n        },\n        \"template\": {\n            \"colorPrimary\": \"#1d4ed8\",\n            \"bio_tagline\": \"Digital einfach machen\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingcards/create_landingcard\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"profile\": {\n          \"title\": \"Vertriebsleiterin\",\n          \"phone\": \"+49 211 5550100\",\n          \"bio\": \"Ich begleite Handwerksbetriebe bei der Digitalisierung.\",\n          \"website\": \"https://www.example.com\"\n      },\n      \"template\": {\n          \"colorPrimary\": \"#1d4ed8\",\n          \"bio_tagline\": \"Digital einfach machen\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"profile":{"type":"object","description":"Objekt mit Profildaten, die am Mitglied gespeichert werden. Erlaubte Schlüssel (alle Texte): `title` (Berufsbezeichnung), `phone`, `mobile`, `bio`, `linkedin`, `twitter`, `facebook`, `instagram`, `xing`, `tiktok`, `website`, `skills` und `department`. Nicht genannte Felder bleiben unverändert. Das Profil gehört immer zu dem Mitglied, dem dein Schlüssel zugeordnet ist (bei Angabe von `template_id`: zu dem Mitglied dieses Templates).","example":{"title":"Vertriebsleiterin","phone":"+49 211 5550100","bio":"Ich begleite Handwerksbetriebe bei der Digitalisierung.","website":"https://www.example.com"}},"template":{"type":"object","description":"Objekt mit Gestaltungs- und Textfeldern des Landingcard-Templates. Erlaubte Schlüssel: `bio_value_proposition`, `bio_tagline`, `bio_availability_status`, `bio_contact_priority` (im Datenmodell `WhatsApp`, `E-Mail` oder `Telefon`), `bio_cta_primary`, `bio_cta_secondary`, `bio_social_links_tier1`, `bio_social_links_tier2`, `bio_social_links_tier3`, `bio_social_follower_counts` (diese vier als JSON-Text, z. B. `\"[]\"`), `colorPrimary`, `colorSecondary`, `primaryFont`, `headingFont`, `body_color`, `body_backgroundcolor`, `section_heroshot` (Wahrheitswert), `heroshot_headline`, `heroshot_subheadline`, `heroshot_backgroundcolor`, `heroshot_color`, `metaTitle` und `metaDescription`. Farben sind Texte wie `#1d4ed8`. Unbekannte Schlüssel werden ohne Fehlermeldung übergangen.","example":{"colorPrimary":"#1d4ed8","bio_tagline":"Digital einfach machen"}}}},"example":{"profile":{"title":"Vertriebsleiterin","phone":"+49 211 5550100","bio":"Ich begleite Handwerksbetriebe bei der Digitalisierung.","website":"https://www.example.com"},"template":{"colorPrimary":"#1d4ed8","bio_tagline":"Digital einfach machen"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingcards","werkzeug":"create_landingcard","stufe":"schreiben","data":{"success":true,"data":{"id":6201,"templateId":6201,"memberId":7788,"name":"Mira Beispiel","firstName":"Mira","lastName":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550100","mobile":"+49 170 5550101","title":"Vertriebsleiterin","department":"Vertrieb","bio":"Ich begleite Handwerksbetriebe bei der Digitalisierung.","skills":"Vertrieb, Beratung, Digitalisierung","cardUrl":"https://landingcard.app/card/beispiel-gmbh/mira-beispiel/default","status":"active","profilePicture":"/landingcards/mira.jpg","chatAgentId":null,"chatagentid":null,"bio_value_proposition":"Mehr Anfragen ohne mehr Büroarbeit","bio_tagline":"Digital einfach machen","bio_availability_status":"Mo–Fr erreichbar","bio_contact_priority":"E-Mail","bio_cta_primary":"Termin vereinbaren","bio_cta_secondary":"Kontakt speichern","bio_social_links_tier1":"[]","bio_social_links_tier2":"[]","bio_social_links_tier3":"[]","bio_social_follower_counts":"{}","bio_offers":"[]","faq_headline":"","faq_subheadline":"","section_faq":false,"section_reviews":true,"reviews_max_count":3,"reviews_sort_order":"newest","colorPrimary":"#1d4ed8","colorSecondary":"#f59e0b","colorAccent":"","header_bg_color":"","header_font_color":"","primaryFont":"Inter","headingFont":"Inter","body_color":"#1f2937","background_color":"#f8fafc","section_heroshot":true,"heroshot_headline":"Hallo, ich bin Mira","heroshot_subheadline":"Deine Ansprechpartnerin für Digitalisierung","heroshot_backgroundcolor":"#F3F4F6","heroshot_color":"#000000","heroshot_backgroundimage":null,"heroshot_backgroundimagelayer_color":null,"heroshot_backgroundimagelayer_value":null,"heroshot_backgroundimagelayer_config":"","heroshot_modal_image_size":"large","hero_gradient_type":"color","hero_gradient_start":"#3B82F6","hero_gradient_end":"#1D4ED8","section_offers":true,"section_products":false,"section_product_ids":"[]","section_product_layout":"grid","section_product_show_price":false,"products":[],"bio_products":"[]","section_order":"","section_links":true,"section_footer":true,"offers_title":"","offers_subtitle":"","offers_backgroundColor":"#FFFFFF","offers_textColor":"#000000","links_backgroundColor":"#F8FAFC","links_title":"","links_subtitle":"","footer_content":"","footer_backgroundColor":"#F8FAFC","footer_textColor":"#374151","limbic_styleguide_id":null,"linguistic_id":null,"storyfilter_id":null,"social":{"linkedin":"https://www.linkedin.com/in/mira-beispiel","xing":null,"twitter":null,"facebook":null,"instagram":null,"tiktok":null,"website":"https://www.example.com"},"company":{"id":1234,"name":"Beispiel GmbH","companyname":"Beispiel GmbH","slug":"beispiel-gmbh","logo":"/logos/beispiel.png","website":"https://www.example.com","phone":"+49 211 5550000","email":"info@example.com","street":"Musterstraße 1","zip":"40210","city":"Düsseldorf","state":null,"country":"DE","ceotitel":null,"ceofirstname":null,"ceolastname":null,"ceoposition":null,"impressum":null,"dataprotection":null,"agb":null,"refcode":null,"primarycolor":"#1d4ed8","secondarycolor":"#f59e0b","portalId":null},"metaTitle":"Mira Beispiel – Vertrieb bei der Beispiel GmbH","metaDescription":"Digitale Visitenkarte von Mira Beispiel.","stats":{"views":0,"qrScans":0},"created_at":1783579200,"updated_at":1783665600,"faq_1_question":"","faq_1_answer":"","faq_2_question":"","faq_2_answer":"","faq_3_question":"","faq_3_answer":"","faq_4_question":"","faq_4_answer":"","faq_5_question":"","faq_5_answer":"","faq_6_question":"","faq_6_answer":"","faq_7_question":"","faq_7_answer":"","faq_8_question":"","faq_8_answer":"","faq_9_question":"","faq_9_answer":"","faq_10_question":"","faq_10_answer":""},"message":"LandingCard erstellt","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingcards/update_landingcard":{"post":{"tags":["Landingcards"],"operationId":"landingcards__update_landingcard","summary":"Landingcard ändern","description":"Ändert Profil und/oder Gestaltung einer vorhandenen Landingcard. Die Karte wird über `template_id` bestimmt, ohne Angabe über das Mitglied, dem dein Schlüssel zugeordnet ist. Angaben in `profile` werden am Mitglied der Karte gespeichert (dabei wird die Landingcard des Mitglieds eingeschaltet und bei Bedarf Slug und QR-Code erzeugt); Angaben in `template` werden auf das Template angewendet. Ohne beides wird nur der aktuelle Stand zurückgegeben. Bei Erfolg kommt der Kartendatensatz (siehe `get_landingcard`) mit der Meldung „LandingCard aktualisiert“. Gibt es keine Karte, kommt `success: false` mit „LandingCard nicht gefunden“.\n\n**Gut zu wissen**\n\n- Ein Profil-Update schaltet die Landingcard des Mitglieds ein, auch wenn sie ausgeschaltet war.\n- Unbekannte Schlüssel in `template` werden stillschweigend übergangen; die Antwort enthält immer den gespeicherten Stand – prüfe damit, was übernommen wurde.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingcards/update_landingcard \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 6201,\n    \"template\": {\n        \"bio_tagline\": \"Digitalisierung ohne Kopfschmerzen\",\n        \"colorSecondary\": \"#f59e0b\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingcards/update_landingcard\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 6201,\n        \"template\": {\n            \"bio_tagline\": \"Digitalisierung ohne Kopfschmerzen\",\n            \"colorSecondary\": \"#f59e0b\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingcards/update_landingcard\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 6201,\n      \"template\": {\n          \"bio_tagline\": \"Digitalisierung ohne Kopfschmerzen\",\n          \"colorSecondary\": \"#f59e0b\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"ID des Landingcard-Templates als Ganzzahl. Optional; ohne Angabe wird die Landingcard des Mitglieds verwendet, dem dein Schlüssel zugeordnet ist. Das Template muss zu deinem Unternehmen gehören.","example":6201},"profile":{"type":"object","description":"Objekt mit Profildaten, die am Mitglied gespeichert werden. Erlaubte Schlüssel (alle Texte): `title` (Berufsbezeichnung), `phone`, `mobile`, `bio`, `linkedin`, `twitter`, `facebook`, `instagram`, `xing`, `tiktok`, `website`, `skills` und `department`. Nicht genannte Felder bleiben unverändert. Das Profil gehört immer zu dem Mitglied, dem dein Schlüssel zugeordnet ist (bei Angabe von `template_id`: zu dem Mitglied dieses Templates)."},"template":{"type":"object","description":"Objekt mit Gestaltungs- und Textfeldern des Landingcard-Templates. Erlaubte Schlüssel: `bio_value_proposition`, `bio_tagline`, `bio_availability_status`, `bio_contact_priority` (im Datenmodell `WhatsApp`, `E-Mail` oder `Telefon`), `bio_cta_primary`, `bio_cta_secondary`, `bio_social_links_tier1`, `bio_social_links_tier2`, `bio_social_links_tier3`, `bio_social_follower_counts` (diese vier als JSON-Text, z. B. `\"[]\"`), `colorPrimary`, `colorSecondary`, `primaryFont`, `headingFont`, `body_color`, `body_backgroundcolor`, `section_heroshot` (Wahrheitswert), `heroshot_headline`, `heroshot_subheadline`, `heroshot_backgroundcolor`, `heroshot_color`, `metaTitle` und `metaDescription`. Farben sind Texte wie `#1d4ed8`. Unbekannte Schlüssel werden ohne Fehlermeldung übergangen.","example":{"bio_tagline":"Digitalisierung ohne Kopfschmerzen","colorSecondary":"#f59e0b"}}}},"example":{"template_id":6201,"template":{"bio_tagline":"Digitalisierung ohne Kopfschmerzen","colorSecondary":"#f59e0b"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingcards","werkzeug":"update_landingcard","stufe":"schreiben","data":{"success":true,"data":{"id":6201,"templateId":6201,"memberId":7788,"name":"Mira Beispiel","firstName":"Mira","lastName":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550100","mobile":"+49 170 5550101","title":"Vertriebsleiterin","department":"Vertrieb","bio":"Ich begleite Handwerksbetriebe bei der Digitalisierung.","skills":"Vertrieb, Beratung, Digitalisierung","cardUrl":"https://landingcard.app/card/beispiel-gmbh/mira-beispiel/default","status":"active","profilePicture":"/landingcards/mira.jpg","chatAgentId":null,"chatagentid":null,"bio_value_proposition":"Mehr Anfragen ohne mehr Büroarbeit","bio_tagline":"Digitalisierung ohne Kopfschmerzen","bio_availability_status":"Mo–Fr erreichbar","bio_contact_priority":"E-Mail","bio_cta_primary":"Termin vereinbaren","bio_cta_secondary":"Kontakt speichern","bio_social_links_tier1":"[]","bio_social_links_tier2":"[]","bio_social_links_tier3":"[]","bio_social_follower_counts":"{}","bio_offers":"[]","faq_headline":"","faq_subheadline":"","section_faq":false,"section_reviews":true,"reviews_max_count":3,"reviews_sort_order":"newest","colorPrimary":"#1d4ed8","colorSecondary":"#f59e0b","colorAccent":"","header_bg_color":"","header_font_color":"","primaryFont":"Inter","headingFont":"Inter","body_color":"#1f2937","background_color":"#f8fafc","section_heroshot":true,"heroshot_headline":"Hallo, ich bin Mira","heroshot_subheadline":"Deine Ansprechpartnerin für Digitalisierung","heroshot_backgroundcolor":"#F3F4F6","heroshot_color":"#000000","heroshot_backgroundimage":null,"heroshot_backgroundimagelayer_color":null,"heroshot_backgroundimagelayer_value":null,"heroshot_backgroundimagelayer_config":"","heroshot_modal_image_size":"large","hero_gradient_type":"color","hero_gradient_start":"#3B82F6","hero_gradient_end":"#1D4ED8","section_offers":true,"section_products":false,"section_product_ids":"[]","section_product_layout":"grid","section_product_show_price":false,"products":[],"bio_products":"[]","section_order":"","section_links":true,"section_footer":true,"offers_title":"","offers_subtitle":"","offers_backgroundColor":"#FFFFFF","offers_textColor":"#000000","links_backgroundColor":"#F8FAFC","links_title":"","links_subtitle":"","footer_content":"","footer_backgroundColor":"#F8FAFC","footer_textColor":"#374151","limbic_styleguide_id":null,"linguistic_id":null,"storyfilter_id":null,"social":{"linkedin":"https://www.linkedin.com/in/mira-beispiel","xing":null,"twitter":null,"facebook":null,"instagram":null,"tiktok":null,"website":"https://www.example.com"},"company":{"id":1234,"name":"Beispiel GmbH","companyname":"Beispiel GmbH","slug":"beispiel-gmbh","logo":"/logos/beispiel.png","website":"https://www.example.com","phone":"+49 211 5550000","email":"info@example.com","street":"Musterstraße 1","zip":"40210","city":"Düsseldorf","state":null,"country":"DE","ceotitel":null,"ceofirstname":null,"ceolastname":null,"ceoposition":null,"impressum":null,"dataprotection":null,"agb":null,"refcode":null,"primarycolor":"#1d4ed8","secondarycolor":"#f59e0b","portalId":null},"metaTitle":"Mira Beispiel – Vertrieb bei der Beispiel GmbH","metaDescription":"Digitale Visitenkarte von Mira Beispiel.","stats":{"views":0,"qrScans":0},"created_at":1783579200,"updated_at":1783665600,"faq_1_question":"","faq_1_answer":"","faq_2_question":"","faq_2_answer":"","faq_3_question":"","faq_3_answer":"","faq_4_question":"","faq_4_answer":"","faq_5_question":"","faq_5_answer":"","faq_6_question":"","faq_6_answer":"","faq_7_question":"","faq_7_answer":"","faq_8_question":"","faq_8_answer":"","faq_9_question":"","faq_9_answer":"","faq_10_question":"","faq_10_answer":""},"message":"LandingCard aktualisiert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingcards/delete_landingcard":{"post":{"tags":["Landingcards"],"operationId":"landingcards__delete_landingcard","summary":"Landingcard deaktivieren oder löschen","description":"Schaltet die Landingcard eines Mitglieds ab und entfernt auf Wunsch ihr Template. Die Karte wird über `template_id` bestimmt, ohne Angabe über das Mitglied, dem dein Schlüssel zugeordnet ist. Zuerst wird die Landingcard am Mitglied ausgeschaltet. Mit `remove_template: true` wird anschließend das Template endgültig gelöscht – das ist nicht umkehrbar, deshalb ist `\"bestaetigt\": true` im Körper Pflicht. Ohne `remove_template` soll das Template als archiviert markiert werden. Die Antwort enthält `member_id` und `template_id` (des gelöschten bzw. archivierten Templates) und die Meldung „LandingCard deaktiviert“.\n\n**Gut zu wissen**\n\n- Der Schlüssel braucht das Recht „löschen“.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingcards/delete_landingcard \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 6201,\n    \"remove_template\": true,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingcards/delete_landingcard\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 6201,\n        \"remove_template\": True,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingcards/delete_landingcard\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 6201,\n      \"remove_template\": true,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"ID des Landingcard-Templates als Ganzzahl. Optional; ohne Angabe wird die Landingcard des Mitglieds verwendet, dem dein Schlüssel zugeordnet ist. Das Template muss zu deinem Unternehmen gehören.","example":6201},"remove_template":{"type":"boolean","description":"Wahrheitswert. Bei `true` wird das Landingcard-Template endgültig gelöscht; bei `false` oder ohne Angabe (Standard) bleibt es erhalten und soll archiviert werden.","example":true},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["bestaetigt"]},"example":{"template_id":6201,"remove_template":true,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingcards","werkzeug":"delete_landingcard","stufe":"loeschen","data":{"success":true,"data":{"member_id":7788,"template_id":6201},"message":"LandingCard deaktiviert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingcards/activate_landingcard":{"post":{"tags":["Landingcards"],"operationId":"landingcards__activate_landingcard","summary":"Landingcard aktivieren","description":"Schaltet die Landingcard des Mitglieds ein, dem dein Schlüssel zugeordnet ist,. Das Template wird über `template_id` bestimmt, ohne Angabe ist es das Landingcard-Template des Mitglieds; existiert keines, wird nur das Mitglied eingeschaltet. Die Antwort enthält `member_id` und `template_id` (`null`, wenn es kein Template gibt) mit der Meldung „LandingCard aktiviert“. Die Aktion zählt zur Stufe „veröffentlichen“ und verlangt deshalb `\"bestaetigt\": true`.\n\n**Gut zu wissen**\n\n- Der Schlüssel braucht das Recht „veröffentlichen“.\n\n**Recht am Schlüssel:** `veroeffentlichen`. Der Aufruf wirkt nach außen und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingcards/activate_landingcard \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 6201,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingcards/activate_landingcard\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 6201,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingcards/activate_landingcard\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 6201,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"veroeffentlichen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"ID des Landingcard-Templates als Ganzzahl. Optional; ohne Angabe wird das Template des Mitglieds verwendet, dem dein Schlüssel zugeordnet ist. Das Template muss zu deinem Unternehmen gehören.","example":6201},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["bestaetigt"]},"example":{"template_id":6201,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingcards","werkzeug":"activate_landingcard","stufe":"veroeffentlichen","data":{"success":true,"data":{"member_id":7788,"template_id":6201},"message":"LandingCard aktiviert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „veroeffentlichen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingcards/deactivate_landingcard":{"post":{"tags":["Landingcards"],"operationId":"landingcards__deactivate_landingcard","summary":"Landingcard deaktivieren","description":"Schaltet die Landingcard des Mitglieds aus, dem dein Schlüssel zugeordnet ist,. Das Template wird über `template_id` bestimmt, ohne Angabe ist es das Landingcard-Template des Mitglieds; existiert keines, wird nur das Mitglied ausgeschaltet. Die Antwort enthält `member_id` und `template_id` (`null`, wenn es kein Template gibt) mit der Meldung „LandingCard deaktiviert“. Die Daten bleiben erhalten; mit `activate_landingcard` schaltest du die Karte wieder ein.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingcards/deactivate_landingcard \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 6201\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingcards/deactivate_landingcard\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 6201,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingcards/deactivate_landingcard\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 6201\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"ID des Landingcard-Templates als Ganzzahl. Optional; ohne Angabe wird das Template des Mitglieds verwendet, dem dein Schlüssel zugeordnet ist. Das Template muss zu deinem Unternehmen gehören.","example":6201}}},"example":{"template_id":6201}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingcards","werkzeug":"deactivate_landingcard","stufe":"schreiben","data":{"success":true,"data":{"member_id":7788,"template_id":6201},"message":"LandingCard deaktiviert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingcards/get_landingcard_analytics":{"post":{"tags":["Landingcards"],"operationId":"landingcards__get_landingcard_analytics","summary":"Landingcard-Kennzahlen abrufen","description":"Gibt einfache Kennzahlen zu einer Landingcard zurück: `member_id`, `template_id` (oder `null`), `views`, `qr_scans`, `leads` und `last_updated` (aktueller Zeitpunkt der Abfrage, ISO-8601). Die Karte wird über `template_id` bestimmt, ohne Angabe über das Mitglied, dem dein Schlüssel zugeordnet ist. Die Zahlen `views`, `qr_scans` und `leads` sind in dieser Version feste Platzhalter und liefern immer 0; echte Messwerte liefert dieser Aufruf noch nicht. Findet sich kein Mitglied, kommt `success: false` mit „LandingCard nicht gefunden“.\n\n**Gut zu wissen**\n\n- Nutze die Werte nicht für Auswertungen – sie sind immer 0.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingcards/get_landingcard_analytics \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 6201\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingcards/get_landingcard_analytics\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 6201,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingcards/get_landingcard_analytics\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 6201\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"ID des Landingcard-Templates als Ganzzahl. Optional; ohne Angabe wird die Landingcard des Mitglieds verwendet, dem dein Schlüssel zugeordnet ist. Das Template muss zu deinem Unternehmen gehören.","example":6201}}},"example":{"template_id":6201}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingcards","werkzeug":"get_landingcard_analytics","stufe":"lesen","data":{"success":true,"data":{"member_id":7788,"template_id":6201,"views":0,"qr_scans":0,"leads":0,"last_updated":"2026-11-09T10:15:32+01:00"},"message":null,"timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingcards/bulk_create_landingcards":{"post":{"tags":["Landingcards"],"operationId":"landingcards__bulk_create_landingcards","summary":"Landingcards für mehrere Mitglieder aktivieren","description":"Schaltet die Landingcard für eine Liste von Mitgliedern deines Unternehmens ein. Je Mitglied wird die Karte aktiviert, bei Bedarf ein eindeutiger Slug gebildet und ein QR-Code erzeugt. Ein Landingcard-Template wird dabei nicht angelegt. Die Antwort enthält in `data` eine Liste mit einem Eintrag je Mitglied: `memberId`, `success` und bei Erfolg `card` (Mitgliedsdatensatz mit `name`, Kontaktdaten, `company`, `social`, `qrCode`, `cardUrl`, `status`, `memberSlug`, `stats` …), bei Misserfolg `error`. Mitglieder, die nicht existieren oder zu einem anderen Unternehmen gehören, ergeben `success: false` mit „Member not found or not in company“; die übrigen Einträge werden trotzdem verarbeitet. Die Meldung lautet „LandingCards aktiviert“.\n\n**Gut zu wissen**\n\n- Eine leere Liste `member_ids` wird mit einem Fehler abgelehnt.\n- Die Karten-URL in diesem Datensatz zeigt auf `landingpage.tools/card/…`, nicht auf `landingcard.app`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingcards/bulk_create_landingcards \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"member_ids\": [\n        7788,\n        7790\n    ]\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingcards/bulk_create_landingcards\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"member_ids\": [\n            7788,\n            7790,\n        ],\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingcards/bulk_create_landingcards\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"member_ids\": [\n          7788,\n          7790\n      ]\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"member_ids":{"type":"array","items":{"type":"integer"},"description":"Liste der Mitglieds-IDs (Ganzzahlen) aus deinem Unternehmen, für die die Landingcard eingeschaltet wird. Pflichtangabe, darf nicht leer sein. Es gibt keine Obergrenze; große Listen dauern entsprechend länger, da je Mitglied ein QR-Code erzeugt werden kann.","example":[7788,7790]}},"required":["member_ids"]},"example":{"member_ids":[7788,7790]}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingcards","werkzeug":"bulk_create_landingcards","stufe":"schreiben","data":{"success":true,"data":[{"memberId":7788,"success":true,"card":{"id":7788,"name":"Mira Beispiel","firstName":"Mira","lastName":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550100","mobile":"+49 170 5550101","title":"Vertriebsleiterin","department":"Vertrieb","bio":"Ich begleite Handwerksbetriebe bei der Digitalisierung.","skills":"Vertrieb, Beratung","company":{"id":1234,"name":"Beispiel GmbH","slug":"beispiel-gmbh","logo":"/logos/beispiel.png","website":"https://www.example.com","primaryColor":"#6366f1","secondaryColor":"#8b5cf6"},"social":{"linkedin":"https://www.linkedin.com/in/mira-beispiel","xing":null,"twitter":null,"facebook":null,"instagram":null,"tiktok":null,"website":"https://www.example.com"},"profilePicture":"/landingcards/mira.jpg","qrCode":"/landingcards/qrcodes/qr-7788-1783579200.png","cardUrl":"https://landingpage.tools/card/beispiel-gmbh/mira-beispiel","status":"active","memberSlug":"mira-beispiel","createdAt":1783579200,"updatedAt":1783665600,"stats":{"views":0,"uniqueVisitors":0,"leads":0,"qrScans":0,"vCardDownloads":0,"shares":0}}},{"memberId":7790,"success":false,"error":"Member not found or not in company"}],"message":"LandingCards aktiviert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/landingcards/regenerate_landingcard_qrcode":{"post":{"tags":["Landingcards"],"operationId":"landingcards__regenerate_landingcard_qrcode","summary":"QR-Code neu erzeugen","description":"Erzeugt für das Mitglied, dem dein Schlüssel zugeordnet ist, einen neuen QR-Code der Landingcard. Die Funktion löst die Verknüpfung zum bisherigen QR-Code am Mitglied, speichert es und legt über das Anlegen-Verfahren der Landingcard einen neuen QR-Code an (Bild als PNG, kodiert wird ein verfolgbarer Kurzlink). Dabei wird die Landingcard des Mitglieds eingeschaltet und bei Bedarf der Slug gebildet. Die Funktion hat keine Parameter. Die Antwort enthält `member_id` und `template_id` (oder `null`) mit der Meldung „QR-Code aktualisiert“; das Bild selbst liefert sie nicht – es ist über den Kartendatensatz erreichbar.\n\n**Gut zu wissen**\n\n- Der neue QR-Code-Datensatz wird unter dem festen Schlüssel `landingcard-<Mitglieds-ID>` im Ordner `/qrcodes` angelegt. Existiert dort schon ein Datensatz mit diesem Schlüssel, meldet das System einen doppelten Pfad und der Aufruf schlägt fehl; die Verknüpfung am Mitglied ist dann bereits gelöst.\n- Der Aufruf wirkt nur auf das Mitglied des Schlüssels.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/landingcards/regenerate_landingcard_qrcode \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/landingcards/regenerate_landingcard_qrcode\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/landingcards/regenerate_landingcard_qrcode\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"landingcards","werkzeug":"regenerate_landingcard_qrcode","stufe":"schreiben","data":{"success":true,"data":{"member_id":7788,"template_id":6201},"message":"QR-Code aktualisiert","timestamp":"2026-11-09 10:15:32","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/qrcodes/get_qrcodes":{"post":{"tags":["QR-Codes"],"operationId":"qrcodes__get_qrcodes","summary":"QR-Codes auflisten","description":"Liefert die QR-Codes deiner Firma, **neueste zuerst** (nach Erstellungsdatum absteigend). Das Ergebnis steht in `data.items`; dazu kommen `total` (Gesamtzahl der Treffer ohne Seitenbegrenzung), `limit` und `offset`. Jedes Element enthält `id`, `name`, `target`, `is_active`, `short_code`, `company_id`, `document_id`, `member_id`, `meta_title`, `meta_description`, `screenshot`, `created_at` und `updated_at`. Das Feld `base64_image` ist in der Liste immer `null`, damit die Antwort klein bleibt; das Bild holst du mit `get_qr_image` oder `get_qrcode`. Ohne Angabe werden 25 Einträge geliefert. Zum Blättern erhöhst du `offset` um `limit`, bis du `total` erreicht hast.\n\n**Gut zu wissen**\n\n- `limit` hat nach unten die Grenze 1 und nach oben keine feste Grenze; `offset` ist mindestens 0.\n- Ein `status` ungleich `active` oder `inactive` wird ignoriert, dann kommen alle Codes.\n- `base64_image` ist in der Liste immer `null` – das Bild liefern nur `get_qrcode` und `get_qr_image`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/qrcodes/get_qrcodes \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"active\",\n    \"limit\": 10,\n    \"offset\": 0\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/qrcodes/get_qrcodes\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"active\",\n        \"limit\": 10,\n        \"offset\": 0,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/qrcodes/get_qrcodes\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"active\",\n      \"limit\": 10,\n      \"offset\": 0\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Optionaler Filter auf den Aktivstatus. Erlaubt sind `active` (nur aktive Codes) und `inactive` (nur deaktivierte); Groß-/Kleinschreibung spielt keine Rolle. Jeder andere Wert wird still ignoriert und liefert alle Codes.","example":"active"},"limit":{"type":"integer","description":"Maximale Anzahl Einträge pro Antwort als ganze Zahl. Standard ist 25, der kleinste Wert ist 1. Es gibt keine feste Obergrenze; sehr große Werte machen die Antwort aber entsprechend groß.","example":10},"offset":{"type":"integer","description":"Anzahl der Einträge, die am Anfang der sortierten Liste übersprungen werden, für das seitenweise Blättern. Standard ist 0, negative Werte werden zu 0.","example":0}}},"example":{"status":"active","limit":10,"offset":0}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"qrcodes","werkzeug":"get_qrcodes","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":48213,"name":"Messestand Frühjahr","target":"https://www.example.com/messe","is_active":true,"short_code":"a3f9c21b7e","company_id":"4711","document_id":"0","member_id":null,"meta_title":"Messe Frühjahr – Beispiel GmbH","meta_description":"Besuchen Sie uns auf der Frühjahrsmesse.","base64_image":null,"screenshot":null,"created_at":"2026-10-12T09:41:07+02:00","updated_at":"2026-10-12T09:41:09+02:00"}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/qrcodes/get_qrcode":{"post":{"tags":["QR-Codes"],"operationId":"qrcodes__get_qrcode","summary":"QR-Code abrufen","description":"Liefert einen einzelnen QR-Code deiner Firma anhand seiner `id`. Die Antwort enthält dieselben Felder wie ein Listeneintrag, zusätzlich aber das gespeicherte Bild in `base64_image` (Base64-Zeichenkette). `short_code` ist der automatisch vergebene Kurzlink-Bestandteil, `screenshot` der Pfad eines hinterlegten Zielseiten-Screenshots oder `null`, `meta_title` und `meta_description` stammen aus der Zielseite. Gehört die ID nicht zu deiner Firma oder existiert sie nicht, kommt keine Fehlerstatus-Antwort, sondern in `data` ein Objekt mit `success: false` und einer Fehlermeldung. Wenn du nur das Bild brauchst, ist `get_qr_image` schlanker.\n\n**Gut zu wissen**\n\n- Fehler wie „nicht gefunden oder kein Zugriff“ stehen in `data.error` mit `data.success = false`.\n- Die Antwort kann durch das Base64-Bild mehrere hundert Kilobyte groß werden.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/qrcodes/get_qrcode \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"qrcode_id\": 48213\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/qrcodes/get_qrcode\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"qrcode_id\": 48213,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/qrcodes/get_qrcode\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"qrcode_id\": 48213\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"qrcode_id":{"type":"integer","description":"Die numerische ID des QR-Codes, wie sie in `id` von `get_qrcodes` oder `create_qrcode` zurückkommt. Pflichtangabe. Codes anderer Firmen werden wie nicht vorhanden behandelt.","example":48213}},"required":["qrcode_id"]},"example":{"qrcode_id":48213}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"qrcodes","werkzeug":"get_qrcode","stufe":"lesen","data":{"success":true,"data":{"id":48213,"name":"Messestand Frühjahr","target":"https://www.example.com/messe","is_active":true,"short_code":"a3f9c21b7e","company_id":"4711","document_id":"0","member_id":null,"meta_title":"Messe Frühjahr – Beispiel GmbH","meta_description":"Besuchen Sie uns auf der Frühjahrsmesse.","base64_image":"UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=","screenshot":null,"created_at":"2026-10-12T09:41:07+02:00","updated_at":"2026-10-12T09:41:09+02:00"},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/qrcodes/create_qrcode":{"post":{"tags":["QR-Codes"],"operationId":"qrcodes__create_qrcode","summary":"QR-Code anlegen","description":"Legt einen neuen, standardmäßig **aktiven** QR-Code für deine Firma an. Die Plattform vergibt einen zufälligen 10-stelligen `short_code`, rendert daraus das QR-Bild (Kurzlink-Adresse der Plattform) und speichert es in `base64_image`. Danach versucht sie, Titel und Beschreibung der Ziel-URL abzurufen (Abruf mit Zeitlimit von etwa 10 Sekunden) und in `meta_title` und `meta_description` zu speichern; scheitert das, bleiben die Felder leer, der Code wird trotzdem angelegt. Mit `is_active` = false legst du den Code gleich deaktiviert an. Zurück kommt der vollständige neue Eintrag inklusive Bild mit der Meldung „QR-Code erstellt“. Ohne `document_id` wird `\"0\"` gespeichert.\n\n**Gut zu wissen**\n\n- `name` und `target` werden getrimmt und HTML-maskiert (zum Beispiel wird `&` zu `&amp;`); eine URL mit mehreren Query-Parametern ist danach so gespeichert.\n- `target` wird nicht auf ein URL-Format geprüft, nur auf „nicht leer“.\n- Für die Meta-Daten ruft die Plattform die Ziel-URL ab; sie sollte also öffentlich erreichbar sein.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/qrcodes/create_qrcode \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Flyer Herbstaktion\",\n    \"target\": \"https://www.example.com/herbst\",\n    \"is_active\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/qrcodes/create_qrcode\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"name\": \"Flyer Herbstaktion\",\n        \"target\": \"https://www.example.com/herbst\",\n        \"is_active\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/qrcodes/create_qrcode\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"name\": \"Flyer Herbstaktion\",\n      \"target\": \"https://www.example.com/herbst\",\n      \"is_active\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Frei wählbarer Name des QR-Codes, zum Beispiel der Einsatzzweck. Pflichtangabe, darf nicht leer sein. Der Wert wird getrimmt und HTML-maskiert gespeichert.","example":"Flyer Herbstaktion"},"target":{"type":"string","description":"Ziel-URL, zu der der QR-Code weiterleitet, zum Beispiel `https://www.example.com/angebot`. Pflichtangabe. Aus dieser Adresse werden beim Anlegen Seitentitel und Beschreibung gelesen. Es findet keine URL-Formatprüfung statt.","example":"https://www.example.com/herbst"},"document_id":{"type":"integer","description":"Optional: ID eines Dokuments, mit dem der QR-Code verknüpft wird, als ganze Zahl. Ohne Angabe wird `0` (kein Dokument) gespeichert; in der Antwort steht der Wert als Zeichenkette."},"is_active":{"type":"boolean","description":"Optional: `true` (Standard) legt den Code aktiv an, `false` legt ihn deaktiviert an. Ein deaktivierter Code bleibt gespeichert und kann mit `update_qrcode` wieder aktiviert werden.","default":true,"example":true}},"required":["name","target"]},"example":{"name":"Flyer Herbstaktion","target":"https://www.example.com/herbst","is_active":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"qrcodes","werkzeug":"create_qrcode","stufe":"schreiben","data":{"success":true,"data":{"id":48230,"name":"Flyer Herbstaktion","target":"https://www.example.com/herbst","is_active":true,"short_code":"7d02e9ab41","company_id":"4711","document_id":"0","member_id":"9021","meta_title":"Herbstaktion – Beispiel GmbH","meta_description":"Unsere Angebote im Herbst.","base64_image":"UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=","screenshot":null,"created_at":"2026-11-09T10:20:31+01:00","updated_at":"2026-11-09T10:20:33+01:00"},"message":"QR-Code erstellt","timestamp":"2026-11-09 10:20:33","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/qrcodes/update_qrcode":{"post":{"tags":["QR-Codes"],"operationId":"qrcodes__update_qrcode","summary":"QR-Code ändern","description":"Ändert Felder eines bestehenden QR-Codes. Im Objekt `data` gibst du nur die Felder an, die sich ändern sollen; nicht genannte Felder bleiben unverändert. Wirksam sind `name`, `target` und `is_active`. Zurück kommt der aktualisierte Eintrag mit Bild und der Meldung „QR-Code aktualisiert“. Das gedruckte Bild muss sich bei einer Änderung von `target` nicht ändern, weil es auf den Kurzlink der Plattform zeigt. Beachte, dass beim Ändern der Ziel-URL die gespeicherten Meta-Daten (`meta_title`, `meta_description`) nicht automatisch neu gelesen werden. Unbekannte Schlüssel in `data` werden ohne Fehler ignoriert.\n\n**Gut zu wissen**\n\n- Nur ein Feld mit Wert ungleich `null` wird übernommen; `null` löscht nichts.\n- `name` und `target` werden getrimmt und HTML-maskiert gespeichert.\n- Meta-Daten werden bei einer neuen Ziel-URL nicht neu gelesen.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/qrcodes/update_qrcode \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"qrcode_id\": 48230,\n    \"data\": {\n        \"target\": \"https://www.example.com/herbst-2026\",\n        \"is_active\": false\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/qrcodes/update_qrcode\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"qrcode_id\": 48230,\n        \"data\": {\n            \"target\": \"https://www.example.com/herbst-2026\",\n            \"is_active\": False,\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/qrcodes/update_qrcode\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"qrcode_id\": 48230,\n      \"data\": {\n          \"target\": \"https://www.example.com/herbst-2026\",\n          \"is_active\": false\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"qrcode_id":{"type":"integer","description":"Die ID des zu ändernden QR-Codes, wie sie `get_qrcodes` oder `create_qrcode` liefert. Pflichtangabe; Codes anderer Firmen werden abgelehnt.","example":48230},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern. Wirksame Schlüssel: `name` (Text), `target` (Ziel-URL als Text) und `is_active` (true/false). Pflichtangabe; ein leeres Objekt ändert nichts. Alle anderen Schlüssel werden ignoriert.","example":{"target":"https://www.example.com/herbst-2026","is_active":false}}},"required":["qrcode_id","data"]},"example":{"qrcode_id":48230,"data":{"target":"https://www.example.com/herbst-2026","is_active":false}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"qrcodes","werkzeug":"update_qrcode","stufe":"schreiben","data":{"success":true,"data":{"id":48230,"name":"Flyer Herbstaktion","target":"https://www.example.com/herbst-2026","is_active":false,"short_code":"7d02e9ab41","company_id":"4711","document_id":"0","member_id":"9021","meta_title":"Herbstaktion – Beispiel GmbH","meta_description":"Unsere Angebote im Herbst.","base64_image":"UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=","screenshot":null,"created_at":"2026-11-09T10:20:31+01:00","updated_at":"2026-11-09T10:31:02+01:00"},"message":"QR-Code aktualisiert","timestamp":"2026-11-09 10:31:02","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/qrcodes/delete_qrcode":{"post":{"tags":["QR-Codes"],"operationId":"qrcodes__delete_qrcode","summary":"QR-Code löschen","description":"Löscht einen QR-Code deiner Firma **endgültig**. Es gibt keine Papierkorb-Funktion über diese API. Danach ist der `short_code` nicht mehr gültig; bereits gedruckte Codes, die auf diesen Kurzlink zeigen, führen ins Leere. Zurück kommt nur die gelöschte ID in `data.qrcode_id` mit der Meldung „QR-Code gelöscht“. Weil die Aktion unumkehrbar ist, verlangt die API im Körper zusätzlich `\"bestaetigt\": true`, sonst antwortet sie mit HTTP 409. Wenn du einen Code nur vorübergehend abschalten willst, setze stattdessen mit `update_qrcode` das Feld `is_active` auf false.\n\n**Gut zu wissen**\n\n- Ohne `\"bestaetigt\": true` wird die Anfrage mit 409 abgelehnt.\n- Zum vorübergehenden Abschalten eignet sich `update_qrcode` mit `is_active` = false.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/qrcodes/delete_qrcode \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"qrcode_id\": 48230,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/qrcodes/delete_qrcode\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"qrcode_id\": 48230,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/qrcodes/delete_qrcode\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"qrcode_id\": 48230,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"qrcode_id":{"type":"integer","description":"Die ID des zu löschenden QR-Codes. Pflichtangabe. Der Code muss zu deiner Firma gehören, sonst kommt die Fehlermeldung „QR-Code nicht gefunden oder kein Zugriff“.","example":48230},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["qrcode_id","bestaetigt"]},"example":{"qrcode_id":48230,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"qrcodes","werkzeug":"delete_qrcode","stufe":"loeschen","data":{"success":true,"data":{"qrcode_id":48230},"message":"QR-Code gelöscht","timestamp":"2026-11-09 10:40:12","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/qrcodes/copy_qrcode":{"post":{"tags":["QR-Codes"],"operationId":"qrcodes__copy_qrcode","summary":"QR-Code kopieren","description":"Dupliziert einen bestehenden QR-Code deiner Firma als neuen Eintrag. Übernommen werden Ziel-URL, Dokument-Verknüpfung, Mitglieds-Zuordnung, Meta-Titel, Meta-Beschreibung und das Bild; der Kopie wird ein **neuer** `short_code` zugewiesen. Ohne Angabe heißt die Kopie „<Originalname> (Kopie)“ und hat den Aktivstatus des Originals. Mit `name` und `is_active` überschreibst du beides. Zurück kommt der neue Eintrag mit der Meldung „QR-Code kopiert“. Die Kopie ist in demselben Ordner wie das Original abgelegt und sofort veröffentlicht. Nützlich, um aus einem Code mehrere Varianten für verschiedene Kanäle zu erzeugen, deren Ziel du anschließend mit `update_qrcode` anpasst.\n\n**Gut zu wissen**\n\n- Das Bild wird unverändert vom Original übernommen; es kodiert also weiterhin den Kurzlink des Originals, nicht den neuen `short_code`.\n- Der neue Name wird HTML-maskiert gespeichert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/qrcodes/copy_qrcode \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"qrcode_id\": 48213,\n    \"name\": \"Messestand Frühjahr – Plakat\",\n    \"is_active\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/qrcodes/copy_qrcode\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"qrcode_id\": 48213,\n        \"name\": \"Messestand Frühjahr – Plakat\",\n        \"is_active\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/qrcodes/copy_qrcode\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"qrcode_id\": 48213,\n      \"name\": \"Messestand Frühjahr – Plakat\",\n      \"is_active\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"qrcode_id":{"type":"integer","description":"Die ID des QR-Codes, der kopiert werden soll. Pflichtangabe; der Code muss zu deiner Firma gehören.","example":48213},"name":{"type":"string","description":"Optional: Name der Kopie. Ohne Angabe wird der Originalname mit dem Zusatz „ (Kopie)“ verwendet. Der Wert wird getrimmt und HTML-maskiert gespeichert.","example":"Messestand Frühjahr – Plakat"},"is_active":{"type":"boolean","description":"Optional: Aktivstatus der Kopie (`true` oder `false`). Ohne Angabe übernimmt die Kopie den Status des Originals.","example":true}},"required":["qrcode_id"]},"example":{"qrcode_id":48213,"name":"Messestand Frühjahr – Plakat","is_active":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"qrcodes","werkzeug":"copy_qrcode","stufe":"schreiben","data":{"success":true,"data":{"id":48244,"name":"Messestand Frühjahr – Plakat","target":"https://www.example.com/messe","is_active":true,"short_code":"c81b05d3f6","company_id":"4711","document_id":"0","member_id":null,"meta_title":"Messe Frühjahr – Beispiel GmbH","meta_description":"Besuchen Sie uns auf der Frühjahrsmesse.","base64_image":"UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=","screenshot":null,"created_at":"2026-11-09T10:45:00+01:00","updated_at":"2026-11-09T10:45:00+01:00"},"message":"QR-Code kopiert","timestamp":"2026-11-09 10:45:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/qrcodes/get_qrcode_analytics":{"post":{"tags":["QR-Codes"],"operationId":"qrcodes__get_qrcode_analytics","summary":"QR-Code-Analytics abrufen","description":"Liefert eine einfache Kennzahlen-Struktur zu einem QR-Code: `qrcode_id`, `target`, `is_active` und `last_meta_update`. Die Felder `scans_total`, `scans_unique` und `last_scan` sind derzeit **Platzhalter**: Die Funktion liefert dafür immer `0`, `0` und `null`, weil an dieser Stelle noch keine Scan-Auswertung angebunden ist. `last_meta_update` enthält das Änderungsdatum des Eintrags im ISO-8601-Format, sofern für den Code Meta-Titel oder Meta-Beschreibung vorhanden sind, sonst `null`. Nutze die Funktion nicht, um echte Scan-Zahlen auszuwerten; für Bestandszahlen über alle Codes ist `get_qrcode_stats` gedacht.\n\n**Gut zu wissen**\n\n- `scans_total`, `scans_unique` und `last_scan` sind fest `0`, `0` und `null`.\n- `last_meta_update` ist das allgemeine Änderungsdatum des Eintrags, nicht ein getrennter Meta-Zeitstempel.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/qrcodes/get_qrcode_analytics \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"qrcode_id\": 48213\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/qrcodes/get_qrcode_analytics\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"qrcode_id\": 48213,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/qrcodes/get_qrcode_analytics\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"qrcode_id\": 48213\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"qrcode_id":{"type":"integer","description":"Die ID des QR-Codes, für den die Kennzahlen geliefert werden sollen. Pflichtangabe; der Code muss zu deiner Firma gehören.","example":48213}},"required":["qrcode_id"]},"example":{"qrcode_id":48213}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"qrcodes","werkzeug":"get_qrcode_analytics","stufe":"lesen","data":{"success":true,"data":{"qrcode_id":48213,"target":"https://www.example.com/messe","is_active":true,"last_meta_update":"2026-10-12T09:41:09+02:00","scans_total":0,"scans_unique":0,"last_scan":null},"message":null,"timestamp":"2026-11-09 10:50:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/qrcodes/get_qrcode_stats":{"post":{"tags":["QR-Codes"],"operationId":"qrcodes__get_qrcode_stats","summary":"QR-Code-Statistik abrufen","description":"Zählt alle QR-Codes deiner Firma und liefert fünf Kennzahlen in `data`: `total` (alle Codes), `active` (aktive), `inactive` (deaktivierte), `with_meta` (Codes, bei denen Meta-Titel oder Meta-Beschreibung gefüllt ist) und `without_meta` (alle übrigen). Die Funktion hat keine Parameter und ist ein schneller Überblick, zum Beispiel für ein Dashboard. Sie betrifft ausschließlich Bestandsdaten; Scan-Zahlen enthält sie nicht. Ein hoher Wert bei `without_meta` bedeutet, dass beim Anlegen oder später keine Titel aus der Zielseite gelesen werden konnten.\n\n**Gut zu wissen**\n\n- Die Funktion lädt alle Codes der Firma; bei sehr vielen Codes kann sie spürbar länger dauern.\n- Scan-Zahlen enthält sie nicht.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/qrcodes/get_qrcode_stats \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/qrcodes/get_qrcode_stats\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/qrcodes/get_qrcode_stats\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"qrcodes","werkzeug":"get_qrcode_stats","stufe":"lesen","data":{"success":true,"data":{"total":14,"active":11,"inactive":3,"with_meta":12,"without_meta":2},"message":null,"timestamp":"2026-11-09 10:52:10","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/qrcodes/get_qrcode_scans":{"post":{"tags":["QR-Codes"],"operationId":"qrcodes__get_qrcode_scans","summary":"Scan-Historie abrufen (Platzhalter)","description":"Soll die Scan-Historie eines QR-Codes liefern, ist aber derzeit ein **Platzhalter**: Die Funktion prüft nur, dass der Code zu deiner Firma gehört, und antwortet dann immer mit einer leeren Liste `items` und der Meldung `message` = „Scan-Tracking ist derzeit nicht verfügbar“. Es werden keine Scans gespeichert oder gezählt. Du kannst die Funktion bedenkenlos aufrufen, um zu prüfen, ob ein Code existiert und dir gehört; für echte Auswertungen taugt sie noch nicht. Sobald Scan-Tracking angebunden wird, bleibt der Aufbau mit `items` voraussichtlich bestehen.\n\n**Gut zu wissen**\n\n- Die Antwort enthält immer eine leere Liste, unabhängig davon, wie oft der Code gescannt wurde.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/qrcodes/get_qrcode_scans \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"qrcode_id\": 48213\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/qrcodes/get_qrcode_scans\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"qrcode_id\": 48213,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/qrcodes/get_qrcode_scans\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"qrcode_id\": 48213\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"qrcode_id":{"type":"integer","description":"Die ID des QR-Codes, dessen Scan-Historie abgefragt wird. Pflichtangabe; der Code muss zu deiner Firma gehören, sonst kommt eine Fehlermeldung.","example":48213}},"required":["qrcode_id"]},"example":{"qrcode_id":48213}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"qrcodes","werkzeug":"get_qrcode_scans","stufe":"lesen","data":{"success":true,"data":{"items":[],"message":"Scan-Tracking ist derzeit nicht verfügbar"},"message":null,"timestamp":"2026-11-09 10:55:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/qrcodes/get_qr_image":{"post":{"tags":["QR-Codes"],"operationId":"qrcodes__get_qr_image","summary":"QR-Code-Bild abrufen","description":"Liefert nur das Bild eines QR-Codes deiner Firma, ohne die übrigen Felder. In `data.base64` steht das gespeicherte Bild als Base64-Zeichenkette (beim Anlegen wird es im WebP-Format erzeugt), in `data.screenshot` der Pfad eines zum Code hinterlegten Zielseiten-Screenshots oder `null`. Die Funktion eignet sich, um das Bild zu laden, bevor du es druckst oder in eine Seite einbettest, nachdem `get_qrcodes` dir die ID geliefert hat (die Liste enthält das Bild bewusst nicht). Das Bild kodiert die Kurzlink-Adresse des Codes; es ändert sich nicht, wenn du das Ziel mit `update_qrcode` änderst.\n\n**Gut zu wissen**\n\n- Die Base64-Zeichenkette kann groß sein; hole sie gezielt und nicht in einer Schleife für viele Codes.\n- `base64` kann `null` sein, wenn für den Eintrag kein Bild gespeichert ist.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/qrcodes/get_qr_image \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"qrcode_id\": 48213\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/qrcodes/get_qr_image\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"qrcode_id\": 48213,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/qrcodes/get_qr_image\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"qrcode_id\": 48213\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"qrcode_id":{"type":"integer","description":"Die ID des QR-Codes, dessen Bild geliefert werden soll. Pflichtangabe; der Code muss zu deiner Firma gehören, sonst kommt „QR-Code nicht gefunden oder kein Zugriff“.","example":48213}},"required":["qrcode_id"]},"example":{"qrcode_id":48213}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"qrcodes","werkzeug":"get_qr_image","stufe":"lesen","data":{"success":true,"data":{"base64":"UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=","screenshot":null},"message":null,"timestamp":"2026-11-09 10:58:40","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/testimonials/get_testimonials":{"post":{"tags":["Kundenstimmen"],"operationId":"testimonials__get_testimonials","summary":"Kundenstimmen auflisten","description":"Liefert die Testimonials deiner Firma, **neueste zuerst** (nach Erstellungsdatum absteigend), seitenweise. Das Ergebnis steht in `data.items`; dazu kommen `total` (Gesamtzahl der Treffer), `limit` und `offset`. Jedes Element enthält `id`, `name`, `company`, `city`, `ceo`, `position`, `quote`, `problem`, `solution`, `future`, `image1desc`, `image2desc`, `rating`, `variante`, `state`, `company_id`, `created_at` und `updated_at`. Die Bewertung kommt als Zeichenkette zurück (zum Beispiel `\"5\"`). Mit `min_rating` blendest du schlechter bewertete Stimmen aus. Ohne `limit` werden 25 Einträge geliefert. Für eine nach Bewertung sortierte Liste nimm `filter_by_rating`, für einen vollständigen Abzug `export_testimonials`.\n\n**Gut zu wissen**\n\n- `min_rating` = 0 oder fehlend heißt: kein Filter.\n- `limit` hat die Untergrenze 1, aber keine feste Obergrenze; `offset` ist mindestens 0.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/testimonials/get_testimonials \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"min_rating\": 4,\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/testimonials/get_testimonials\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"min_rating\": 4,\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/testimonials/get_testimonials\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"min_rating\": 4,\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"min_rating":{"type":"integer","description":"Optional: Mindestbewertung als ganze Zahl. Es werden nur Testimonials mit `rating` größer oder gleich diesem Wert geliefert. Sinnvoll sind Werte von 1 bis 5; 0 oder keine Angabe schaltet den Filter aus.","example":4},"limit":{"type":"integer","description":"Maximale Anzahl Einträge pro Antwort als ganze Zahl. Standard ist 25, der kleinste Wert ist 1. Es gibt keine feste Obergrenze.","example":10},"offset":{"type":"integer","description":"Anzahl der Einträge, die am Anfang der sortierten Liste übersprungen werden, zum seitenweisen Blättern. Standard ist 0, negative Werte werden zu 0."}}},"example":{"min_rating":4,"limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"testimonials","werkzeug":"get_testimonials","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":31877,"name":"Marta Beispiel","company":"Muster Gartenbau GmbH","city":"Musterstadt","ceo":"Marta Beispiel","position":"Geschäftsführerin","quote":"Seit der neuen Webseite kommen deutlich mehr Anfragen.","problem":"Zu wenige Anfragen über die Webseite.","solution":"Neue Landingpage mit klarer Handlungsaufforderung.","future":"Weitere Regionen erschließen.","image1desc":"Geschäftsführerin vor einem Gewächshaus","image2desc":"Dashboard mit steigenden Anfragen","rating":"5","variante":"schmerzloesung","state":"present","company_id":"4711","created_at":"2026-09-18T14:02:11+02:00","updated_at":"2026-09-18T14:02:11+02:00"}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 11:02:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/testimonials/get_testimonial":{"post":{"tags":["Kundenstimmen"],"operationId":"testimonials__get_testimonial","summary":"Kundenstimme abrufen","description":"Liefert ein einzelnes Testimonial deiner Firma anhand seiner `id` mit allen Feldern: Personenangaben, `quote`, die Geschichte (`problem`, `solution`, `future`), die beiden Bildbeschreibungen, `rating` (als Zeichenkette), `variante`, `state`, `company_id` sowie `created_at` und `updated_at` im ISO-8601-Format. Nicht gesetzte Felder kommen als `null` oder leere Zeichenkette zurück. Gehört die ID nicht zu deiner Firma oder gibt es sie nicht, steht in `data` ein Objekt mit `success: false` und der Meldung „Testimonial nicht gefunden oder kein Zugriff“. Die ID bekommst du aus `get_testimonials`, `filter_by_rating` oder dem Ergebnis eines Imports.\n\n**Gut zu wissen**\n\n- Fehler werden in `data.error` geliefert, nicht über den HTTP-Status.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/testimonials/get_testimonial \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"testimonial_id\": 31877\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/testimonials/get_testimonial\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"testimonial_id\": 31877,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/testimonials/get_testimonial\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"testimonial_id\": 31877\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"testimonial_id":{"type":"integer","description":"Die numerische ID des Testimonials, wie sie in `id` von `get_testimonials` oder `create_testimonial` zurückkommt. Pflichtangabe. Einträge anderer Firmen werden wie nicht vorhanden behandelt.","example":31877}},"required":["testimonial_id"]},"example":{"testimonial_id":31877}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"testimonials","werkzeug":"get_testimonial","stufe":"lesen","data":{"success":true,"data":{"id":31877,"name":"Marta Beispiel","company":"Muster Gartenbau GmbH","city":"Musterstadt","ceo":"Marta Beispiel","position":"Geschäftsführerin","quote":"Seit der neuen Webseite kommen deutlich mehr Anfragen.","problem":"Zu wenige Anfragen über die Webseite.","solution":"Neue Landingpage mit klarer Handlungsaufforderung.","future":"Weitere Regionen erschließen.","image1desc":"Geschäftsführerin vor einem Gewächshaus","image2desc":"Dashboard mit steigenden Anfragen","rating":"5","variante":"schmerzloesung","state":"present","company_id":"4711","created_at":"2026-09-18T14:02:11+02:00","updated_at":"2026-09-18T14:02:11+02:00"},"message":null,"timestamp":"2026-11-09 11:04:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/testimonials/create_testimonial":{"post":{"tags":["Kundenstimmen"],"operationId":"testimonials__create_testimonial","summary":"Kundenstimme anlegen","description":"Legt ein neues Testimonial für deine Firma an. Alle Inhalte übergibst du im Objekt `data`; keiner der Schlüssel ist technisch Pflicht, sinnvoll sind mindestens `name`, `quote` und `rating`. Der Eintrag wird veröffentlicht im Ordner `/testimonials` abgelegt. Zurück kommt der vollständige neue Eintrag mit der Meldung „Testimonial erstellt“. Die Felder `name`, `company`, `city`, `ceo`, `position`, `variante` und `state` werden getrimmt und HTML-maskiert gespeichert; `quote`, `problem`, `solution`, `future`, `image1desc` und `image2desc` werden unverändert übernommen. `rating` wird als ganze Zahl gespeichert und in Antworten als Zeichenkette geliefert. Unbekannte Schlüssel in `data` werden ignoriert.\n\n**Gut zu wissen**\n\n- `variante` und `state` werden nicht gegen die erlaubten Werte geprüft; halte dich an `schmerzloesung`, `process`, `zitat` bzw. `past`, `present`, `future`.\n- `rating` wird nicht auf 1–5 begrenzt.\n- Ein Aufruf mit leerem `data` legt trotzdem einen (leeren) Eintrag an.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/testimonials/create_testimonial \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"data\": {\n        \"name\": \"Jonas Probst\",\n        \"company\": \"Probst Elektrotechnik\",\n        \"city\": \"Beispielhausen\",\n        \"position\": \"Inhaber\",\n        \"quote\": \"Der Auftritt wirkt endlich so professionell wie unsere Arbeit.\",\n        \"rating\": 5,\n        \"variante\": \"zitat\",\n        \"state\": \"present\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/testimonials/create_testimonial\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"data\": {\n            \"name\": \"Jonas Probst\",\n            \"company\": \"Probst Elektrotechnik\",\n            \"city\": \"Beispielhausen\",\n            \"position\": \"Inhaber\",\n            \"quote\": \"Der Auftritt wirkt endlich so professionell wie unsere Arbeit.\",\n            \"rating\": 5,\n            \"variante\": \"zitat\",\n            \"state\": \"present\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/testimonials/create_testimonial\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"data\": {\n          \"name\": \"Jonas Probst\",\n          \"company\": \"Probst Elektrotechnik\",\n          \"city\": \"Beispielhausen\",\n          \"position\": \"Inhaber\",\n          \"quote\": \"Der Auftritt wirkt endlich so professionell wie unsere Arbeit.\",\n          \"rating\": 5,\n          \"variante\": \"zitat\",\n          \"state\": \"present\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"Objekt mit den Inhalten der Kundenstimme. Wirksame Schlüssel: `name`, `company`, `city`, `ceo`, `position`, `quote`, `problem`, `solution`, `future`, `image1desc`, `image2desc` (alle Text), `rating` (ganze Zahl, üblich 1–5), `variante` (`schmerzloesung`, `process` oder `zitat`) und `state` (`past`, `present` oder `future`). Pflichtangabe als Objekt; nicht genannte Felder bleiben leer.","example":{"name":"Jonas Probst","company":"Probst Elektrotechnik","city":"Beispielhausen","position":"Inhaber","quote":"Der Auftritt wirkt endlich so professionell wie unsere Arbeit.","rating":5,"variante":"zitat","state":"present"}}},"required":["data"]},"example":{"data":{"name":"Jonas Probst","company":"Probst Elektrotechnik","city":"Beispielhausen","position":"Inhaber","quote":"Der Auftritt wirkt endlich so professionell wie unsere Arbeit.","rating":5,"variante":"zitat","state":"present"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"testimonials","werkzeug":"create_testimonial","stufe":"schreiben","data":{"success":true,"data":{"id":31902,"name":"Jonas Probst","company":"Probst Elektrotechnik","city":"Beispielhausen","ceo":null,"position":"Inhaber","quote":"Der Auftritt wirkt endlich so professionell wie unsere Arbeit.","problem":null,"solution":null,"future":null,"image1desc":null,"image2desc":null,"rating":"5","variante":"zitat","state":"present","company_id":"4711","created_at":"2026-11-09T11:10:00+01:00","updated_at":"2026-11-09T11:10:00+01:00"},"message":"Testimonial erstellt","timestamp":"2026-11-09 11:10:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/testimonials/update_testimonial":{"post":{"tags":["Kundenstimmen"],"operationId":"testimonials__update_testimonial","summary":"Kundenstimme ändern","description":"Ändert Felder eines bestehenden Testimonials. Im Objekt `data` gibst du nur die Felder an, die sich ändern sollen; alles andere bleibt unverändert. Wirksame Schlüssel sind dieselben wie beim Anlegen: `name`, `company`, `city`, `ceo`, `position`, `quote`, `problem`, `solution`, `future`, `image1desc`, `image2desc`, `rating`, `variante` und `state`. Ein Feld wird nur übernommen, wenn sein Wert nicht `null` ist; ein Leeren auf `null` ist so nicht möglich, ein leerer Text `\"\"` dagegen schon. Zurück kommt der aktualisierte Eintrag mit der Meldung „Testimonial aktualisiert“. Typische Anwendung: Bewertung korrigieren, Zitat kürzen oder den Zeitbezug (`state`) anpassen.\n\n**Gut zu wissen**\n\n- `null`-Werte in `data` werden ignoriert; zum Leeren eines Textfelds sende `\"\"`.\n- Textfelder wie `name` werden HTML-maskiert gespeichert, `quote` nicht.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/testimonials/update_testimonial \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"testimonial_id\": 31902,\n    \"data\": {\n        \"rating\": 4,\n        \"state\": \"past\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/testimonials/update_testimonial\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"testimonial_id\": 31902,\n        \"data\": {\n            \"rating\": 4,\n            \"state\": \"past\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/testimonials/update_testimonial\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"testimonial_id\": 31902,\n      \"data\": {\n          \"rating\": 4,\n          \"state\": \"past\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"testimonial_id":{"type":"integer","description":"Die ID des zu ändernden Testimonials, wie sie `get_testimonials` oder `create_testimonial` liefert. Pflichtangabe; Einträge anderer Firmen werden abgelehnt.","example":31902},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern. Wirksame Schlüssel: `name`, `company`, `city`, `ceo`, `position`, `quote`, `problem`, `solution`, `future`, `image1desc`, `image2desc`, `rating` (ganze Zahl), `variante` und `state`. Pflichtangabe; nicht genannte Felder bleiben unverändert, unbekannte Schlüssel werden ignoriert.","example":{"rating":4,"state":"past"}}},"required":["testimonial_id","data"]},"example":{"testimonial_id":31902,"data":{"rating":4,"state":"past"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"testimonials","werkzeug":"update_testimonial","stufe":"schreiben","data":{"success":true,"data":{"id":31902,"name":"Jonas Probst","company":"Probst Elektrotechnik","city":"Beispielhausen","ceo":null,"position":"Inhaber","quote":"Der Auftritt wirkt endlich so professionell wie unsere Arbeit.","problem":null,"solution":null,"future":null,"image1desc":null,"image2desc":null,"rating":"4","variante":"zitat","state":"past","company_id":"4711","created_at":"2026-11-09T11:10:00+01:00","updated_at":"2026-11-09T11:18:30+01:00"},"message":"Testimonial aktualisiert","timestamp":"2026-11-09 11:18:30","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/testimonials/delete_testimonial":{"post":{"tags":["Kundenstimmen"],"operationId":"testimonials__delete_testimonial","summary":"Kundenstimme löschen","description":"Löscht ein Testimonial deiner Firma **endgültig**. Über diese API gibt es keinen Papierkorb und keine Wiederherstellung. Zurück kommt nur die gelöschte ID in `data.testimonial_id` mit der Meldung „Testimonial gelöscht“. Weil die Aktion unumkehrbar ist, verlangt die API im Körper zusätzlich `\"bestaetigt\": true`, sonst antwortet sie mit HTTP 409. Wenn du eine Stimme nur nicht mehr verwenden möchtest, kannst du sie stattdessen mit `update_testimonial` über `state` oder `rating` anders einordnen; vor dem Löschen empfiehlt sich ein Abzug mit `export_testimonials`.\n\n**Gut zu wissen**\n\n- Ohne `\"bestaetigt\": true` wird die Anfrage mit 409 abgelehnt.\n- Sichere Bestände vorher mit `export_testimonials`.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/testimonials/delete_testimonial \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"testimonial_id\": 31902,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/testimonials/delete_testimonial\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"testimonial_id\": 31902,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/testimonials/delete_testimonial\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"testimonial_id\": 31902,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"testimonial_id":{"type":"integer","description":"Die ID des zu löschenden Testimonials. Pflichtangabe. Der Eintrag muss zu deiner Firma gehören, sonst kommt die Fehlermeldung „Testimonial nicht gefunden oder kein Zugriff“.","example":31902},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["testimonial_id","bestaetigt"]},"example":{"testimonial_id":31902,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"testimonials","werkzeug":"delete_testimonial","stufe":"loeschen","data":{"success":true,"data":{"testimonial_id":31902},"message":"Testimonial gelöscht","timestamp":"2026-11-09 11:25:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/testimonials/scrape_testimonials_from_url":{"post":{"tags":["Kundenstimmen"],"operationId":"testimonials__scrape_testimonials_from_url","summary":"Kundenstimmen aus Webseite importieren","description":"Liest eine Webseite, erkennt darauf Kundenbewertungen und legt für jede erkannte Stimme **sofort ein neues Testimonial** in deiner Firma an. Dazu lädt die Plattform die Seite (Zeitlimit 30 Sekunden), entfernt das HTML und kürzt den Text auf höchstens 50.000 Zeichen; eine automatische Auswertung extrahiert daraus Name, Firma, Stadt, Position, Zitat, optional Problem/Lösung/Ausblick, Bewertung und Variante. Zurück kommt eine Liste der angelegten Einträge mit `id`, `name`, `company`, `quote`, `rating` und `variante`. Findet die Auswertung nichts, ist die Liste leer. Fehlende Angaben werden vom Verfahren ergänzt oder geschätzt (zum Beispiel Namen wie „Kunde 1“ oder eine Bewertung nach Tonfall); prüfe die Einträge deshalb, bevor du sie veröffentlichst.\n\n**Gut zu wissen**\n\n- Jeder Aufruf legt neue Einträge an; ein erneuter Import derselben Seite erzeugt Dubletten.\n- Bewertung, Namen und Positionen können geschätzt sein – nicht ungeprüft veröffentlichen.\n- Die URL muss gültig und öffentlich erreichbar sein; sonst kommt ein Fehler in `data.error`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/testimonials/scrape_testimonials_from_url \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"url\": \"https://www.example.com/referenzen\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/testimonials/scrape_testimonials_from_url\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"url\": \"https://www.example.com/referenzen\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/testimonials/scrape_testimonials_from_url\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"url\": \"https://www.example.com/referenzen\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"Vollständige Adresse der Webseite mit den Kundenstimmen, zum Beispiel `https://www.example.com/referenzen`. Pflichtangabe. Die Adresse muss ein gültiges URL-Format haben und öffentlich abrufbar sein.","example":"https://www.example.com/referenzen"}},"required":["url"]},"example":{"url":"https://www.example.com/referenzen"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"testimonials","werkzeug":"scrape_testimonials_from_url","stufe":"schreiben","data":{"success":true,"data":[{"id":31911,"name":"Sabine Muster","company":"Muster Praxis","quote":"Schnelle Termine und freundliches Team.","rating":"5","variante":"zitat"},{"id":31912,"name":"Kunde 2","company":"","quote":"Die Beratung hat mir viel Zeit gespart.","rating":"4","variante":"zitat"}],"message":"Testimonials importiert","timestamp":"2026-11-09 11:30:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/testimonials/filter_by_rating":{"post":{"tags":["Kundenstimmen"],"operationId":"testimonials__filter_by_rating","summary":"Kundenstimmen nach Bewertung filtern","description":"Liefert alle Testimonials deiner Firma, deren `rating` mindestens `min_rating` beträgt, **sortiert nach Bewertung absteigend** (beste zuerst). Im Unterschied zu `get_testimonials` gibt es hier keine Seiteneinteilung: `data` ist direkt die Liste der Einträge (jeweils mit allen Feldern wie in `get_testimonial`), nicht ein Objekt mit `items`. Die Funktion verändert nichts, ist im Katalog aber als schreibend eingestuft; dein Schlüssel braucht dafür also das Recht „schreiben“. Praktisch, um die besten Stimmen für eine Webseite oder Anzeige herauszusuchen. Bei sehr vielen Stimmen ist `get_testimonials` mit `limit` die sparsamere Wahl.\n\n**Gut zu wissen**\n\n- Antwort-`data` ist eine Liste, kein Objekt mit `items`.\n- Kein Limit: es werden alle passenden Einträge geliefert.\n- Der Schlüssel braucht das Recht „schreiben“, obwohl nichts verändert wird.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/testimonials/filter_by_rating \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"min_rating\": 5\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/testimonials/filter_by_rating\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"min_rating\": 5,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/testimonials/filter_by_rating\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"min_rating\": 5\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"min_rating":{"type":"integer","description":"Mindestbewertung als ganze Zahl (sinnvoll 1 bis 5). Pflichtangabe; mit 0 werden alle Stimmen geliefert. Geliefert werden alle Stimmen mit `rating` größer oder gleich diesem Wert.","example":5}},"required":["min_rating"]},"example":{"min_rating":5}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"testimonials","werkzeug":"filter_by_rating","stufe":"lesen","data":{"success":true,"data":[{"id":31877,"name":"Marta Beispiel","company":"Muster Gartenbau GmbH","city":"Musterstadt","ceo":"Marta Beispiel","position":"Geschäftsführerin","quote":"Seit der neuen Webseite kommen deutlich mehr Anfragen.","problem":"Zu wenige Anfragen über die Webseite.","solution":"Neue Landingpage mit klarer Handlungsaufforderung.","future":"Weitere Regionen erschließen.","image1desc":"Geschäftsführerin vor einem Gewächshaus","image2desc":"Dashboard mit steigenden Anfragen","rating":"5","variante":"schmerzloesung","state":"present","company_id":"4711","created_at":"2026-09-18T14:02:11+02:00","updated_at":"2026-09-18T14:02:11+02:00"}],"message":null,"timestamp":"2026-11-09 11:35:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/testimonials/get_testimonial_statistics":{"post":{"tags":["Kundenstimmen"],"operationId":"testimonials__get_testimonial_statistics","summary":"Kennzahlen der Kundenstimmen","description":"Berechnet aus allen Testimonials deiner Firma drei Kennzahlen: `total` (Anzahl), `average_rating` (Durchschnitt der Bewertungen, auf zwei Nachkommastellen gerundet, `0` bei leerem Bestand) und `by_variant` (Objekt, das je Variante die Anzahl nennt, zum Beispiel `schmerzloesung`, `process`, `zitat`). Einträge ohne Variante werden unter dem Schlüssel `unknown` gezählt. Einträge ohne Bewertung gehen mit dem Wert 0 in den Durchschnitt ein und senken ihn. Die Funktion hat keine Parameter. Sie eignet sich für einen schnellen Überblick, bevor du mit `filter_by_rating` oder `get_testimonials` in Details gehst.\n\n**Gut zu wissen**\n\n- Testimonials ohne Bewertung zählen als 0 und ziehen den Durchschnitt nach unten.\n- Die Funktion lädt alle Einträge der Firma; bei sehr großen Beständen kann sie länger dauern.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/testimonials/get_testimonial_statistics \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/testimonials/get_testimonial_statistics\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/testimonials/get_testimonial_statistics\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"testimonials","werkzeug":"get_testimonial_statistics","stufe":"lesen","data":{"success":true,"data":{"total":12,"average_rating":4.58,"by_variant":{"zitat":7,"schmerzloesung":4,"unknown":1}},"message":null,"timestamp":"2026-11-09 11:40:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/testimonials/export_testimonials":{"post":{"tags":["Kundenstimmen"],"operationId":"testimonials__export_testimonials","summary":"Kundenstimmen als JSON exportieren","description":"Gibt **alle** Testimonials deiner Firma in einer einzigen Antwort zurück, ohne Seiteneinteilung und ohne feste Sortierung. In `data.exported_at` steht der Zeitpunkt des Exports im ISO-8601-Format, in `data.items` die Liste der Einträge mit allen Feldern wie in `get_testimonial`. Die Funktion verändert nichts und hat keine Parameter. Sie eignet sich für Sicherungen, für den Abgleich mit anderen Systemen oder für den Import in eine Webseite. Bei sehr vielen Stimmen kann die Antwort groß werden; für gezielte Abfragen sind `get_testimonials` (mit `limit`) und `filter_by_rating` besser geeignet.\n\n**Gut zu wissen**\n\n- Es gibt keine Begrenzung der Anzahl; die Antwort wächst mit dem Bestand.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/testimonials/export_testimonials \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/testimonials/export_testimonials\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/testimonials/export_testimonials\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"testimonials","werkzeug":"export_testimonials","stufe":"lesen","data":{"success":true,"data":{"exported_at":"2026-11-09T11:45:00+01:00","items":[{"id":31877,"name":"Marta Beispiel","company":"Muster Gartenbau GmbH","city":"Musterstadt","ceo":"Marta Beispiel","position":"Geschäftsführerin","quote":"Seit der neuen Webseite kommen deutlich mehr Anfragen.","problem":"Zu wenige Anfragen über die Webseite.","solution":"Neue Landingpage mit klarer Handlungsaufforderung.","future":"Weitere Regionen erschließen.","image1desc":"Geschäftsführerin vor einem Gewächshaus","image2desc":"Dashboard mit steigenden Anfragen","rating":"5","variante":"schmerzloesung","state":"present","company_id":"4711","created_at":"2026-09-18T14:02:11+02:00","updated_at":"2026-09-18T14:02:11+02:00"}]},"message":null,"timestamp":"2026-11-09 11:45:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/offers/get_offers":{"post":{"tags":["Angebote im Katalog"],"operationId":"offers__get_offers","summary":"Angebote auflisten","description":"Liefert die Angebote deiner Firma, **neueste zuerst** (nach Erstellungsdatum absteigend), seitenweise. Das Ergebnis steht in `data.items`; dazu kommen `total` (Gesamtzahl der Treffer), `limit` und `offset`. Jeder Eintrag enthält `id`, `title`, `state`, `company_id`, `interview_id`, `created_at` und `updated_at`, aber **nicht** den Inhalt; den holst du mit `get_offer`. Filter kannst du kombinieren: `state` vergleicht exakt, `interview_id` zeigt nur Angebote zu einem Interview, `search` sucht als Teilzeichenfolge im Titel und im Inhalt (`content`). Ohne `limit` werden 25 Einträge geliefert. Zum Blättern erhöhst du `offset` um `limit`.\n\n**Gut zu wissen**\n\n- Die Liste enthält keinen `content`; dafür ist `get_offer` da.\n- `search` sucht im gespeicherten Inhaltstext (JSON), nicht nur in lesbaren Feldern; auch Schlüsselnamen können treffen.\n- `limit` hat die Untergrenze 1, aber keine feste Obergrenze.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/offers/get_offers \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"state\": \"present\",\n    \"search\": \"Beratung\",\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/offers/get_offers\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"state\": \"present\",\n        \"search\": \"Beratung\",\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/offers/get_offers\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"state\": \"present\",\n      \"search\": \"Beratung\",\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","description":"Optionaler Status-Filter als Text, zum Beispiel `present`. Es werden nur Angebote mit genau diesem Status geliefert (Groß-/Kleinschreibung nach Datenbank-Vergleich). Die bekannten Werte liefert `list_offer_states`.","example":"present"},"search":{"type":"string","description":"Optionaler Suchtext. Es werden Angebote geliefert, in deren Titel oder Inhalt der Text als Teilzeichenfolge vorkommt. Der Suchtext wird getrimmt und HTML-maskiert, Sonderzeichen wie `&` oder Anführungszeichen können deshalb nicht wie erwartet treffen.","example":"Beratung"},"interview_id":{"type":"integer","description":"Optional: ID eines Interviews als ganze Zahl. Es werden nur Angebote geliefert, die diesem Interview zugeordnet sind."},"limit":{"type":"integer","description":"Maximale Anzahl Einträge pro Antwort als ganze Zahl. Standard ist 25, der kleinste Wert ist 1. Es gibt keine feste Obergrenze.","example":10},"offset":{"type":"integer","description":"Anzahl der Einträge, die am Anfang der sortierten Liste übersprungen werden, zum seitenweisen Blättern. Standard ist 0, negative Werte werden zu 0."}}},"example":{"state":"present","search":"Beratung","limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"offers","werkzeug":"get_offers","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":52014,"title":"Starterpaket Webseiten-Beratung","state":"present","company_id":4711,"interview_id":null,"created_at":"2026-10-02T08:15:40+02:00","updated_at":"2026-10-20T16:03:12+02:00"}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 11:50:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/offers/get_offer":{"post":{"tags":["Angebote im Katalog"],"operationId":"offers__get_offer","summary":"Angebot abrufen","description":"Liefert ein einzelnes Angebot deiner Firma mit allen Feldern: `id`, `title`, `state`, `company_id`, `interview_id`, `created_at`, `updated_at` und dem Inhalt. Der Inhalt kommt doppelt: `content` ist der gespeicherte JSON-Text als Objekt aufgelöst (ein leeres Objekt, wenn nichts oder kein gültiges JSON-Objekt gespeichert ist), `raw_content` ist der unveränderte gespeicherte Text. Gehört die ID nicht zu deiner Firma oder existiert sie nicht, steht in `data` ein Objekt mit `success: false` und der Meldung „Angebot nicht gefunden oder kein Zugriff“. Die ID bekommst du aus `get_offers` oder `create_offer`.\n\n**Gut zu wissen**\n\n- Wurde `content` als reiner Text gespeichert, ist `content` leer und nur `raw_content` gefüllt.\n- Fehler stehen in `data.error`, nicht im HTTP-Status.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/offers/get_offer \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"offer_id\": 52014\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/offers/get_offer\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"offer_id\": 52014,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/offers/get_offer\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"offer_id\": 52014\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"offer_id":{"type":"integer","description":"Die numerische ID des Angebots, wie sie in `id` von `get_offers` oder `create_offer` zurückkommt. Pflichtangabe. Angebote anderer Firmen werden wie nicht vorhanden behandelt.","example":52014}},"required":["offer_id"]},"example":{"offer_id":52014}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"offers","werkzeug":"get_offer","stufe":"lesen","data":{"success":true,"data":{"id":52014,"title":"Starterpaket Webseiten-Beratung","state":"present","company_id":4711,"interview_id":null,"created_at":"2026-10-02T08:15:40+02:00","updated_at":"2026-10-20T16:03:12+02:00","content":{"title":"Webseiten-Beratung für den Start","description":"Zwei Workshops und ein schriftlicher Maßnahmenplan.","price":"1.490 € netto"},"raw_content":"{\"title\":\"Webseiten-Beratung für den Start\",\"description\":\"Zwei Workshops und ein schriftlicher Maßnahmenplan.\",\"price\":\"1.490 € netto\"}"},"message":null,"timestamp":"2026-11-09 11:52:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/offers/create_offer":{"post":{"tags":["Angebote im Katalog"],"operationId":"offers__create_offer","summary":"Angebot anlegen","description":"Legt ein neues Angebot im Katalog deiner Firma an und veröffentlicht es. Pflicht ist nur der `title`. Ohne `state` wird der Status `present` gesetzt. Den `content` übergibst du als JSON-Objekt; er wird als JSON-Text gespeichert (ein Text wird unverändert übernommen). Mit `interview_id` verknüpfst du das Angebot mit einem Interview. Der Eintrag wird im Ordner `/offers` abgelegt, der bei Bedarf angelegt wird; der interne Schlüssel wird aus dem Titel gebildet und bei Kollisionen durchnummeriert. Zurück kommt der vollständige neue Eintrag mit `content` und `raw_content` und der Meldung „Angebot erstellt“.\n\n**Gut zu wissen**\n\n- `title` und `state` werden getrimmt und HTML-maskiert gespeichert.\n- `state` wird nicht gegen die Werte aus `list_offer_states` geprüft.\n- `interview_id` = 0 wird wie nicht angegeben behandelt.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/offers/create_offer \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Starterpaket Webseiten-Beratung\",\n    \"content\": {\n        \"title\": \"Webseiten-Beratung für den Start\",\n        \"description\": \"Zwei Workshops und ein schriftlicher Maßnahmenplan.\",\n        \"price\": \"1.490 € netto\",\n        \"processSteps\": [\n            \"Kennenlernen\",\n            \"Workshop 1\",\n            \"Workshop 2\",\n            \"Maßnahmenplan\"\n        ]\n    },\n    \"state\": \"present\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/offers/create_offer\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"title\": \"Starterpaket Webseiten-Beratung\",\n        \"content\": {\n            \"title\": \"Webseiten-Beratung für den Start\",\n            \"description\": \"Zwei Workshops und ein schriftlicher Maßnahmenplan.\",\n            \"price\": \"1.490 € netto\",\n            \"processSteps\": [\n                \"Kennenlernen\",\n                \"Workshop 1\",\n                \"Workshop 2\",\n                \"Maßnahmenplan\",\n            ],\n        },\n        \"state\": \"present\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/offers/create_offer\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"title\": \"Starterpaket Webseiten-Beratung\",\n      \"content\": {\n          \"title\": \"Webseiten-Beratung für den Start\",\n          \"description\": \"Zwei Workshops und ein schriftlicher Maßnahmenplan.\",\n          \"price\": \"1.490 € netto\",\n          \"processSteps\": [\n              \"Kennenlernen\",\n              \"Workshop 1\",\n              \"Workshop 2\",\n              \"Maßnahmenplan\"\n          ]\n      },\n      \"state\": \"present\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Titel des Angebots. Pflichtangabe, darf nicht leer sein. Der Wert wird getrimmt und HTML-maskiert gespeichert und dient auch als Grundlage für den internen Schlüssel.","example":"Starterpaket Webseiten-Beratung"},"content":{"type":"object","description":"Optional: Inhalt als JSON-Objekt, zum Beispiel mit den Schlüsseln `title`, `description`, `price`, `needs`, `targetAudience` und `processSteps` (Liste), die `summarize_offer` auswertet. Weitere Schlüssel sind erlaubt und bleiben erhalten. Ein reiner Text wird unverändert gespeichert.","example":{"title":"Webseiten-Beratung für den Start","description":"Zwei Workshops und ein schriftlicher Maßnahmenplan.","price":"1.490 € netto","processSteps":["Kennenlernen","Workshop 1","Workshop 2","Maßnahmenplan"]}},"state":{"type":"string","description":"Optional: Status des Angebots als Text, zum Beispiel `present` (aktuell) oder `future` (geplant). Standard ist `present`. Die bekannten Werte nennt `list_offer_states`; eine Prüfung findet nicht statt.","example":"present"},"interview_id":{"type":"integer","description":"Optional: ID eines Interviews, mit dem das Angebot verknüpft wird, als ganze Zahl. Ohne Angabe bleibt das Feld leer."}},"required":["title"]},"example":{"title":"Starterpaket Webseiten-Beratung","content":{"title":"Webseiten-Beratung für den Start","description":"Zwei Workshops und ein schriftlicher Maßnahmenplan.","price":"1.490 € netto","processSteps":["Kennenlernen","Workshop 1","Workshop 2","Maßnahmenplan"]},"state":"present"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"offers","werkzeug":"create_offer","stufe":"schreiben","data":{"success":true,"data":{"id":52014,"title":"Starterpaket Webseiten-Beratung","state":"present","company_id":4711,"interview_id":null,"created_at":"2026-11-09T11:55:00+01:00","updated_at":"2026-11-09T11:55:00+01:00","content":{"title":"Webseiten-Beratung für den Start","description":"Zwei Workshops und ein schriftlicher Maßnahmenplan.","price":"1.490 € netto","processSteps":["Kennenlernen","Workshop 1","Workshop 2","Maßnahmenplan"]},"raw_content":"{\"title\":\"Webseiten-Beratung für den Start\",\"description\":\"Zwei Workshops und ein schriftlicher Maßnahmenplan.\",\"price\":\"1.490 € netto\",\"processSteps\":[\"Kennenlernen\",\"Workshop 1\",\"Workshop 2\",\"Maßnahmenplan\"]}"},"message":"Angebot erstellt","timestamp":"2026-11-09 11:55:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/offers/update_offer":{"post":{"tags":["Angebote im Katalog"],"operationId":"offers__update_offer","summary":"Angebot ändern","description":"Ändert Felder eines bestehenden Angebots. Im Objekt `data` gibst du nur die Felder an, die sich ändern sollen. Wirksam sind `title`, `state`, `interview_id` und `content`. Ein leeres `data` wird mit der Meldung „Keine Daten zum Aktualisieren übergeben“ abgelehnt. **Achtung bei `content`:** Der Inhalt wird als Ganzes ersetzt, nicht schrittweise zusammengeführt. Willst du nur ein Feld darin ändern, lies zuerst mit `get_offer` den bisherigen `content`, passe ihn an und sende ihn vollständig zurück. Zurück kommt der aktualisierte Eintrag mit `content` und `raw_content` und der Meldung „Angebot aktualisiert“.\n\n**Gut zu wissen**\n\n- `content` ersetzt den bisherigen Inhalt vollständig; fehlende Schlüssel gehen verloren.\n- `title`, `state` und `interview_id` werden nur übernommen, wenn sie nicht `null` sind.\n- Ein `content` von `null` überschreibt den Inhalt; er ist danach leer.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/offers/update_offer \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"offer_id\": 52014,\n    \"data\": {\n        \"state\": \"future\",\n        \"title\": \"Starterpaket Webseiten-Beratung (ab Januar)\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/offers/update_offer\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"offer_id\": 52014,\n        \"data\": {\n            \"state\": \"future\",\n            \"title\": \"Starterpaket Webseiten-Beratung (ab Januar)\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/offers/update_offer\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"offer_id\": 52014,\n      \"data\": {\n          \"state\": \"future\",\n          \"title\": \"Starterpaket Webseiten-Beratung (ab Januar)\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"offer_id":{"type":"integer","description":"Die ID des zu ändernden Angebots, wie sie `get_offers` oder `create_offer` liefert. Pflichtangabe; Angebote anderer Firmen werden abgelehnt.","example":52014},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern. Wirksame Schlüssel: `title` (Text), `state` (Text), `interview_id` (ganze Zahl) und `content` (JSON-Objekt oder Text, ersetzt den gesamten Inhalt). Pflichtangabe und nicht leer; unbekannte Schlüssel werden ignoriert.","example":{"state":"future","title":"Starterpaket Webseiten-Beratung (ab Januar)"}}},"required":["offer_id","data"]},"example":{"offer_id":52014,"data":{"state":"future","title":"Starterpaket Webseiten-Beratung (ab Januar)"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"offers","werkzeug":"update_offer","stufe":"schreiben","data":{"success":true,"data":{"id":52014,"title":"Starterpaket Webseiten-Beratung (ab Januar)","state":"future","company_id":4711,"interview_id":null,"created_at":"2026-11-09T11:55:00+01:00","updated_at":"2026-11-09T12:05:30+01:00","content":{"title":"Webseiten-Beratung für den Start","description":"Zwei Workshops und ein schriftlicher Maßnahmenplan.","price":"1.490 € netto"},"raw_content":"{\"title\":\"Webseiten-Beratung für den Start\",\"description\":\"Zwei Workshops und ein schriftlicher Maßnahmenplan.\",\"price\":\"1.490 € netto\"}"},"message":"Angebot aktualisiert","timestamp":"2026-11-09 12:05:30","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/offers/delete_offer":{"post":{"tags":["Angebote im Katalog"],"operationId":"offers__delete_offer","summary":"Angebot löschen","description":"Löscht ein Angebot deiner Firma **endgültig**. Über diese API gibt es keinen Papierkorb und keine Wiederherstellung. Die Antwort enthält keine Daten: `data` ist `null`, die Meldung lautet „Angebot gelöscht“. Weil die Aktion unumkehrbar ist, verlangt die API im Körper zusätzlich `\"bestaetigt\": true`, sonst antwortet sie mit HTTP 409. Wenn du ein Angebot nur nicht mehr anbieten willst, ist es meist besser, es mit `update_offer` auf einen anderen Status wie `retired` zu setzen; vor dem Löschen kannst du es mit `copy_offer` oder `get_offer` sichern.\n\n**Gut zu wissen**\n\n- Ohne `\"bestaetigt\": true` wird die Anfrage mit 409 abgelehnt.\n- Die Antwort enthält die gelöschte ID nicht; merke sie dir vorher.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/offers/delete_offer \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"offer_id\": 52014,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/offers/delete_offer\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"offer_id\": 52014,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/offers/delete_offer\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"offer_id\": 52014,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"offer_id":{"type":"integer","description":"Die ID des zu löschenden Angebots. Pflichtangabe. Das Angebot muss zu deiner Firma gehören, sonst kommt die Fehlermeldung „Angebot nicht gefunden oder kein Zugriff“.","example":52014},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["offer_id","bestaetigt"]},"example":{"offer_id":52014,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"offers","werkzeug":"delete_offer","stufe":"loeschen","data":{"success":true,"data":null,"message":"Angebot gelöscht","timestamp":"2026-11-09 12:10:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/offers/copy_offer":{"post":{"tags":["Angebote im Katalog"],"operationId":"offers__copy_offer","summary":"Angebot kopieren","description":"Dupliziert ein bestehendes Angebot deiner Firma. Übernommen werden Inhalt (`content`) und Interview-Verknüpfung; die Kopie liegt im selben Ordner wie das Original. Ohne `title` heißt sie „<Originaltitel> (Kopie)“, ohne `state` übernimmt sie den Status des Originals (ersatzweise `present`). Wichtig: Die Kopie wird **nicht veröffentlicht**, sondern als unveröffentlichter Entwurf angelegt, anders als bei `create_offer`. Zurück kommt der neue Eintrag mit `content` und `raw_content` und der Meldung „Angebot kopiert“. Typisch, um eine Variante für eine andere Zielgruppe zu erstellen, die du danach mit `update_offer` anpasst.\n\n**Gut zu wissen**\n\n- Die Kopie ist unveröffentlicht (Entwurf), das Original bleibt unverändert.\n- `title` und `state` werden getrimmt und HTML-maskiert gespeichert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/offers/copy_offer \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"offer_id\": 52014,\n    \"title\": \"Starterpaket für Handwerksbetriebe\",\n    \"state\": \"draft\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/offers/copy_offer\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"offer_id\": 52014,\n        \"title\": \"Starterpaket für Handwerksbetriebe\",\n        \"state\": \"draft\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/offers/copy_offer\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"offer_id\": 52014,\n      \"title\": \"Starterpaket für Handwerksbetriebe\",\n      \"state\": \"draft\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"offer_id":{"type":"integer","description":"Die ID des Quell-Angebots, das kopiert werden soll. Pflichtangabe; das Angebot muss zu deiner Firma gehören.","example":52014},"title":{"type":"string","description":"Optional: Titel der Kopie. Ohne Angabe wird der Originaltitel mit dem Zusatz „ (Kopie)“ verwendet. Der Wert wird getrimmt und HTML-maskiert gespeichert.","example":"Starterpaket für Handwerksbetriebe"},"state":{"type":"string","description":"Optional: Status der Kopie als Text, zum Beispiel `draft` oder `future`. Ohne Angabe wird der Status des Originals übernommen.","example":"draft"}},"required":["offer_id"]},"example":{"offer_id":52014,"title":"Starterpaket für Handwerksbetriebe","state":"draft"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"offers","werkzeug":"copy_offer","stufe":"schreiben","data":{"success":true,"data":{"id":52031,"title":"Starterpaket für Handwerksbetriebe","state":"draft","company_id":4711,"interview_id":null,"created_at":"2026-11-09T12:15:00+01:00","updated_at":"2026-11-09T12:15:00+01:00","content":{"title":"Webseiten-Beratung für den Start","description":"Zwei Workshops und ein schriftlicher Maßnahmenplan.","price":"1.490 € netto"},"raw_content":"{\"title\":\"Webseiten-Beratung für den Start\",\"description\":\"Zwei Workshops und ein schriftlicher Maßnahmenplan.\",\"price\":\"1.490 € netto\"}"},"message":"Angebot kopiert","timestamp":"2026-11-09 12:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/offers/summarize_offer":{"post":{"tags":["Angebote im Katalog"],"operationId":"offers__summarize_offer","summary":"Angebot zusammenfassen","description":"Liest ein Angebot und liefert zusätzlich zum vollständigen Eintrag (`data.offer`, mit `content` und `raw_content`) eine kompakte Zusammenfassung in `data.summary`. Die Zusammenfassung wird **ohne KI-Aufruf** direkt aus dem gespeicherten `content` gebildet: `headline` (`content.title`, sonst der Angebotstitel), `description`, `price`, `needs`, `target_audience` (aus `content.targetAudience`) und `process_steps` (aus `content.processSteps`, sonst eine leere Liste). Fehlt ein Schlüssel im Inhalt, ist der Wert `null`. Die Funktion verändert nichts, ist im Katalog aber als schreibend eingestuft; dein Schlüssel braucht dafür das Recht „schreiben“.\n\n**Gut zu wissen**\n\n- Die Zusammenfassung ist keine Textverdichtung, sondern ein Auszug bestimmter Schlüssel aus `content`.\n- Der Schlüssel braucht das Recht „schreiben“, obwohl nichts verändert wird.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/offers/summarize_offer \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"offer_id\": 52014\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/offers/summarize_offer\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"offer_id\": 52014,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/offers/summarize_offer\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"offer_id\": 52014\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"offer_id":{"type":"integer","description":"Die ID des Angebots, das zusammengefasst werden soll. Pflichtangabe; das Angebot muss zu deiner Firma gehören, sonst kommt eine Fehlermeldung.","example":52014}},"required":["offer_id"]},"example":{"offer_id":52014}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"offers","werkzeug":"summarize_offer","stufe":"lesen","data":{"success":true,"data":{"offer":{"id":52014,"title":"Starterpaket Webseiten-Beratung","state":"present","company_id":4711,"interview_id":null,"created_at":"2026-11-09T11:55:00+01:00","updated_at":"2026-11-09T11:55:00+01:00","content":{"title":"Webseiten-Beratung für den Start","description":"Zwei Workshops und ein schriftlicher Maßnahmenplan.","price":"1.490 € netto"},"raw_content":"{\"title\":\"Webseiten-Beratung für den Start\",\"description\":\"Zwei Workshops und ein schriftlicher Maßnahmenplan.\",\"price\":\"1.490 € netto\"}"},"summary":{"headline":"Webseiten-Beratung für den Start","description":"Zwei Workshops und ein schriftlicher Maßnahmenplan.","price":"1.490 € netto","needs":null,"target_audience":null,"process_steps":[]}},"message":null,"timestamp":"2026-11-09 12:20:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/offers/list_offer_states":{"post":{"tags":["Angebote im Katalog"],"operationId":"offers__list_offer_states","summary":"Bekannte Angebotsstatus auflisten","description":"Gibt die Liste der bekannten Statuswerte für Angebote zurück: `present`, `future`, `retired` und `draft`. Die Liste ist fest hinterlegt und hängt nicht von deinen Daten ab; sie sagt also nicht, welche Status tatsächlich bei deinen Angeboten vorkommen. Die Funktion hat keine Parameter. Nutze sie, um gültige Werte für die Filter in `get_offers` sowie für `create_offer`, `update_offer` und `copy_offer` zu wählen. Beim Speichern prüft die Plattform den Status nicht gegen diese Liste, deshalb lohnt es sich, bei den hier genannten Werten zu bleiben.\n\n**Gut zu wissen**\n\n- Die Liste ist statisch; vorhandene Angebote können davon abweichende Werte tragen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/offers/list_offer_states \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/offers/list_offer_states\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/offers/list_offer_states\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"offers","werkzeug":"list_offer_states","stufe":"lesen","data":{"success":true,"data":{"states":["present","future","retired","draft"]},"message":null,"timestamp":"2026-11-09 12:22:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/expertise/get_expertises":{"post":{"tags":["Expertise"],"operationId":"expertise__get_expertises","summary":"Expertise-Einträge auflisten","description":"Liefert die Expertise-Einträge deiner Firma, **neueste zuerst** (nach Erstellungsdatum absteigend), seitenweise. Das Ergebnis steht in `data.items`; dazu kommen `total` (Gesamtzahl der Treffer), `limit` und `offset`. Jeder Eintrag enthält `id`, `title`, `state`, `company_id`, `interview_id`, `created_at` und `updated_at`, aber **nicht** den Inhalt; den holst du mit `get_expertise`. Filter kannst du kombinieren: `state` vergleicht exakt, `interview_id` zeigt nur Einträge zu einem Interview, `search` sucht als Teilzeichenfolge im Titel und im Inhalt (`content`). Ohne `limit` werden 25 Einträge geliefert. Zum Blättern erhöhst du `offset` um `limit`.\n\n**Gut zu wissen**\n\n- Die Liste enthält keinen `content`; dafür ist `get_expertise` da.\n- `search` sucht im gespeicherten Inhaltstext (JSON), nicht nur in lesbaren Feldern.\n- `limit` hat die Untergrenze 1, aber keine feste Obergrenze.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/expertise/get_expertises \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"state\": \"active\",\n    \"search\": \"Reklamation\",\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/expertise/get_expertises\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"state\": \"active\",\n        \"search\": \"Reklamation\",\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/expertise/get_expertises\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"state\": \"active\",\n      \"search\": \"Reklamation\",\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","description":"Optionaler Status-Filter als Text, zum Beispiel `active`. Es werden nur Einträge mit genau diesem Status geliefert. Die bekannten Werte liefert `list_expertise_states`.","example":"active"},"search":{"type":"string","description":"Optionaler Suchtext. Es werden Einträge geliefert, in deren Titel oder Inhalt der Text als Teilzeichenfolge vorkommt. Der Suchtext wird getrimmt und HTML-maskiert, Sonderzeichen wie `&` oder Anführungszeichen können deshalb nicht wie erwartet treffen.","example":"Reklamation"},"interview_id":{"type":"integer","description":"Optional: ID eines Interviews als ganze Zahl. Es werden nur Einträge geliefert, die diesem Interview zugeordnet sind."},"limit":{"type":"integer","description":"Maximale Anzahl Einträge pro Antwort als ganze Zahl. Standard ist 25, der kleinste Wert ist 1. Es gibt keine feste Obergrenze.","example":10},"offset":{"type":"integer","description":"Anzahl der Einträge, die am Anfang der sortierten Liste übersprungen werden, zum seitenweisen Blättern. Standard ist 0, negative Werte werden zu 0."}}},"example":{"state":"active","search":"Reklamation","limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"expertise","werkzeug":"get_expertises","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":61120,"title":"Umgang mit Reklamationen im Handwerk","state":"active","company_id":4711,"interview_id":7340,"created_at":"2026-10-05T10:22:18+02:00","updated_at":"2026-10-05T10:22:18+02:00"}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 12:30:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/expertise/get_expertise":{"post":{"tags":["Expertise"],"operationId":"expertise__get_expertise","summary":"Expertise-Eintrag abrufen","description":"Liefert einen einzelnen Expertise-Eintrag deiner Firma mit allen Feldern: `id`, `title`, `state`, `company_id`, `interview_id`, `created_at`, `updated_at` und dem Inhalt. Der Inhalt kommt doppelt: `content` ist der gespeicherte JSON-Text als Objekt aufgelöst (ein leeres Objekt, wenn nichts oder kein gültiges JSON-Objekt gespeichert ist), `raw_content` ist der unveränderte gespeicherte Text. Gehört die ID nicht zu deiner Firma oder existiert sie nicht, steht in `data` ein Objekt mit `success: false` und der Meldung „Expertise nicht gefunden oder kein Zugriff“. Die ID bekommst du aus `get_expertises` oder `create_expertise`.\n\n**Gut zu wissen**\n\n- Wurde `content` als reiner Text gespeichert, ist `content` leer und nur `raw_content` gefüllt.\n- Fehler stehen in `data.error`, nicht im HTTP-Status.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/expertise/get_expertise \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"expertise_id\": 61120\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/expertise/get_expertise\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"expertise_id\": 61120,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/expertise/get_expertise\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"expertise_id\": 61120\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"expertise_id":{"type":"integer","description":"Die numerische ID des Expertise-Eintrags, wie sie in `id` von `get_expertises` oder `create_expertise` zurückkommt. Pflichtangabe. Einträge anderer Firmen werden wie nicht vorhanden behandelt.","example":61120}},"required":["expertise_id"]},"example":{"expertise_id":61120}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"expertise","werkzeug":"get_expertise","stufe":"lesen","data":{"success":true,"data":{"id":61120,"title":"Umgang mit Reklamationen im Handwerk","state":"active","company_id":4711,"interview_id":7340,"created_at":"2026-10-05T10:22:18+02:00","updated_at":"2026-10-05T10:22:18+02:00","content":{"title":"Reklamationen schnell und fair lösen","initial_expertise_identification":"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.","recommendations_and_methods":["Innerhalb von 24 Stunden antworten","Vor-Ort-Termin anbieten"]},"raw_content":"{\"title\":\"Reklamationen schnell und fair lösen\",\"initial_expertise_identification\":\"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.\",\"recommendations_and_methods\":[\"Innerhalb von 24 Stunden antworten\",\"Vor-Ort-Termin anbieten\"]}"},"message":null,"timestamp":"2026-11-09 12:32:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/expertise/create_expertise":{"post":{"tags":["Expertise"],"operationId":"expertise__create_expertise","summary":"Expertise-Eintrag anlegen","description":"Legt einen neuen Expertise-Eintrag für deine Firma an und veröffentlicht ihn. Pflicht ist nur der `title`. Ohne `state` wird der Status `active` gesetzt. Den `content` übergibst du als JSON-Objekt; er wird als JSON-Text gespeichert (ein Text wird unverändert übernommen). Mit `interview_id` verknüpfst du den Eintrag mit einem Interview. Der Eintrag wird im Ordner `/expertise` abgelegt, der bei Bedarf angelegt wird; der interne Schlüssel wird aus dem Titel gebildet und bei Kollisionen durchnummeriert. Zurück kommt der vollständige neue Eintrag mit `content` und `raw_content` und der Meldung „Expertise erstellt“.\n\n**Gut zu wissen**\n\n- `title` und `state` werden getrimmt und HTML-maskiert gespeichert.\n- `state` wird nicht gegen die Werte aus `list_expertise_states` geprüft.\n- `interview_id` = 0 wird wie nicht angegeben behandelt.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/expertise/create_expertise \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Umgang mit Reklamationen im Handwerk\",\n    \"content\": {\n        \"title\": \"Reklamationen schnell und fair lösen\",\n        \"initial_expertise_identification\": \"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.\",\n        \"recommendations_and_methods\": [\n            \"Innerhalb von 24 Stunden antworten\",\n            \"Vor-Ort-Termin anbieten\"\n        ]\n    },\n    \"state\": \"draft\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/expertise/create_expertise\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"title\": \"Umgang mit Reklamationen im Handwerk\",\n        \"content\": {\n            \"title\": \"Reklamationen schnell und fair lösen\",\n            \"initial_expertise_identification\": \"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.\",\n            \"recommendations_and_methods\": [\n                \"Innerhalb von 24 Stunden antworten\",\n                \"Vor-Ort-Termin anbieten\",\n            ],\n        },\n        \"state\": \"draft\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/expertise/create_expertise\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"title\": \"Umgang mit Reklamationen im Handwerk\",\n      \"content\": {\n          \"title\": \"Reklamationen schnell und fair lösen\",\n          \"initial_expertise_identification\": \"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.\",\n          \"recommendations_and_methods\": [\n              \"Innerhalb von 24 Stunden antworten\",\n              \"Vor-Ort-Termin anbieten\"\n          ]\n      },\n      \"state\": \"draft\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Titel des Expertise-Eintrags. Pflichtangabe, darf nicht leer sein. Der Wert wird getrimmt und HTML-maskiert gespeichert und dient auch als Grundlage für den internen Schlüssel.","example":"Umgang mit Reklamationen im Handwerk"},"content":{"type":"object","description":"Optional: Inhalt als JSON-Objekt, zum Beispiel mit den Schlüsseln `title`, `initial_expertise_identification`, `process_analysis`, `experience_and_learning_curve` und `recommendations_and_methods`, die `summarize_expertise` auswertet. Weitere Schlüssel sind erlaubt und bleiben erhalten. Ein reiner Text wird unverändert gespeichert.","example":{"title":"Reklamationen schnell und fair lösen","initial_expertise_identification":"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.","recommendations_and_methods":["Innerhalb von 24 Stunden antworten","Vor-Ort-Termin anbieten"]}},"state":{"type":"string","description":"Optional: Status des Eintrags als Text, zum Beispiel `active` oder `draft`. Standard ist `active`. Die bekannten Werte nennt `list_expertise_states`; eine Prüfung findet nicht statt.","example":"draft"},"interview_id":{"type":"integer","description":"Optional: ID eines Interviews, aus dem der Eintrag stammt, als ganze Zahl. Ohne Angabe bleibt das Feld leer."}},"required":["title"]},"example":{"title":"Umgang mit Reklamationen im Handwerk","content":{"title":"Reklamationen schnell und fair lösen","initial_expertise_identification":"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.","recommendations_and_methods":["Innerhalb von 24 Stunden antworten","Vor-Ort-Termin anbieten"]},"state":"draft"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"expertise","werkzeug":"create_expertise","stufe":"schreiben","data":{"success":true,"data":{"id":61120,"title":"Umgang mit Reklamationen im Handwerk","state":"draft","company_id":4711,"interview_id":null,"created_at":"2026-11-09T12:35:00+01:00","updated_at":"2026-11-09T12:35:00+01:00","content":{"title":"Reklamationen schnell und fair lösen","initial_expertise_identification":"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.","recommendations_and_methods":["Innerhalb von 24 Stunden antworten","Vor-Ort-Termin anbieten"]},"raw_content":"{\"title\":\"Reklamationen schnell und fair lösen\",\"initial_expertise_identification\":\"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.\",\"recommendations_and_methods\":[\"Innerhalb von 24 Stunden antworten\",\"Vor-Ort-Termin anbieten\"]}"},"message":"Expertise erstellt","timestamp":"2026-11-09 12:35:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/expertise/update_expertise":{"post":{"tags":["Expertise"],"operationId":"expertise__update_expertise","summary":"Expertise-Eintrag ändern","description":"Ändert Felder eines bestehenden Expertise-Eintrags. Im Objekt `data` gibst du nur die Felder an, die sich ändern sollen. Wirksam sind `title`, `state`, `interview_id` und `content`. Ein leeres `data` wird mit der Meldung „Keine Daten zum Aktualisieren übergeben“ abgelehnt. **Achtung bei `content`:** Der Inhalt wird als Ganzes ersetzt, nicht schrittweise zusammengeführt. Willst du nur einen Abschnitt darin ändern, lies zuerst mit `get_expertise` den bisherigen `content`, passe ihn an und sende ihn vollständig zurück. Zurück kommt der aktualisierte Eintrag mit `content` und `raw_content` und der Meldung „Expertise aktualisiert“.\n\n**Gut zu wissen**\n\n- `content` ersetzt den bisherigen Inhalt vollständig; fehlende Schlüssel gehen verloren.\n- `title`, `state` und `interview_id` werden nur übernommen, wenn sie nicht `null` sind.\n- Ein `content` von `null` überschreibt den Inhalt; er ist danach leer.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/expertise/update_expertise \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"expertise_id\": 61120,\n    \"data\": {\n        \"state\": \"active\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/expertise/update_expertise\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"expertise_id\": 61120,\n        \"data\": {\n            \"state\": \"active\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/expertise/update_expertise\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"expertise_id\": 61120,\n      \"data\": {\n          \"state\": \"active\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"expertise_id":{"type":"integer","description":"Die ID des zu ändernden Eintrags, wie sie `get_expertises` oder `create_expertise` liefert. Pflichtangabe; Einträge anderer Firmen werden abgelehnt.","example":61120},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern. Wirksame Schlüssel: `title` (Text), `state` (Text), `interview_id` (ganze Zahl) und `content` (JSON-Objekt oder Text, ersetzt den gesamten Inhalt). Pflichtangabe und nicht leer; unbekannte Schlüssel werden ignoriert.","example":{"state":"active"}}},"required":["expertise_id","data"]},"example":{"expertise_id":61120,"data":{"state":"active"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"expertise","werkzeug":"update_expertise","stufe":"schreiben","data":{"success":true,"data":{"id":61120,"title":"Umgang mit Reklamationen im Handwerk","state":"active","company_id":4711,"interview_id":null,"created_at":"2026-11-09T12:35:00+01:00","updated_at":"2026-11-09T12:41:10+01:00","content":{"title":"Reklamationen schnell und fair lösen"},"raw_content":"{\"title\":\"Reklamationen schnell und fair lösen\"}"},"message":"Expertise aktualisiert","timestamp":"2026-11-09 12:41:10","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/expertise/delete_expertise":{"post":{"tags":["Expertise"],"operationId":"expertise__delete_expertise","summary":"Expertise-Eintrag löschen","description":"Löscht einen Expertise-Eintrag deiner Firma **endgültig**. Über diese API gibt es keinen Papierkorb und keine Wiederherstellung. Die Antwort enthält keine Daten: `data` ist `null`, die Meldung lautet „Expertise gelöscht“. Weil die Aktion unumkehrbar ist, verlangt die API im Körper zusätzlich `\"bestaetigt\": true`, sonst antwortet sie mit HTTP 409. Wenn du einen Eintrag nur aus dem Verkehr ziehen möchtest, setze ihn stattdessen mit `update_expertise` auf den Status `archived`; vor dem Löschen kannst du ihn mit `get_expertise` oder `copy_expertise` sichern.\n\n**Gut zu wissen**\n\n- Ohne `\"bestaetigt\": true` wird die Anfrage mit 409 abgelehnt.\n- Die Antwort enthält die gelöschte ID nicht; merke sie dir vorher.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/expertise/delete_expertise \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"expertise_id\": 61120,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/expertise/delete_expertise\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"expertise_id\": 61120,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/expertise/delete_expertise\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"expertise_id\": 61120,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"expertise_id":{"type":"integer","description":"Die ID des zu löschenden Expertise-Eintrags. Pflichtangabe. Der Eintrag muss zu deiner Firma gehören, sonst kommt die Fehlermeldung „Expertise nicht gefunden oder kein Zugriff“.","example":61120},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["expertise_id","bestaetigt"]},"example":{"expertise_id":61120,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"expertise","werkzeug":"delete_expertise","stufe":"loeschen","data":{"success":true,"data":null,"message":"Expertise gelöscht","timestamp":"2026-11-09 12:45:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/expertise/copy_expertise":{"post":{"tags":["Expertise"],"operationId":"expertise__copy_expertise","summary":"Expertise-Eintrag kopieren","description":"Dupliziert einen bestehenden Expertise-Eintrag deiner Firma. Übernommen werden Inhalt (`content`) und Interview-Verknüpfung; die Kopie liegt im selben Ordner wie das Original. Ohne `title` heißt sie „<Originaltitel> (Kopie)“, ohne `state` übernimmt sie den Status des Originals (ersatzweise `active`). Wichtig: Die Kopie wird **nicht veröffentlicht**, sondern als unveröffentlichter Entwurf angelegt, anders als bei `create_expertise`. Zurück kommt der neue Eintrag mit `content` und `raw_content` und der Meldung „Expertise kopiert“. Typisch, um eine Variante für ein anderes Thema zu erstellen, die du danach mit `update_expertise` anpasst.\n\n**Gut zu wissen**\n\n- Die Kopie ist unveröffentlicht (Entwurf), das Original bleibt unverändert.\n- `title` und `state` werden getrimmt und HTML-maskiert gespeichert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/expertise/copy_expertise \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"expertise_id\": 61120,\n    \"title\": \"Umgang mit Reklamationen im Handel\",\n    \"state\": \"draft\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/expertise/copy_expertise\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"expertise_id\": 61120,\n        \"title\": \"Umgang mit Reklamationen im Handel\",\n        \"state\": \"draft\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/expertise/copy_expertise\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"expertise_id\": 61120,\n      \"title\": \"Umgang mit Reklamationen im Handel\",\n      \"state\": \"draft\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"expertise_id":{"type":"integer","description":"Die ID des Eintrags, der kopiert werden soll. Pflichtangabe; der Eintrag muss zu deiner Firma gehören.","example":61120},"title":{"type":"string","description":"Optional: Titel der Kopie. Ohne Angabe wird der Originaltitel mit dem Zusatz „ (Kopie)“ verwendet. Der Wert wird getrimmt und HTML-maskiert gespeichert.","example":"Umgang mit Reklamationen im Handel"},"state":{"type":"string","description":"Optional: Status der Kopie als Text, zum Beispiel `draft`. Ohne Angabe wird der Status des Originals übernommen.","example":"draft"}},"required":["expertise_id"]},"example":{"expertise_id":61120,"title":"Umgang mit Reklamationen im Handel","state":"draft"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"expertise","werkzeug":"copy_expertise","stufe":"schreiben","data":{"success":true,"data":{"id":61138,"title":"Umgang mit Reklamationen im Handel","state":"draft","company_id":4711,"interview_id":null,"created_at":"2026-11-09T12:50:00+01:00","updated_at":"2026-11-09T12:50:00+01:00","content":{"title":"Reklamationen schnell und fair lösen"},"raw_content":"{\"title\":\"Reklamationen schnell und fair lösen\"}"},"message":"Expertise kopiert","timestamp":"2026-11-09 12:50:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/expertise/summarize_expertise":{"post":{"tags":["Expertise"],"operationId":"expertise__summarize_expertise","summary":"Expertise-Eintrag zusammenfassen","description":"Liest einen Expertise-Eintrag und liefert zusätzlich zum vollständigen Eintrag (`data.expertise`, mit `content` und `raw_content`) eine Kurzfassung in `data.summary`. Die Kurzfassung wird **ohne KI-Aufruf** direkt aus dem gespeicherten `content` gebildet: `title` (`content.title`, sonst der Eintragstitel), `initial_expertise_identification`, `process_analysis`, `experience_and_learning_curve` und `recommendations_and_methods`. Fehlt ein Schlüssel im Inhalt, ist der Wert `null` (bei `initial_expertise_identification`) bzw. eine leere Liste (bei den drei anderen). Die Funktion verändert nichts, ist im Katalog aber als schreibend eingestuft; dein Schlüssel braucht dafür das Recht „schreiben“.\n\n**Gut zu wissen**\n\n- Die Kurzfassung ist ein Auszug bestimmter Schlüssel aus `content`, keine Textverdichtung.\n- Der Schlüssel braucht das Recht „schreiben“, obwohl nichts verändert wird.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/expertise/summarize_expertise \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"expertise_id\": 61120\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/expertise/summarize_expertise\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"expertise_id\": 61120,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/expertise/summarize_expertise\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"expertise_id\": 61120\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"expertise_id":{"type":"integer","description":"Die ID des Expertise-Eintrags, der zusammengefasst werden soll. Pflichtangabe; der Eintrag muss zu deiner Firma gehören, sonst kommt eine Fehlermeldung.","example":61120}},"required":["expertise_id"]},"example":{"expertise_id":61120}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"expertise","werkzeug":"summarize_expertise","stufe":"lesen","data":{"success":true,"data":{"expertise":{"id":61120,"title":"Umgang mit Reklamationen im Handwerk","state":"active","company_id":4711,"interview_id":null,"created_at":"2026-11-09T12:35:00+01:00","updated_at":"2026-11-09T12:41:10+01:00","content":{"title":"Reklamationen schnell und fair lösen","initial_expertise_identification":"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.","recommendations_and_methods":["Innerhalb von 24 Stunden antworten","Vor-Ort-Termin anbieten"]},"raw_content":"{\"title\":\"Reklamationen schnell und fair lösen\",\"initial_expertise_identification\":\"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.\",\"recommendations_and_methods\":[\"Innerhalb von 24 Stunden antworten\",\"Vor-Ort-Termin anbieten\"]}"},"summary":{"title":"Reklamationen schnell und fair lösen","initial_expertise_identification":"Langjährige Erfahrung im Kundendienst eines Installationsbetriebs.","process_analysis":[],"experience_and_learning_curve":[],"recommendations_and_methods":["Innerhalb von 24 Stunden antworten","Vor-Ort-Termin anbieten"]}},"message":null,"timestamp":"2026-11-09 12:55:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/expertise/list_expertise_states":{"post":{"tags":["Expertise"],"operationId":"expertise__list_expertise_states","summary":"Bekannte Expertise-Status auflisten","description":"Gibt die Liste der bekannten Statuswerte für Expertise-Einträge zurück: `active`, `draft` und `archived`. Die Liste ist fest hinterlegt und hängt nicht von deinen Daten ab; sie sagt also nicht, welche Status tatsächlich bei deinen Einträgen vorkommen. Die Funktion hat keine Parameter. Nutze sie, um gültige Werte für den Filter in `get_expertises` sowie für `create_expertise`, `update_expertise` und `copy_expertise` zu wählen. Beim Speichern prüft die Plattform den Status nicht gegen diese Liste, deshalb lohnt es sich, bei den hier genannten Werten zu bleiben.\n\n**Gut zu wissen**\n\n- Die Liste ist statisch; vorhandene Einträge können davon abweichende Werte tragen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/expertise/list_expertise_states \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/expertise/list_expertise_states\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/expertise/list_expertise_states\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"expertise","werkzeug":"list_expertise_states","stufe":"lesen","data":{"success":true,"data":{"states":["active","draft","archived"]},"message":null,"timestamp":"2026-11-09 12:57:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/glossary/get_glossary_entries":{"post":{"tags":["Glossar"],"operationId":"glossary__get_glossary_entries","summary":"Glossar-Einträge auflisten","description":"Liefert die Glossar-Einträge deiner Firma, **neueste zuerst** (nach Erstellungsdatum absteigend), seitenweise. Das Ergebnis steht in `data.items`; dazu kommen `limit`, `offset` und `total` (Gesamtzahl der Treffer). Jeder Eintrag enthält `id`, `term_de`, `term_en`, `definition_de`, `definition_en`, `company_id`, `category`, `status`, `priority`, `confidence`, `created_at` und `updated_at`; Beispiele und Synonyme sind in der Liste nicht enthalten, die liefert `get_glossary_entry`. Die Filter lassen sich kombinieren: `category` und `status` vergleichen exakt, `search` sucht als Teilzeichenfolge in den vier Textfeldern Begriff und Definition (de/en). Mit `include_global` kommen zusätzlich die globalen Begriffe ohne Firmenzuordnung (`company_id` ist dort `null`) dazu. Ohne `limit` werden 25 Einträge geliefert.\n\n**Gut zu wissen**\n\n- `search` durchsucht nur Begriffe und Definitionen, nicht Beispiele oder Synonyme.\n- Der Suchtext wird HTML-maskiert; Sonderzeichen wie `&` können deshalb nicht wie erwartet treffen.\n- `limit` hat die Untergrenze 1, aber keine feste Obergrenze.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/glossary/get_glossary_entries \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"search\": \"Lead\",\n    \"status\": \"active\",\n    \"include_global\": true,\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/glossary/get_glossary_entries\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"search\": \"Lead\",\n        \"status\": \"active\",\n        \"include_global\": True,\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/glossary/get_glossary_entries\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"search\": \"Lead\",\n      \"status\": \"active\",\n      \"include_global\": true,\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"search":{"type":"string","description":"Optionaler Suchtext. Es werden Einträge geliefert, in deren deutschem oder englischem Begriff oder in deren Definition der Text als Teilzeichenfolge vorkommt. Der Suchtext wird getrimmt und HTML-maskiert.","example":"Lead"},"category":{"type":"string","description":"Optionaler Filter auf die Kategorie als exakter Text. Im Datenmodell vorgesehen sind `Business`, `Technology`, `Legal` und `Marketing`; es werden nur Einträge mit genau diesem Wert geliefert."},"status":{"type":"string","description":"Optionaler Filter auf den Status als exakter Text. Im Datenmodell vorgesehen sind `active`, `draft` und `archived`; es werden nur Einträge mit genau diesem Wert geliefert.","example":"active"},"include_global":{"type":"boolean","description":"Optional: `true` nimmt zusätzlich globale Begriffe ohne Firmenzuordnung in die Treffer auf. Standard ist `false`, dann kommen nur Einträge deiner Firma. Die Filter gelten auch für die globalen Begriffe.","example":true},"limit":{"type":"integer","description":"Maximale Anzahl Einträge pro Antwort als ganze Zahl. Standard ist 25, der kleinste Wert ist 1. Es gibt keine feste Obergrenze.","example":10},"offset":{"type":"integer","description":"Anzahl der Einträge, die am Anfang der sortierten Liste übersprungen werden, zum seitenweisen Blättern. Standard ist 0, negative Werte werden zu 0."}}},"example":{"search":"Lead","status":"active","include_global":true,"limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"glossary","werkzeug":"get_glossary_entries","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":71045,"term_de":"Lead-Qualifizierung","term_en":"Lead qualification","definition_de":"Prüfung, ob eine Anfrage zu Angebot und Budget passt.","definition_en":"Checking whether an inquiry fits the offer and budget.","company_id":4711,"category":"Marketing","status":"active","priority":"medium","confidence":null,"created_at":"2026-10-14T09:12:30+02:00","updated_at":"2026-10-14T09:12:30+02:00"}],"limit":10,"offset":0,"total":1},"message":null,"timestamp":"2026-11-09 13:00:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/glossary/get_glossary_entry":{"post":{"tags":["Glossar"],"operationId":"glossary__get_glossary_entry","summary":"Glossar-Eintrag abrufen","description":"Liefert einen einzelnen Glossar-Eintrag anhand seiner `id` in der ausführlichen Form. Zusätzlich zu den Feldern der Liste (`term_de`, `term_en`, `definition_de`, `definition_en`, `company_id`, `category`, `status`, `priority`, `confidence`, `created_at`, `updated_at`) enthält die Antwort `examples_de`, `examples_en`, `synonyms_de` und `synonyms_en` jeweils als Text. Der Eintrag muss zu deiner Firma gehören; globale Begriffe ohne Firmenzuordnung sind ebenfalls abrufbar. Für Einträge anderer Firmen kommt in `data` ein Objekt mit `success: false` und der Meldung „Kein Zugriff auf diesen Glossar-Eintrag“, für unbekannte IDs „Glossar-Eintrag nicht gefunden“. Die ID bekommst du aus `get_glossary_entries` oder `create_glossary_entry`.\n\n**Gut zu wissen**\n\n- Fehler stehen in `data.error`, nicht im HTTP-Status.\n- Synonyme und Beispiele sind je Sprache ein einzelner Text, keine Liste.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/glossary/get_glossary_entry \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"entry_id\": 71045\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/glossary/get_glossary_entry\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"entry_id\": 71045,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/glossary/get_glossary_entry\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"entry_id\": 71045\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"entry_id":{"type":"integer","description":"Die numerische ID des Glossar-Eintrags, wie sie in `id` von `get_glossary_entries` oder `create_glossary_entry` zurückkommt. Pflichtangabe.","example":71045}},"required":["entry_id"]},"example":{"entry_id":71045}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"glossary","werkzeug":"get_glossary_entry","stufe":"lesen","data":{"success":true,"data":{"id":71045,"term_de":"Lead-Qualifizierung","term_en":"Lead qualification","definition_de":"Prüfung, ob eine Anfrage zu Angebot und Budget passt.","definition_en":"Checking whether an inquiry fits the offer and budget.","company_id":4711,"category":"Marketing","status":"active","priority":"medium","confidence":null,"created_at":"2026-10-14T09:12:30+02:00","updated_at":"2026-10-14T09:12:30+02:00","examples_de":"Der Vertrieb qualifiziert neue Anfragen innerhalb eines Tages.","examples_en":"Sales qualifies new inquiries within one day.","synonyms_de":"Anfragenbewertung","synonyms_en":"lead scoring"},"message":null,"timestamp":"2026-11-09 13:02:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/glossary/create_glossary_entry":{"post":{"tags":["Glossar"],"operationId":"glossary__create_glossary_entry","summary":"Glossar-Eintrag anlegen","description":"Legt einen neuen Glossar-Eintrag für deine Firma an und veröffentlicht ihn. Pflicht sind `term_de` und `definition_de`. Alle weiteren Angaben (`term_en`, `definition_en`, `category`, `status`, `priority`, `synonyms_de`, `synonyms_en`) sind optional und bleiben leer, wenn du sie weglässt; es gibt keine automatischen Standardwerte für Status oder Priorität. Der Eintrag wird im Ordner `/glossar` abgelegt, der bei Bedarf angelegt wird; der interne Schlüssel wird aus dem deutschen Begriff gebildet und bei Kollisionen durchnummeriert. Zurück kommt der vollständige neue Eintrag in der ausführlichen Form mit der Meldung „Glossar-Eintrag erstellt“. Prüfe vorher mit `get_glossary_entries`, ob der Begriff schon existiert; doppelte Begriffe werden nicht verhindert.\n\n**Gut zu wissen**\n\n- Die Texte werden unverändert gespeichert (keine HTML-Maskierung); sende nur Inhalte, die du später so ausgeben willst.\n- `category`, `status` und `priority` werden nicht gegen eine Wertliste geprüft; vorgesehen sind `Business`/`Technology`/`Legal`/`Marketing`, `active`/`draft`/`archived` und `high`/`medium`/`low`.\n- Doppelte Begriffe werden nicht erkannt.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/glossary/create_glossary_entry \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"term_de\": \"Lead-Qualifizierung\",\n    \"definition_de\": \"Prüfung, ob eine Anfrage zu Angebot und Budget passt.\",\n    \"term_en\": \"Lead qualification\",\n    \"category\": \"Marketing\",\n    \"status\": \"active\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/glossary/create_glossary_entry\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"term_de\": \"Lead-Qualifizierung\",\n        \"definition_de\": \"Prüfung, ob eine Anfrage zu Angebot und Budget passt.\",\n        \"term_en\": \"Lead qualification\",\n        \"category\": \"Marketing\",\n        \"status\": \"active\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/glossary/create_glossary_entry\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"term_de\": \"Lead-Qualifizierung\",\n      \"definition_de\": \"Prüfung, ob eine Anfrage zu Angebot und Budget passt.\",\n      \"term_en\": \"Lead qualification\",\n      \"category\": \"Marketing\",\n      \"status\": \"active\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"term_de":{"type":"string","description":"Deutscher Begriff, zum Beispiel „Lead-Qualifizierung“. Pflichtangabe, darf nicht leer sein. Aus ihm wird auch der interne Schlüssel des Eintrags gebildet.","example":"Lead-Qualifizierung"},"definition_de":{"type":"string","description":"Deutsche Definition bzw. Erklärung des Begriffs als Text. Pflichtangabe, darf nicht leer sein. In der Kurzfassung von `summarize_glossary_entry` wird sie auf 160 Zeichen gekürzt.","example":"Prüfung, ob eine Anfrage zu Angebot und Budget passt."},"term_en":{"type":"string","description":"Optional: englische Übersetzung des Begriffs. Ohne Angabe bleibt das Feld leer; die Suche in `get_glossary_entries` berücksichtigt es, sobald es gefüllt ist.","example":"Lead qualification"},"definition_en":{"type":"string","description":"Optional: englische Definition als Text. Ohne Angabe bleibt das Feld leer. Fehlt die deutsche Definition, nutzt die Kurzfassung ersatzweise diese."},"category":{"type":"string","description":"Optional: Kategorie des Begriffs als Text. Im Datenmodell vorgesehen sind `Business`, `Technology`, `Legal` und `Marketing`; die Schreibweise muss für den Kategorie-Filter exakt passen.","example":"Marketing"},"status":{"type":"string","description":"Optional: Status des Eintrags als Text. Im Datenmodell vorgesehen sind `active`, `draft` und `archived`. Ohne Angabe bleibt der Status leer.","example":"active"},"priority":{"type":"string","description":"Optional: Priorität als Text. Im Datenmodell vorgesehen sind `high`, `medium` und `low`. Ohne Angabe bleibt die Priorität leer."},"synonyms_de":{"type":"string","description":"Optional: deutsche Synonyme als ein einzelner Text, zum Beispiel kommagetrennt („Anfragenbewertung, Lead-Check“). Es wird kein Listenformat erzwungen."},"synonyms_en":{"type":"string","description":"Optional: englische Synonyme als ein einzelner Text, zum Beispiel kommagetrennt („lead scoring, lead vetting“). Es wird kein Listenformat erzwungen."}},"required":["term_de","definition_de"]},"example":{"term_de":"Lead-Qualifizierung","definition_de":"Prüfung, ob eine Anfrage zu Angebot und Budget passt.","term_en":"Lead qualification","category":"Marketing","status":"active"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"glossary","werkzeug":"create_glossary_entry","stufe":"schreiben","data":{"success":true,"data":{"id":71045,"term_de":"Lead-Qualifizierung","term_en":"Lead qualification","definition_de":"Prüfung, ob eine Anfrage zu Angebot und Budget passt.","definition_en":null,"company_id":4711,"category":"Marketing","status":"active","priority":null,"confidence":null,"created_at":"2026-11-09T13:05:00+01:00","updated_at":"2026-11-09T13:05:00+01:00","examples_de":null,"examples_en":null,"synonyms_de":null,"synonyms_en":null},"message":"Glossar-Eintrag erstellt","timestamp":"2026-11-09 13:05:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/glossary/update_glossary_entry":{"post":{"tags":["Glossar"],"operationId":"glossary__update_glossary_entry","summary":"Glossar-Eintrag ändern","description":"Ändert Felder eines bestehenden Glossar-Eintrags. Im Objekt `data` gibst du nur die Felder an, die sich ändern sollen; ein leeres `data` wird mit „Keine Daten zum Aktualisieren übergeben“ abgelehnt. Wirksam sind die Textfelder `term_de`, `term_en`, `definition_de`, `definition_en`, `examples_de`, `examples_en`, `synonyms_de`, `synonyms_en`, `category`, `priority` und `status`. Ist ein Schlüssel in `data` vorhanden, wird das Feld überschrieben, auch mit `null` oder einem leeren Text; so kannst du Felder gezielt leeren. Nicht genannte Felder bleiben unverändert. Zurück kommt der aktualisierte Eintrag in der ausführlichen Form mit der Meldung „Glossar-Eintrag aktualisiert“.\n\n**Gut zu wissen**\n\n- Anders als bei vielen anderen Bereichen leert ein `null` im Feld das Feld tatsächlich.\n- Übergib Textfelder immer als Text; andere Typen führen zu einem Fehler.\n- Die Texte werden unverändert gespeichert (keine HTML-Maskierung).\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/glossary/update_glossary_entry \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"entry_id\": 71045,\n    \"data\": {\n        \"definition_en\": \"Checking whether an inquiry fits the offer and budget.\",\n        \"synonyms_de\": \"Anfragenbewertung\",\n        \"priority\": \"medium\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/glossary/update_glossary_entry\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"entry_id\": 71045,\n        \"data\": {\n            \"definition_en\": \"Checking whether an inquiry fits the offer and budget.\",\n            \"synonyms_de\": \"Anfragenbewertung\",\n            \"priority\": \"medium\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/glossary/update_glossary_entry\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"entry_id\": 71045,\n      \"data\": {\n          \"definition_en\": \"Checking whether an inquiry fits the offer and budget.\",\n          \"synonyms_de\": \"Anfragenbewertung\",\n          \"priority\": \"medium\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"entry_id":{"type":"integer","description":"Die ID des zu ändernden Glossar-Eintrags, wie sie `get_glossary_entries` oder `create_glossary_entry` liefert. Pflichtangabe; Einträge anderer Firmen werden abgelehnt.","example":71045},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern. Wirksame Schlüssel (alle Text): `term_de`, `term_en`, `definition_de`, `definition_en`, `examples_de`, `examples_en`, `synonyms_de`, `synonyms_en`, `category`, `priority`, `status`. Pflichtangabe und nicht leer; unbekannte Schlüssel werden ignoriert.","example":{"definition_en":"Checking whether an inquiry fits the offer and budget.","synonyms_de":"Anfragenbewertung","priority":"medium"}}},"required":["entry_id","data"]},"example":{"entry_id":71045,"data":{"definition_en":"Checking whether an inquiry fits the offer and budget.","synonyms_de":"Anfragenbewertung","priority":"medium"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"glossary","werkzeug":"update_glossary_entry","stufe":"schreiben","data":{"success":true,"data":{"id":71045,"term_de":"Lead-Qualifizierung","term_en":"Lead qualification","definition_de":"Prüfung, ob eine Anfrage zu Angebot und Budget passt.","definition_en":"Checking whether an inquiry fits the offer and budget.","company_id":4711,"category":"Marketing","status":"active","priority":"medium","confidence":null,"created_at":"2026-11-09T13:05:00+01:00","updated_at":"2026-11-09T13:12:40+01:00","examples_de":null,"examples_en":null,"synonyms_de":"Anfragenbewertung","synonyms_en":null},"message":"Glossar-Eintrag aktualisiert","timestamp":"2026-11-09 13:12:40","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/glossary/delete_glossary_entry":{"post":{"tags":["Glossar"],"operationId":"glossary__delete_glossary_entry","summary":"Glossar-Eintrag löschen","description":"Löscht einen Glossar-Eintrag **endgültig**. Über diese API gibt es keinen Papierkorb und keine Wiederherstellung. Die Antwort enthält keine Daten: `data` ist `null`, die Meldung lautet „Glossar-Eintrag gelöscht“. Weil die Aktion unumkehrbar ist, verlangt die API im Körper zusätzlich `\"bestaetigt\": true`, sonst antwortet sie mit HTTP 409. Wenn du einen Begriff nur nicht mehr verwenden möchtest, setze ihn stattdessen mit `update_glossary_entry` auf den Status `archived`; so bleibt er für Nachschlagen und Wiederverwendung erhalten.\n\n**Gut zu wissen**\n\n- Ohne `\"bestaetigt\": true` wird die Anfrage mit 409 abgelehnt.\n- Die Antwort enthält die gelöschte ID nicht; merke sie dir vorher.\n- Zum Ausblenden ohne Verlust nimm `update_glossary_entry` mit `status` = `archived`.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/glossary/delete_glossary_entry \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"entry_id\": 71045,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/glossary/delete_glossary_entry\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"entry_id\": 71045,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/glossary/delete_glossary_entry\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"entry_id\": 71045,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"entry_id":{"type":"integer","description":"Die ID des zu löschenden Glossar-Eintrags. Pflichtangabe. Einträge anderer Firmen werden abgelehnt, unbekannte IDs liefern „Glossar-Eintrag nicht gefunden“.","example":71045},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["entry_id","bestaetigt"]},"example":{"entry_id":71045,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"glossary","werkzeug":"delete_glossary_entry","stufe":"loeschen","data":{"success":true,"data":null,"message":"Glossar-Eintrag gelöscht","timestamp":"2026-11-09 13:20:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/glossary/summarize_glossary_entry":{"post":{"tags":["Glossar"],"operationId":"glossary__summarize_glossary_entry","summary":"Glossar-Eintrag zusammenfassen","description":"Liefert zu einem Glossar-Eintrag den vollständigen Eintrag (`data.entry`, ausführliche Form mit Beispielen und Synonymen) und eine Kurzfassung in `data.summary`. Die Kurzfassung wird **ohne KI-Aufruf** gebildet und enthält `term_de`, `term_en`, `category`, `status`, `priority`, `definition_short` und `synonyms`. `definition_short` ist die deutsche Definition (ersatzweise die englische), von HTML-Tags befreit und auf 160 Zeichen gekürzt; bei Kürzung endet sie mit „…“. `synonyms` enthält die nicht leeren Synonymtexte (deutsch, englisch). Die Funktion verändert nichts, ist im Katalog aber als schreibend eingestuft; dein Schlüssel braucht dafür das Recht „schreiben“.\n\n**Gut zu wissen**\n\n- `synonyms` lässt leere Werte weg, behält aber deren Position als Schlüssel: Ist nur das englische Synonym gesetzt, kommt ein Objekt wie `{\"1\": \"…\"}` statt einer Liste.\n- Der Schlüssel braucht das Recht „schreiben“, obwohl nichts verändert wird.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/glossary/summarize_glossary_entry \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"entry_id\": 71045\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/glossary/summarize_glossary_entry\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"entry_id\": 71045,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/glossary/summarize_glossary_entry\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"entry_id\": 71045\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"entry_id":{"type":"integer","description":"Die ID des Glossar-Eintrags, der zusammengefasst werden soll. Pflichtangabe; der Eintrag muss zu deiner Firma gehören, sonst kommt eine Fehlermeldung.","example":71045}},"required":["entry_id"]},"example":{"entry_id":71045}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"glossary","werkzeug":"summarize_glossary_entry","stufe":"lesen","data":{"success":true,"data":{"entry":{"id":71045,"term_de":"Lead-Qualifizierung","term_en":"Lead qualification","definition_de":"Prüfung, ob eine Anfrage zu Angebot und Budget passt.","definition_en":"Checking whether an inquiry fits the offer and budget.","company_id":4711,"category":"Marketing","status":"active","priority":"medium","confidence":null,"created_at":"2026-11-09T13:05:00+01:00","updated_at":"2026-11-09T13:12:40+01:00","examples_de":null,"examples_en":null,"synonyms_de":"Anfragenbewertung","synonyms_en":null},"summary":{"term_de":"Lead-Qualifizierung","term_en":"Lead qualification","category":"Marketing","status":"active","priority":"medium","definition_short":"Prüfung, ob eine Anfrage zu Angebot und Budget passt.","synonyms":["Anfragenbewertung"]}},"message":null,"timestamp":"2026-11-09 13:25:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/handbook/get_handbook_pages":{"post":{"tags":["Handbuch"],"operationId":"handbook__get_handbook_pages","summary":"Handbuch-Seiten auflisten","description":"Liefert eine Seite der Handbuch-Einträge deines Unternehmens, **neueste zuerst** (Sortierung nach Erstellungsdatum absteigend). Die Antwort enthält `items`, `total` (Gesamtzahl der Treffer ohne Seiteneinteilung), `limit` und `offset`. Die Einträge enthalten die Metadaten und die strukturierten Teile (`tags`, `steps`, `checklist`, `kpis`, `related_pages`, `changelog`), aber **nicht** `content`, `technical_info` und `common_issues` – dafür brauchst du `get_handbook_page`. Standardmäßig sind auch systemweite Seiten (`is_internal`) enthalten; mit `include_internal: false` schließt du sie aus. Seiten ohne Unternehmenszuordnung kommen nur mit `include_global: true` dazu.\n\n**Gut zu wissen**\n\n- Ohne `limit` werden 25 Einträge geliefert; blättere mit `offset`.\n- Der Parameter `search` wird von dieser Funktion nicht ausgewertet – für Stichwortsuche `search_handbook_pages` verwenden.\n- `include_internal` ist standardmäßig `true`: systemweite Seiten anderer Herkunft erscheinen in der Liste.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/handbook/get_handbook_pages \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"draft\",\n    \"include_internal\": false,\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/handbook/get_handbook_pages\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"draft\",\n        \"include_internal\": False,\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/handbook/get_handbook_pages\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"draft\",\n      \"include_internal\": false,\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Filtert auf genau diesen Status (Textvergleich, z. B. `draft`, `review`, `active`, `archived`). Ohne Angabe werden alle Status geliefert.","example":"draft"},"category":{"type":"string","description":"Filtert auf genau diese Kategorie, z. B. `02-sops`. Es wird nur exakt verglichen, keine Teilstrings."},"page_type":{"type":"string","description":"Filtert auf den Seitentyp, z. B. `sop`, `workflow`, `training`, `documentation` oder `overview`. Exakter Vergleich."},"search":{"type":"string","description":"Im Schema als Freitextsuche beschrieben, wird von dieser Funktion aber nicht ausgewertet und hat keine Wirkung. Nutze `search_handbook_pages` für die Suche in Titel, Slug, Inhalt und Tags."},"include_global":{"type":"boolean","description":"Wenn `true`, werden zusätzlich Seiten ohne Unternehmenszuordnung (globale Seiten) eingeschlossen. Standard `false`."},"include_internal":{"type":"boolean","description":"Steuert, ob systemweite Seiten (`is_internal: true`) mitgeliefert werden. Standard `true`; mit `false` erhältst du nur Seiten deines Unternehmens.","example":false},"limit":{"type":"integer","description":"Maximale Anzahl Einträge pro Antwort. Standard 25, Minimum 1; eine feste Obergrenze gibt es im Code nicht.","example":10},"offset":{"type":"integer","description":"Anzahl der zu überspringenden Einträge für die Seiteneinteilung. Standard 0, negative Werte werden zu 0."}}},"example":{"status":"draft","include_internal":false,"limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"handbook","werkzeug":"get_handbook_pages","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":1042,"title":"Kundenanfragen im Tagesgeschäft bearbeiten","slug":"kundenanfragen-bearbeiten","category":"02-sops","subcategory":"Kundenservice","page_type":"sop","status":"draft","version":"1.0","company_id":12345,"is_internal":false,"goal":"Anfragen innerhalb eines Werktags beantworten","responsible":"Lena Beispiel","author":"Max Muster","access_level":"employee","source_interview":null,"job_id":null,"chroma_indexed":false,"chroma_collection":null,"created_at":"2026-11-03T09:12:08+01:00","updated_at":"2026-11-08T16:40:21+01:00","last_reviewed":null,"tags":["service","anfragen"],"steps":[{"title":"Anfrage sichten","description":"Posteingang prüfen und Dringlichkeit festlegen"},{"title":"Antwort verfassen","description":"Vorlage wählen und anpassen"}],"checklist":[{"item":"Ticket angelegt"},{"item":"Antwort versendet"}],"kpis":[{"name":"Antwortzeit","target":"< 24 h"}],"related_pages":[],"changelog":[]}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/handbook/get_handbook_page":{"post":{"tags":["Handbuch"],"operationId":"handbook__get_handbook_page","summary":"Eine Handbuch-Seite abrufen","description":"Lädt eine einzelne Handbuch-Seite anhand ihrer ID und liefert sie **vollständig** in `data`: alle Metadaten (Titel, Slug, Kategorie, Seitentyp, Status, Version, Verantwortliche, Autor, Zugriffsstufe, Index-Status) sowie die strukturierten Felder `tags`, `steps`, `checklist`, `kpis`, `related_pages`, `changelog` und zusätzlich `content`, `technical_info` und `common_issues`. Zeitstempel kommen im ISO-8601-Format. Die Seite muss deinem Unternehmen gehören, keiner Firma zugeordnet sein oder als systemweite Seite (`is_internal`) markiert sein; sonst antwortet die Funktion mit `success: false` und dem Hinweis „nicht gefunden oder kein Zugriff“. Nutze sie, bevor du mit `update_handbook_page` etwas änderst.\n\n**Gut zu wissen**\n\n- Die ID bekommst du aus `get_handbook_pages`, `search_handbook_pages` oder `semantic_search_handbook`.\n- `steps`, `checklist`, `kpis` u. a. werden als dekodierte Listen geliefert; ist ein Feld leer oder kein gültiges JSON, kommt eine leere Liste.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/handbook/get_handbook_page \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"page_id\": 1042\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/handbook/get_handbook_page\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"page_id\": 1042,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/handbook/get_handbook_page\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"page_id\": 1042\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"page_id":{"type":"integer","description":"Numerische ID der Handbuch-Seite (Pflicht). Wird als Ganzzahl interpretiert; IDs fremder Unternehmen führen zu einem Zugriffsfehler.","example":1042}},"required":["page_id"]},"example":{"page_id":1042}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"handbook","werkzeug":"get_handbook_page","stufe":"lesen","data":{"success":true,"data":{"id":1042,"title":"Kundenanfragen im Tagesgeschäft bearbeiten","slug":"kundenanfragen-bearbeiten","category":"02-sops","subcategory":"Kundenservice","page_type":"sop","status":"draft","version":"1.0","company_id":12345,"is_internal":false,"goal":"Anfragen innerhalb eines Werktags beantworten","responsible":"Lena Beispiel","author":"Max Muster","access_level":"employee","source_interview":null,"job_id":null,"chroma_indexed":false,"chroma_collection":null,"created_at":"2026-11-03T09:12:08+01:00","updated_at":"2026-11-08T16:40:21+01:00","last_reviewed":null,"tags":["service","anfragen"],"steps":[{"title":"Anfrage sichten","description":"Posteingang prüfen und Dringlichkeit festlegen"},{"title":"Antwort verfassen","description":"Vorlage wählen und anpassen"}],"checklist":[{"item":"Ticket angelegt"},{"item":"Antwort versendet"}],"kpis":[{"name":"Antwortzeit","target":"< 24 h"}],"related_pages":[],"changelog":[],"content":"## Ablauf\n\nJede Kundenanfrage wird am selben Werktag gesichtet und spätestens am Folgetag beantwortet.","technical_info":null,"common_issues":null},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/handbook/search_handbook_pages":{"post":{"tags":["Handbuch"],"operationId":"handbook__search_handbook_pages","summary":"Handbuch per Stichwort durchsuchen","description":"Stichwortsuche über Titel, Slug, Inhalt und Tags. Gesucht wird als Teilstring (SQL-`LIKE %…%`), die Treffer sind nach **letzter Änderung absteigend** sortiert. Die Antwort hat dieselbe Form wie `get_handbook_pages`: `items` (ohne `content`), `total`, `limit`, `offset`. Zusätzlich lässt sich nach `category` und `status` eingrenzen; systemweite Seiten sind standardmäßig dabei. Globale Seiten ohne Unternehmenszuordnung werden hier nicht eingeschlossen. Für inhaltliche Suche ohne exakte Stichworte ist `semantic_search_handbook` besser geeignet, setzt aber einen aufgebauten Suchindex voraus.\n\n**Gut zu wissen**\n\n- Der Suchbegriff wird vor der Suche HTML-maskiert (`&`, `<`, `>`, `\"`, `'`); Begriffe mit diesen Zeichen finden daher keine Rohtext-Treffer.\n- Standardmäßig werden nur 10 Treffer geliefert.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/handbook/search_handbook_pages \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\": \"Reklamation\",\n    \"limit\": 5,\n    \"category\": \"02-sops\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/handbook/search_handbook_pages\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"query\": \"Reklamation\",\n        \"limit\": 5,\n        \"category\": \"02-sops\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/handbook/search_handbook_pages\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"query\": \"Reklamation\",\n      \"limit\": 5,\n      \"category\": \"02-sops\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Suchbegriff (Pflicht). Wird in Titel, Slug, Inhalt und Tags als Teilstring gesucht, ohne Berücksichtigung von Wortgrenzen.","example":"Reklamation"},"limit":{"type":"integer","description":"Maximale Trefferzahl pro Antwort. Standard 10, Minimum 1.","example":5},"offset":{"type":"integer","description":"Anzahl zu überspringender Treffer für die Seiteneinteilung. Standard 0."},"category":{"type":"string","description":"Optionaler Filter auf eine exakte Kategorie, z. B. `02-sops`.","example":"02-sops"},"status":{"type":"string","description":"Optionaler Filter auf einen exakten Status, z. B. `active`."},"include_internal":{"type":"boolean","description":"Wenn `false`, werden systemweite Seiten (`is_internal`) ausgeschlossen. Standard `true`."}},"required":["query"]},"example":{"query":"Reklamation","limit":5,"category":"02-sops"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"handbook","werkzeug":"search_handbook_pages","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":1051,"title":"Reklamationen bearbeiten","slug":"reklamationen-bearbeiten","category":"02-sops","subcategory":"Kundenservice","page_type":"sop","status":"draft","version":"1.0","company_id":12345,"is_internal":false,"goal":"Anfragen innerhalb eines Werktags beantworten","responsible":"Lena Beispiel","author":"Max Muster","access_level":"employee","source_interview":null,"job_id":null,"chroma_indexed":false,"chroma_collection":null,"created_at":"2026-11-03T09:12:08+01:00","updated_at":"2026-11-07T11:02:55+01:00","last_reviewed":null,"tags":["service","anfragen"],"steps":[{"title":"Anfrage sichten","description":"Posteingang prüfen und Dringlichkeit festlegen"},{"title":"Antwort verfassen","description":"Vorlage wählen und anpassen"}],"checklist":[{"item":"Ticket angelegt"},{"item":"Antwort versendet"}],"kpis":[{"name":"Antwortzeit","target":"< 24 h"}],"related_pages":[],"changelog":[]}],"total":1,"limit":5,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/handbook/semantic_search_handbook":{"post":{"tags":["Handbuch"],"operationId":"handbook__semantic_search_handbook","summary":"Handbuch semantisch durchsuchen","description":"Natürlichsprachige Suche über den Suchindex des Handbuchs. Die Funktion holt zunächst mehr Kandidaten als gewünscht (mindestens das Dreifache von `limit`, mindestens 10), filtert sie auf zugängliche Seiten und optional auf `category` und `page_type` und liefert dann höchstens `limit` Treffer. Jeder Treffer enthält `score` (der Abstandswert des Index – **kleiner bedeutet ähnlicher**), `excerpt` (ein Textauszug passend zur Anfrage) und `page` (Metadaten wie bei `get_handbook_pages`, ohne `content`). Nur Seiten, die im Index stehen, können gefunden werden; neue Seiten indexierst du über `auto_index` beim Anlegen, `reindex` beim Ändern oder `reindex_handbook_page`. Ist die semantische Suche nicht eingerichtet, kommt ein Fehler.\n\n**Gut zu wissen**\n\n- Die Antwort enthält `count` statt `total`.\n- Systemweite Seiten sind standardmäßig enthalten (`include_internal` = `true`).\n- Eine nicht indexierte Seite taucht nie auf – prüfe das Feld `chroma_indexed`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/handbook/semantic_search_handbook \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\": \"Wie gehe ich mit einer Kundenbeschwerde um?\",\n    \"limit\": 3,\n    \"page_type\": \"sop\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/handbook/semantic_search_handbook\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"query\": \"Wie gehe ich mit einer Kundenbeschwerde um?\",\n        \"limit\": 3,\n        \"page_type\": \"sop\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/handbook/semantic_search_handbook\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"query\": \"Wie gehe ich mit einer Kundenbeschwerde um?\",\n      \"limit\": 3,\n      \"page_type\": \"sop\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Natürlichsprachige Suchanfrage (Pflicht), z. B. eine Frage oder ein Thema. Muss kein Stichwort enthalten, das wörtlich im Text steht.","example":"Wie gehe ich mit einer Kundenbeschwerde um?"},"limit":{"type":"integer","description":"Anzahl der gewünschten Treffer. Standard 5, Minimum 1.","example":3},"category":{"type":"string","description":"Optionaler Filter: nur Seiten dieser exakten Kategorie bleiben im Ergebnis."},"page_type":{"type":"string","description":"Optionaler Filter auf den Seitentyp, z. B. `sop` oder `training`.","example":"sop"},"include_internal":{"type":"boolean","description":"Wenn `false`, werden systemweite Seiten ausgeschlossen, die nicht deinem Unternehmen gehören. Standard `true`."}},"required":["query"]},"example":{"query":"Wie gehe ich mit einer Kundenbeschwerde um?","limit":3,"page_type":"sop"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"handbook","werkzeug":"semantic_search_handbook","stufe":"lesen","data":{"success":true,"data":{"items":[{"score":0.312,"excerpt":"… Beschwerden werden am selben Werktag bestätigt und innerhalb von 48 Stunden gelöst …","page":{"id":1051,"title":"Reklamationen bearbeiten","slug":"reklamationen-bearbeiten","category":"02-sops","subcategory":"Kundenservice","page_type":"sop","status":"draft","version":"1.0","company_id":12345,"is_internal":false,"goal":"Anfragen innerhalb eines Werktags beantworten","responsible":"Lena Beispiel","author":"Max Muster","access_level":"employee","source_interview":null,"job_id":null,"chroma_indexed":true,"chroma_collection":"handbook_pages","created_at":"2026-11-03T09:12:08+01:00","updated_at":"2026-11-08T16:40:21+01:00","last_reviewed":null,"tags":["service","anfragen"],"steps":[{"title":"Anfrage sichten","description":"Posteingang prüfen und Dringlichkeit festlegen"},{"title":"Antwort verfassen","description":"Vorlage wählen und anpassen"}],"checklist":[{"item":"Ticket angelegt"},{"item":"Antwort versendet"}],"kpis":[{"name":"Antwortzeit","target":"< 24 h"}],"related_pages":[],"changelog":[]}}],"count":1},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/handbook/get_internal_handbook_pages":{"post":{"tags":["Handbuch"],"operationId":"handbook__get_internal_handbook_pages","summary":"Systemweite Handbuch-Seiten auflisten","description":"Listet alle Handbuch-Seiten, die als systemweit markiert sind (`is_internal: true`), neueste zuerst. Diese SOPs gehören nicht einem einzelnen Unternehmen, sondern stehen allen zur Verfügung. Optional filterst du nach Kategorie, Status und Seitentyp. Die Antwort liefert `items` (ohne `content`), `total`, `limit` und `offset` wie `get_handbook_pages`. Den vollständigen Inhalt einer dieser Seiten holst du anschließend mit `get_handbook_page`.\n\n**Gut zu wissen**\n\n- Standardmenge: 25 Einträge.\n- Es wird nicht nach Unternehmen gefiltert – die Liste ist für alle Aufrufer dieselbe.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/handbook/get_internal_handbook_pages \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"category\": \"02-sops\",\n    \"limit\": 20\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/handbook/get_internal_handbook_pages\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"category\": \"02-sops\",\n        \"limit\": 20,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/handbook/get_internal_handbook_pages\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"category\": \"02-sops\",\n      \"limit\": 20\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"string","description":"Optionaler Filter auf eine exakte Kategorie, z. B. `02-sops`.","example":"02-sops"},"status":{"type":"string","description":"Optionaler Filter auf einen exakten Status, z. B. `active`."},"page_type":{"type":"string","description":"Optionaler Filter auf den Seitentyp, z. B. `sop` oder `workflow`."},"limit":{"type":"integer","description":"Maximale Anzahl Einträge pro Antwort. Standard 25, Minimum 1.","example":20},"offset":{"type":"integer","description":"Anzahl der zu überspringenden Einträge. Standard 0."}}},"example":{"category":"02-sops","limit":20}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"handbook","werkzeug":"get_internal_handbook_pages","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":77,"title":"Datenschutz im Alltag","slug":"datenschutz-im-alltag","category":"02-sops","subcategory":"Kundenservice","page_type":"sop","status":"active","version":"1.0","company_id":null,"is_internal":true,"goal":"Anfragen innerhalb eines Werktags beantworten","responsible":"Lena Beispiel","author":"Max Muster","access_level":"employee","source_interview":null,"job_id":null,"chroma_indexed":false,"chroma_collection":null,"created_at":"2026-11-03T09:12:08+01:00","updated_at":"2026-11-08T16:40:21+01:00","last_reviewed":null,"tags":["service","anfragen"],"steps":[{"title":"Anfrage sichten","description":"Posteingang prüfen und Dringlichkeit festlegen"},{"title":"Antwort verfassen","description":"Vorlage wählen und anpassen"}],"checklist":[{"item":"Ticket angelegt"},{"item":"Antwort versendet"}],"kpis":[{"name":"Antwortzeit","target":"< 24 h"}],"related_pages":[],"changelog":[]}],"total":1,"limit":20,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/handbook/create_handbook_page":{"post":{"tags":["Handbuch"],"operationId":"handbook__create_handbook_page","summary":"Handbuch-Seite anlegen","description":"Legt eine neue Handbuch-Seite für dein Unternehmen an und gibt sie vollständig zurück (inklusive `content`). Nur `title` ist Pflicht. Fehlt `slug`, wird er aus dem Titel erzeugt (Kleinbuchstaben, Sonderzeichen werden zu Bindestrichen). Nicht angegebene Felder erhalten Standardwerte: Status `draft`, Seitentyp `sop`, Kategorie `02-sops`. Strukturierte Angaben (`steps`, `checklist`, `kpis`, `tags`) werden als JSON gespeichert und beim Lesen wieder als Listen geliefert. Mit `auto_index: true` wird die Seite sofort in den Suchindex aufgenommen, sonst später über `reindex_handbook_page`. Die Antwort enthält `message: \"Handbook-Seite erstellt\"`.\n\n**Gut zu wissen**\n\n- Kurztextfelder wie `title`, `goal` und `responsible` werden HTML-maskiert gespeichert (`&` wird zu `&amp;`); `content` bleibt unverändert.\n- Seiten mit `is_internal: true` gelten als systemweit und erscheinen in den Listen und Suchen anderer Unternehmen.\n- Der Objektschlüssel wird aus dem Slug abgeleitet und bei Kollision automatisch durchnummeriert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/handbook/create_handbook_page \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Neukunden-Onboarding\",\n    \"category\": \"02-sops\",\n    \"page_type\": \"sop\",\n    \"status\": \"draft\",\n    \"content\": \"## Ziel\\n\\nNeue Kunden erhalten innerhalb von zwei Werktagen ihren Zugang.\",\n    \"tags\": [\n        \"onboarding\",\n        \"kunden\"\n    ],\n    \"steps\": [\n        {\n            \"title\": \"Vertrag prüfen\",\n            \"description\": \"Unterschriebenen Vertrag ablegen\"\n        },\n        {\n            \"title\": \"Zugang einrichten\",\n            \"description\": \"Konto anlegen und Willkommensmail senden\"\n        }\n    ],\n    \"auto_index\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/handbook/create_handbook_page\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"title\": \"Neukunden-Onboarding\",\n        \"category\": \"02-sops\",\n        \"page_type\": \"sop\",\n        \"status\": \"draft\",\n        \"content\": \"## Ziel\\n\\nNeue Kunden erhalten innerhalb von zwei Werktagen ihren Zugang.\",\n        \"tags\": [\n            \"onboarding\",\n            \"kunden\",\n        ],\n        \"steps\": [\n            {\n                \"title\": \"Vertrag prüfen\",\n                \"description\": \"Unterschriebenen Vertrag ablegen\",\n            },\n            {\n                \"title\": \"Zugang einrichten\",\n                \"description\": \"Konto anlegen und Willkommensmail senden\",\n            },\n        ],\n        \"auto_index\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/handbook/create_handbook_page\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"title\": \"Neukunden-Onboarding\",\n      \"category\": \"02-sops\",\n      \"page_type\": \"sop\",\n      \"status\": \"draft\",\n      \"content\": \"## Ziel\\n\\nNeue Kunden erhalten innerhalb von zwei Werktagen ihren Zugang.\",\n      \"tags\": [\n          \"onboarding\",\n          \"kunden\"\n      ],\n      \"steps\": [\n          {\n              \"title\": \"Vertrag prüfen\",\n              \"description\": \"Unterschriebenen Vertrag ablegen\"\n          },\n          {\n              \"title\": \"Zugang einrichten\",\n              \"description\": \"Konto anlegen und Willkommensmail senden\"\n          }\n      ],\n      \"auto_index\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Titel der Seite (Pflicht, nicht leer).","example":"Neukunden-Onboarding"},"slug":{"type":"string","description":"URL-tauglicher Bezeichner. Optional; wird sonst aus dem Titel erzeugt (nur Buchstaben und Ziffern, getrennt durch Bindestriche)."},"category":{"type":"string","description":"Kategorie der Seite als Freitext. Standard `02-sops`.","example":"02-sops"},"subcategory":{"type":"string","description":"Optionale Unterkategorie als Freitext, z. B. ein Abteilungsname."},"page_type":{"type":"string","description":"Seitentyp. Das Datenmodell kennt `sop`, `workflow`, `training`, `documentation` und `overview`; Standard `sop`. Der Code prüft den Wert nicht gegen diese Liste.","example":"sop"},"status":{"type":"string","description":"Status der Seite. Das Datenmodell kennt `draft`, `review`, `active` und `archived`; Standard `draft`. Ein Abgleich mit dieser Liste findet nicht statt.","example":"draft"},"content":{"type":"string","description":"Hauptinhalt der Seite als Text, üblicherweise Markdown. Wird unverändert gespeichert.","example":"## Ziel\n\nNeue Kunden erhalten innerhalb von zwei Werktagen ihren Zugang."},"goal":{"type":"string","description":"Kurze Zielbeschreibung der Seite bzw. des Prozesses."},"steps":{"type":"array","items":{"type":"object"},"description":"Liste von Prozessschritten, jeweils ein Objekt mit frei wählbaren Feldern (z. B. `title`, `description`). Wird als JSON gespeichert.","example":[{"title":"Vertrag prüfen","description":"Unterschriebenen Vertrag ablegen"},{"title":"Zugang einrichten","description":"Konto anlegen und Willkommensmail senden"}]},"checklist":{"type":"array","items":{"type":"object"},"description":"Checkliste als Liste von Objekten (z. B. `item`). Wird als JSON gespeichert."},"kpis":{"type":"array","items":{"type":"object"},"description":"Kennzahlen als Liste von Objekten (z. B. `name`, `target`). Wird als JSON gespeichert."},"tags":{"type":"array","items":{"type":"string"},"description":"Liste von Schlagwörtern als Strings. Die Tags werden bei der Stichwortsuche mit durchsucht.","example":["onboarding","kunden"]},"responsible":{"type":"string","description":"Name der verantwortlichen Person als Freitext."},"author":{"type":"string","description":"Name des Autors als Freitext."},"access_level":{"type":"string","description":"Zugriffsstufe der Seite. Das Datenmodell kennt `admin`, `employee`, `partner`, `ai` und `public`. Die Funktion prüft den Wert nicht."},"is_internal":{"type":"boolean","description":"Markiert die Seite als systemweit sichtbare SOP. Standard `false`; bei `true` erscheint die Seite auch in den Listen anderer Unternehmen."},"auto_index":{"type":"boolean","description":"Wenn `true`, wird die Seite nach dem Speichern direkt in den Suchindex aufgenommen. Nur wirksam, wenn der Index eingerichtet ist.","example":true}},"required":["title"]},"example":{"title":"Neukunden-Onboarding","category":"02-sops","page_type":"sop","status":"draft","content":"## Ziel\n\nNeue Kunden erhalten innerhalb von zwei Werktagen ihren Zugang.","tags":["onboarding","kunden"],"steps":[{"title":"Vertrag prüfen","description":"Unterschriebenen Vertrag ablegen"},{"title":"Zugang einrichten","description":"Konto anlegen und Willkommensmail senden"}],"auto_index":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"handbook","werkzeug":"create_handbook_page","stufe":"schreiben","data":{"success":true,"data":{"id":1060,"title":"Neukunden-Onboarding","slug":"neukunden-onboarding","category":"02-sops","subcategory":null,"page_type":"sop","status":"draft","version":"1.0","company_id":12345,"is_internal":false,"goal":null,"responsible":null,"author":null,"access_level":null,"source_interview":null,"job_id":null,"chroma_indexed":true,"chroma_collection":"handbook_pages","created_at":"2026-11-03T09:12:08+01:00","updated_at":"2026-11-08T16:40:21+01:00","last_reviewed":null,"tags":["onboarding","kunden"],"steps":[{"title":"Vertrag prüfen","description":"Unterschriebenen Vertrag ablegen"},{"title":"Zugang einrichten","description":"Konto anlegen und Willkommensmail senden"}],"checklist":[],"kpis":[],"related_pages":[],"changelog":[],"content":"## Ziel\n\nNeue Kunden erhalten innerhalb von zwei Werktagen ihren Zugang.","technical_info":null,"common_issues":null},"message":"Handbook-Seite erstellt","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/handbook/update_handbook_page":{"post":{"tags":["Handbuch"],"operationId":"handbook__update_handbook_page","summary":"Handbuch-Seite aktualisieren","description":"Ändert einzelne Felder einer bestehenden Seite. In `data` übergibst du nur die Felder, die sich ändern sollen; nicht genannte Felder bleiben unverändert. Erlaubte Schlüssel (snake_case): `title`, `slug`, `category`, `subcategory`, `page_type`, `status`, `version`, `goal`, `content`, `is_internal`, `responsible`, `author`, `access_level`, `workflow_reference`, `technical_info`, `common_issues`, `related_pages`, `steps`, `checklist`, `kpis`, `tags`, `changelog`, `source_interview`, `job_id`, `last_reviewed` (Datum). Schlüssel mit Wert `null` werden ignoriert, ein leerer String leert das Feld. Unbekannte Schlüssel werden still übergangen. Eine leere `data` ergibt einen Fehler. Die Antwort enthält die vollständige aktualisierte Seite mit `message: \"Handbook-Seite aktualisiert\"`.\n\n**Gut zu wissen**\n\n- Listenfelder (`steps`, `checklist`, `kpis`, `tags`) werden **komplett ersetzt**, nicht ergänzt.\n- Mit `reindex: true` wird der Suchindex der Seite nach dem Speichern aktualisiert (nur wenn der Index eingerichtet ist).\n- Kurztextfelder werden HTML-maskiert gespeichert; `content`, `technical_info` und `common_issues` nicht.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/handbook/update_handbook_page \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"page_id\": 1042,\n    \"data\": {\n        \"status\": \"active\",\n        \"version\": \"1.1\",\n        \"last_reviewed\": \"2026-11-09\",\n        \"tags\": [\n            \"service\",\n            \"anfragen\",\n            \"sla\"\n        ]\n    },\n    \"reindex\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/handbook/update_handbook_page\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"page_id\": 1042,\n        \"data\": {\n            \"status\": \"active\",\n            \"version\": \"1.1\",\n            \"last_reviewed\": \"2026-11-09\",\n            \"tags\": [\n                \"service\",\n                \"anfragen\",\n                \"sla\",\n            ],\n        },\n        \"reindex\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/handbook/update_handbook_page\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"page_id\": 1042,\n      \"data\": {\n          \"status\": \"active\",\n          \"version\": \"1.1\",\n          \"last_reviewed\": \"2026-11-09\",\n          \"tags\": [\n              \"service\",\n              \"anfragen\",\n              \"sla\"\n          ]\n      },\n      \"reindex\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"page_id":{"type":"integer","description":"Numerische ID der zu ändernden Seite (Pflicht).","example":1042},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern in snake_case, z. B. `{\"status\": \"active\", \"tags\": [\"sop\"]}`. Muss mindestens einen Schlüssel enthalten; `null`-Werte werden übergangen.","example":{"status":"active","version":"1.1","last_reviewed":"2026-11-09","tags":["service","anfragen","sla"]}},"reindex":{"type":"boolean","description":"Wenn `true`, wird die Seite nach dem Speichern im Suchindex aktualisiert, damit `semantic_search_handbook` die Änderung findet. Standard `false`.","example":true}},"required":["page_id","data"]},"example":{"page_id":1042,"data":{"status":"active","version":"1.1","last_reviewed":"2026-11-09","tags":["service","anfragen","sla"]},"reindex":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"handbook","werkzeug":"update_handbook_page","stufe":"schreiben","data":{"success":true,"data":{"id":1042,"title":"Kundenanfragen im Tagesgeschäft bearbeiten","slug":"kundenanfragen-bearbeiten","category":"02-sops","subcategory":"Kundenservice","page_type":"sop","status":"active","version":"1.1","company_id":12345,"is_internal":false,"goal":"Anfragen innerhalb eines Werktags beantworten","responsible":"Lena Beispiel","author":"Max Muster","access_level":"employee","source_interview":null,"job_id":null,"chroma_indexed":true,"chroma_collection":"handbook_pages","created_at":"2026-11-03T09:12:08+01:00","updated_at":"2026-11-09T10:15:42+01:00","last_reviewed":"2026-11-09T00:00:00+00:00","tags":["service","anfragen","sla"],"steps":[{"title":"Anfrage sichten","description":"Posteingang prüfen und Dringlichkeit festlegen"},{"title":"Antwort verfassen","description":"Vorlage wählen und anpassen"}],"checklist":[{"item":"Ticket angelegt"},{"item":"Antwort versendet"}],"kpis":[{"name":"Antwortzeit","target":"< 24 h"}],"related_pages":[],"changelog":[],"content":"## Ablauf\n\nJede Kundenanfrage wird am selben Werktag gesichtet und spätestens am Folgetag beantwortet.","technical_info":null,"common_issues":null},"message":"Handbook-Seite aktualisiert","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/handbook/delete_handbook_page":{"post":{"tags":["Handbuch"],"operationId":"handbook__delete_handbook_page","summary":"Handbuch-Seite löschen","description":"Löscht eine Handbuch-Seite **endgültig**. Die Funktion verlangt die ausdrückliche Bestätigung (`bestaetigt: true`) und eine Seite, auf die du Zugriff hast. Mit `remove_index: true` wird zuvor der zugehörige Eintrag aus dem Suchindex entfernt; ohne diese Angabe bleibt ein eventueller Indexeintrag stehen. Bei Erfolg liefert die Antwort `data: null` und `message: \"Handbook-Seite gelöscht\"`. Eine nicht gefundene oder nicht zugängliche Seite ergibt `success: false`. Wenn du den Inhalt vorher sichern willst, lege mit `copy_handbook_page` eine Kopie an.\n\n**Gut zu wissen**\n\n- Das Löschen ist nicht rückgängig zu machen.\n- Ohne `remove_index` kann ein Eintrag im Suchindex verwaist zurückbleiben.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/handbook/delete_handbook_page \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"page_id\": 1060,\n    \"remove_index\": true,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/handbook/delete_handbook_page\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"page_id\": 1060,\n        \"remove_index\": True,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/handbook/delete_handbook_page\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"page_id\": 1060,\n      \"remove_index\": true,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"page_id":{"type":"integer","description":"Numerische ID der zu löschenden Seite (Pflicht).","example":1060},"remove_index":{"type":"boolean","description":"Wenn `true`, wird der Eintrag der Seite zusätzlich aus dem Suchindex gelöscht, bevor die Seite entfällt. Standard `false`.","example":true},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["page_id","bestaetigt"]},"example":{"page_id":1060,"remove_index":true,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"handbook","werkzeug":"delete_handbook_page","stufe":"loeschen","data":{"success":true,"data":null,"message":"Handbook-Seite gelöscht","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/handbook/copy_handbook_page":{"post":{"tags":["Handbuch"],"operationId":"handbook__copy_handbook_page","summary":"Handbuch-Seite kopieren","description":"Erstellt eine Kopie einer vorhandenen Seite. Übernommen werden Kategorie, Unterkategorie, Seitentyp, Version, Ziel, Inhalt, Schritte, Checkliste, KPIs, technische Infos, häufige Probleme, verwandte Seiten, Workflow-Referenz, Verantwortliche, Autor, Zugriffsstufe, interne Markierung und Tags. Ohne `title` heißt die Kopie „<Originaltitel> (Kopie)“; ohne `slug` wird er aus dem Titel erzeugt. Der Status der Kopie ist `draft`, sofern du keinen anderen Wert angibst. Die Kopie wird dem Unternehmen des Aufrufers zugeordnet und mit `message: \"Handbook-Seite kopiert\"` vollständig zurückgegeben. Index-Status und Änderungsverlauf werden nicht mitkopiert; mit `auto_index` indexierst du die Kopie sofort.\n\n**Gut zu wissen**\n\n- Die Kopie ist als unveröffentlicht angelegt (`published` = false) und liegt im selben Ordner wie das Original.\n- Der Slug wird nicht auf Eindeutigkeit geprüft; der interne Objektschlüssel wird dagegen bei Kollision nummeriert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/handbook/copy_handbook_page \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"page_id\": 1042,\n    \"title\": \"Kundenanfragen bearbeiten – Variante Support\",\n    \"status\": \"draft\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/handbook/copy_handbook_page\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"page_id\": 1042,\n        \"title\": \"Kundenanfragen bearbeiten – Variante Support\",\n        \"status\": \"draft\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/handbook/copy_handbook_page\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"page_id\": 1042,\n      \"title\": \"Kundenanfragen bearbeiten – Variante Support\",\n      \"status\": \"draft\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"page_id":{"type":"integer","description":"Numerische ID der Quellseite (Pflicht).","example":1042},"title":{"type":"string","description":"Neuer Titel für die Kopie. Ohne Angabe: Originaltitel plus ` (Kopie)`.","example":"Kundenanfragen bearbeiten – Variante Support"},"slug":{"type":"string","description":"Optionaler neuer Slug. Ohne Angabe wird er aus dem Titel der Kopie erzeugt."},"status":{"type":"string","description":"Status der Kopie, Standard `draft`. Der Wert wird nicht gegen eine Liste geprüft.","example":"draft"},"auto_index":{"type":"boolean","description":"Wenn `true`, wird die Kopie nach dem Speichern direkt in den Suchindex aufgenommen (nur wenn der Index eingerichtet ist)."}},"required":["page_id"]},"example":{"page_id":1042,"title":"Kundenanfragen bearbeiten – Variante Support","status":"draft"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"handbook","werkzeug":"copy_handbook_page","stufe":"schreiben","data":{"success":true,"data":{"id":1061,"title":"Kundenanfragen bearbeiten – Variante Support","slug":"kundenanfragen-bearbeiten-variante-support","category":"02-sops","subcategory":"Kundenservice","page_type":"sop","status":"draft","version":"1.0","company_id":12345,"is_internal":false,"goal":"Anfragen innerhalb eines Werktags beantworten","responsible":"Lena Beispiel","author":"Max Muster","access_level":"employee","source_interview":null,"job_id":null,"chroma_indexed":false,"chroma_collection":null,"created_at":"2026-11-09T10:20:05+01:00","updated_at":"2026-11-09T10:20:05+01:00","last_reviewed":null,"tags":["service","anfragen"],"steps":[{"title":"Anfrage sichten","description":"Posteingang prüfen und Dringlichkeit festlegen"},{"title":"Antwort verfassen","description":"Vorlage wählen und anpassen"}],"checklist":[{"item":"Ticket angelegt"},{"item":"Antwort versendet"}],"kpis":[{"name":"Antwortzeit","target":"< 24 h"}],"related_pages":[],"changelog":[],"content":"## Ablauf\n\nJede Kundenanfrage wird am selben Werktag gesichtet und spätestens am Folgetag beantwortet.","technical_info":null,"common_issues":null},"message":"Handbook-Seite kopiert","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/handbook/reindex_handbook_page":{"post":{"tags":["Handbuch"],"operationId":"handbook__reindex_handbook_page","summary":"Suchindex einer Seite pflegen","description":"Nimmt eine Seite in den Suchindex für die semantische Suche auf, aktualisiert sie dort oder entfernt sie wieder. Mit `action: \"index\"` wird die Seite neu aufgenommen, mit `\"update\"` (Standard) der bestehende Eintrag erneuert, mit `\"delete\"` entfernt. Zurück kommt die vollständige Seite (inklusive `content`) mit `message: \"Chroma-Index aktualisiert\"`; ihre Felder `chroma_indexed` und `chroma_collection` zeigen den Indexstand. Ist der Index im System nicht eingerichtet, antwortet die Funktion mit `success: false`. Sinnvoll nach größeren Änderungen oder wenn `semantic_search_handbook` eine Seite nicht findet.\n\n**Gut zu wissen**\n\n- Jeder andere Wert als `index` oder `delete` wird wie `update` behandelt.\n- Die Seite selbst bleibt bei `delete` erhalten – nur der Indexeintrag verschwindet.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/handbook/reindex_handbook_page \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"page_id\": 1042,\n    \"action\": \"index\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/handbook/reindex_handbook_page\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"page_id\": 1042,\n        \"action\": \"index\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/handbook/reindex_handbook_page\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"page_id\": 1042,\n      \"action\": \"index\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"page_id":{"type":"integer","description":"Numerische ID der Seite (Pflicht).","example":1042},"action":{"type":"string","enum":["index","update","delete"],"description":"Index-Aktion: `index` (neu aufnehmen), `update` (erneuern, Standard) oder `delete` (aus dem Index entfernen).","example":"index"}},"required":["page_id"]},"example":{"page_id":1042,"action":"index"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"handbook","werkzeug":"reindex_handbook_page","stufe":"schreiben","data":{"success":true,"data":{"id":1042,"title":"Kundenanfragen im Tagesgeschäft bearbeiten","slug":"kundenanfragen-bearbeiten","category":"02-sops","subcategory":"Kundenservice","page_type":"sop","status":"draft","version":"1.0","company_id":12345,"is_internal":false,"goal":"Anfragen innerhalb eines Werktags beantworten","responsible":"Lena Beispiel","author":"Max Muster","access_level":"employee","source_interview":null,"job_id":null,"chroma_indexed":true,"chroma_collection":"handbook_pages","created_at":"2026-11-03T09:12:08+01:00","updated_at":"2026-11-08T16:40:21+01:00","last_reviewed":null,"tags":["service","anfragen"],"steps":[{"title":"Anfrage sichten","description":"Posteingang prüfen und Dringlichkeit festlegen"},{"title":"Antwort verfassen","description":"Vorlage wählen und anpassen"}],"checklist":[{"item":"Ticket angelegt"},{"item":"Antwort versendet"}],"kpis":[{"name":"Antwortzeit","target":"< 24 h"}],"related_pages":[],"changelog":[],"content":"## Ablauf\n\nJede Kundenanfrage wird am selben Werktag gesichtet und spätestens am Folgetag beantwortet.","technical_info":null,"common_issues":null},"message":"Chroma-Index aktualisiert","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/handbook/summarize_handbook_page":{"post":{"tags":["Handbuch"],"operationId":"handbook__summarize_handbook_page","summary":"Handbuch-Seite zusammenfassen","description":"Analysiert eine Seite mit Hilfe von KI und liefert eine strukturierte Auswertung in `data.analysis` mit den Feldern `summary` (Zusammenfassung), `keySteps`, `risks`, `improvementIdeas` und `recommendedReviewers` (jeweils Listen von Texten, außer `summary`). Neben der Analyse enthält `data.page` die Metadaten der Seite (ohne `content`). Grundlage sind Titel, Kategorie, Typ, Status, Ziel, Verantwortliche, Inhalt, Schritte, Checkliste und KPIs der Seite. Mit `focus` lenkst du die Auswertung auf einen Schwerpunkt (z. B. Onboarding, Qualitätssicherung, Schulung). Die Funktion ändert die Seite nicht. Ließ sich die Antwort nicht als Struktur lesen, steht der Rohtext in `analysis.summary` und die Listen sind leer.\n\n**Gut zu wissen**\n\n- Die Auswertung erzeugt bei jedem Aufruf ein neues Ergebnis und kann sich leicht unterscheiden.\n- Der Aufruf kann einige Sekunden dauern, da er eine KI-Auswertung enthält.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/handbook/summarize_handbook_page \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"page_id\": 1042,\n    \"focus\": \"onboarding\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/handbook/summarize_handbook_page\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"page_id\": 1042,\n        \"focus\": \"onboarding\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/handbook/summarize_handbook_page\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"page_id\": 1042,\n      \"focus\": \"onboarding\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"page_id":{"type":"integer","description":"Numerische ID der Seite (Pflicht).","example":1042},"focus":{"type":"string","description":"Optionaler Schwerpunkt der Auswertung als Freitext, z. B. `onboarding`, `qa` oder `training`. Ohne Angabe wird `standard` verwendet.","example":"onboarding"}},"required":["page_id"]},"example":{"page_id":1042,"focus":"onboarding"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"handbook","werkzeug":"summarize_handbook_page","stufe":"schreiben","data":{"success":true,"data":{"analysis":{"summary":"Die Seite beschreibt, wie Kundenanfragen am selben Werktag gesichtet und spätestens am Folgetag beantwortet werden.","keySteps":["Anfrage sichten und Dringlichkeit bestimmen","Antwort aus Vorlage erstellen","Ticket schließen"],"risks":["Keine Vertretungsregel für Abwesenheiten"],"improvementIdeas":["Eskalationsstufe für Anfragen über 48 Stunden ergänzen"],"recommendedReviewers":["Leitung Kundenservice"]},"page":{"id":1042,"title":"Kundenanfragen im Tagesgeschäft bearbeiten","slug":"kundenanfragen-bearbeiten","category":"02-sops","subcategory":"Kundenservice","page_type":"sop","status":"draft","version":"1.0","company_id":12345,"is_internal":false,"goal":"Anfragen innerhalb eines Werktags beantworten","responsible":"Lena Beispiel","author":"Max Muster","access_level":"employee","source_interview":null,"job_id":null,"chroma_indexed":false,"chroma_collection":null,"created_at":"2026-11-03T09:12:08+01:00","updated_at":"2026-11-08T16:40:21+01:00","last_reviewed":null,"tags":["service","anfragen"],"steps":[{"title":"Anfrage sichten","description":"Posteingang prüfen und Dringlichkeit festlegen"},{"title":"Antwort verfassen","description":"Vorlage wählen und anpassen"}],"checklist":[{"item":"Ticket angelegt"},{"item":"Antwort versendet"}],"kpis":[{"name":"Antwortzeit","target":"< 24 h"}],"related_pages":[],"changelog":[]}},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/speechanalysis/get_speech_profiles":{"post":{"tags":["Sprachprofile"],"operationId":"speechanalysis__get_speech_profiles","summary":"Sprachprofile auflisten","description":"Listet die Sprachprofile deines Unternehmens, **neueste zuerst** (Sortierung nach Erstellzeitpunkt absteigend). In `data` kommen `items`, `limit`, `offset` und `total` (Gesamtzahl der Profile mit den gesetzten Filtern). Die Einträge enthalten nur Kopfdaten: `id`, `name`, `company_id`, `interview_id`, `member_id`, `status`, `lang`, `created_at`, `updated_at`. Die Inhalte `analysis` und `profile_data` fehlen in der Liste; dafür brauchst du `get_speech_profile`. Ohne `limit` werden 25 Einträge geliefert. Zeitstempel sind im ISO-8601-Format, fehlende Zeitstempel sind `null`.\n\n**Gut zu wissen**\n\n- Der Status ist ein freier Text; Profile aus einer Interview-Auswertung tragen typischerweise `generated`.\n- Blättere mit `offset`, wenn `total` größer als `limit` ist.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/speechanalysis/get_speech_profiles \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"generated\",\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/speechanalysis/get_speech_profiles\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"generated\",\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/speechanalysis/get_speech_profiles\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"generated\",\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Optionaler Filter auf einen exakten Status-Text, z. B. `generated`. Ohne Angabe werden alle Status geliefert.","example":"generated"},"limit":{"type":"integer","description":"Maximale Anzahl Profile pro Antwort. Standard 25, Minimum 1; eine feste Obergrenze gibt es im Code nicht.","example":10},"offset":{"type":"integer","description":"Anzahl der zu überspringenden Profile für die Seiteneinteilung. Standard 0, negative Werte werden zu 0."}}},"example":{"status":"generated","limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"speechanalysis","werkzeug":"get_speech_profiles","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":5021,"name":"Sprachprofil Gründerin Beispiel","company_id":12345,"interview_id":3310,"member_id":801,"status":"generated","lang":"de","created_at":"2026-10-28T14:05:11+01:00","updated_at":"2026-11-02T09:30:00+01:00"}],"limit":10,"offset":0,"total":1},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/speechanalysis/get_speech_profile":{"post":{"tags":["Sprachprofile"],"operationId":"speechanalysis__get_speech_profile","summary":"Ein Sprachprofil abrufen","description":"Lädt ein einzelnes Sprachprofil deines Unternehmens und liefert zusätzlich zu den Kopfdaten die Inhalte `analysis` und `profile_data`. Beide Felder sind gespeichert als Text; die Funktion versucht, sie als JSON zu dekodieren, und liefert dann verschachtelte Objekte. Ist der Text kein gültiges JSON, kommt der Rohtext zurück. Typische Bereiche des Profils sind `lexikalisch`, `syntaktisch`, `pragmatisch`, `limbisch` und `paraverbal`. Profile anderer Unternehmen sind nicht zugänglich und ergeben `success: false` mit dem Hinweis „nicht gefunden oder kein Zugriff“.\n\n**Gut zu wissen**\n\n- Die genaue Struktur von `analysis` und `profile_data` hängt vom jeweiligen Profil ab; verlasse dich nicht auf feste Unterfelder.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/speechanalysis/get_speech_profile \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"profile_id\": 5021\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/speechanalysis/get_speech_profile\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"profile_id\": 5021,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/speechanalysis/get_speech_profile\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"profile_id\": 5021\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"profile_id":{"type":"integer","description":"Numerische ID des Sprachprofils (Pflicht). Die ID stammt aus `get_speech_profiles`.","example":5021}},"required":["profile_id"]},"example":{"profile_id":5021}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"speechanalysis","werkzeug":"get_speech_profile","stufe":"lesen","data":{"success":true,"data":{"id":5021,"name":"Sprachprofil Gründerin Beispiel","company_id":12345,"interview_id":3310,"member_id":801,"status":"generated","lang":"de","created_at":"2026-10-28T14:05:11+01:00","updated_at":"2026-11-02T09:30:00+01:00","analysis":{"lexikalisch":{"wortschatz_niveau":"gehoben, fachsprachlich"},"pragmatisch":{"kommunikationsstil":"direkt, lösungsorientiert"},"limbisch":{"dominanter_typ":"Dominanz","motivationsfaktoren":["Autonomie","Wirksamkeit"]}},"profile_data":{"titel":"Sprachprofil Gründerin Beispiel","lexikalisch":{"wortschatz_niveau":"gehoben, fachsprachlich"},"pragmatisch":{"kommunikationsstil":"direkt, lösungsorientiert"},"limbisch":{"dominanter_typ":"Dominanz","motivationsfaktoren":["Autonomie","Wirksamkeit"]}}},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/speechanalysis/update_speech_profile":{"post":{"tags":["Sprachprofile"],"operationId":"speechanalysis__update_speech_profile","summary":"Sprachprofil aktualisieren","description":"Ändert Felder eines Sprachprofils. In `data` übergibst du nur, was sich ändern soll. Berücksichtigt werden `analysis` (Text), `profile_data` (Objekt oder Text; ein Objekt wird als JSON gespeichert), `status`, `lang` und `name`. Schlüssel mit `null` oder unbekannte Schlüssel werden ignoriert. Eine leere `data` ergibt einen Fehler („Keine Daten zum Aktualisieren übergeben“). Beim Speichern wird `updated_at` auf den aktuellen Zeitpunkt gesetzt. Die Antwort enthält das vollständige Profil mit `analysis` und `profile_data` sowie `message: \"Sprachprofil aktualisiert\"`.\n\n**Gut zu wissen**\n\n- `analysis` ist ein Textfeld: Übergib JSON als String, nicht als Objekt.\n- `name`, `status` und `lang` werden HTML-maskiert gespeichert (`&` wird zu `&amp;`).\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/speechanalysis/update_speech_profile \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"profile_id\": 5021,\n    \"data\": {\n        \"name\": \"Sprachprofil Geschäftsführerin Beispiel\",\n        \"status\": \"reviewed\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/speechanalysis/update_speech_profile\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"profile_id\": 5021,\n        \"data\": {\n            \"name\": \"Sprachprofil Geschäftsführerin Beispiel\",\n            \"status\": \"reviewed\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/speechanalysis/update_speech_profile\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"profile_id\": 5021,\n      \"data\": {\n          \"name\": \"Sprachprofil Geschäftsführerin Beispiel\",\n          \"status\": \"reviewed\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"profile_id":{"type":"integer","description":"Numerische ID des zu ändernden Profils (Pflicht).","example":5021},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern: `name`, `status`, `lang` (Texte), `analysis` (Text) und `profile_data` (Objekt oder Text). Es muss mindestens ein Schlüssel enthalten sein.","example":{"name":"Sprachprofil Geschäftsführerin Beispiel","status":"reviewed"}}},"required":["profile_id","data"]},"example":{"profile_id":5021,"data":{"name":"Sprachprofil Geschäftsführerin Beispiel","status":"reviewed"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"speechanalysis","werkzeug":"update_speech_profile","stufe":"schreiben","data":{"success":true,"data":{"id":5021,"name":"Sprachprofil Geschäftsführerin Beispiel","company_id":12345,"interview_id":3310,"member_id":801,"status":"reviewed","lang":"de","created_at":"2026-10-28T14:05:11+01:00","updated_at":"2026-11-09T10:15:42+01:00","analysis":{"lexikalisch":{"wortschatz_niveau":"gehoben, fachsprachlich"},"pragmatisch":{"kommunikationsstil":"direkt, lösungsorientiert"},"limbisch":{"dominanter_typ":"Dominanz","motivationsfaktoren":["Autonomie","Wirksamkeit"]}},"profile_data":{"titel":"Sprachprofil Gründerin Beispiel","lexikalisch":{"wortschatz_niveau":"gehoben, fachsprachlich"},"pragmatisch":{"kommunikationsstil":"direkt, lösungsorientiert"},"limbisch":{"dominanter_typ":"Dominanz","motivationsfaktoren":["Autonomie","Wirksamkeit"]}}},"message":"Sprachprofil aktualisiert","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/speechanalysis/delete_speech_profile":{"post":{"tags":["Sprachprofile"],"operationId":"speechanalysis__delete_speech_profile","summary":"Sprachprofil löschen","description":"Löscht ein Sprachprofil deines Unternehmens **endgültig**. Die Funktion verlangt die ausdrückliche Bestätigung (`bestaetigt: true`). Bei Erfolg liefert sie `data: null` und `message: \"Sprachprofil gelöscht\"`. Ist das Profil unbekannt oder gehört es einem anderen Unternehmen, antwortet sie mit `success: false`. Das zugrunde liegende Interview wird nicht angefasst. Wenn du nur den Inhalt korrigieren willst, nutze `update_speech_profile`.\n\n**Gut zu wissen**\n\n- Das Löschen ist nicht rückgängig zu machen.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/speechanalysis/delete_speech_profile \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"profile_id\": 5021,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/speechanalysis/delete_speech_profile\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"profile_id\": 5021,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/speechanalysis/delete_speech_profile\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"profile_id\": 5021,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"profile_id":{"type":"integer","description":"Numerische ID des zu löschenden Profils (Pflicht).","example":5021},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["profile_id","bestaetigt"]},"example":{"profile_id":5021,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"speechanalysis","werkzeug":"delete_speech_profile","stufe":"loeschen","data":{"success":true,"data":null,"message":"Sprachprofil gelöscht","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/speechanalysis/summarize_speech_profile":{"post":{"tags":["Sprachprofile"],"operationId":"speechanalysis__summarize_speech_profile","summary":"Sprachprofil kurz zusammenfassen","description":"Liefert eine kompakte Zusammenfassung eines Sprachprofils, ohne einen KI-Aufruf: Die Werte werden aus den gespeicherten Profildaten gelesen. `data` enthält drei Teile: `profile` (Kopfdaten samt `analysis` und `profile_data`), `summary` und `raw` (die Rohauswertung als Objekt, sonst der gespeicherte Text). In `summary` stehen `title` (Profilname, sonst der Titel aus den Profildaten, sonst „Sprachprofil“), `status`, `lang`, `dominant_limbic_type`, `communication_style`, `needs` (Liste der Motivationsfaktoren) und `lexical_level` (Wortschatzniveau). Fehlt ein Wert in den Profildaten, ist das Feld `null` bzw. eine leere Liste. Die Funktion verändert nichts.\n\n**Gut zu wissen**\n\n- Die Kurzwerte stammen aus den Bereichen `limbisch`, `pragmatisch` und `lexikalisch` von `profile_data`; fehlen diese, bleibt die Zusammenfassung weitgehend leer.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/speechanalysis/summarize_speech_profile \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"profile_id\": 5021\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/speechanalysis/summarize_speech_profile\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"profile_id\": 5021,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/speechanalysis/summarize_speech_profile\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"profile_id\": 5021\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"profile_id":{"type":"integer","description":"Numerische ID des Sprachprofils (Pflicht).","example":5021}},"required":["profile_id"]},"example":{"profile_id":5021}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"speechanalysis","werkzeug":"summarize_speech_profile","stufe":"schreiben","data":{"success":true,"data":{"profile":{"id":5021,"name":"Sprachprofil Gründerin Beispiel","company_id":12345,"interview_id":3310,"member_id":801,"status":"generated","lang":"de","created_at":"2026-10-28T14:05:11+01:00","updated_at":"2026-11-02T09:30:00+01:00","analysis":{"lexikalisch":{"wortschatz_niveau":"gehoben, fachsprachlich"},"pragmatisch":{"kommunikationsstil":"direkt, lösungsorientiert"},"limbisch":{"dominanter_typ":"Dominanz","motivationsfaktoren":["Autonomie","Wirksamkeit"]}},"profile_data":{"titel":"Sprachprofil Gründerin Beispiel","lexikalisch":{"wortschatz_niveau":"gehoben, fachsprachlich"},"pragmatisch":{"kommunikationsstil":"direkt, lösungsorientiert"},"limbisch":{"dominanter_typ":"Dominanz","motivationsfaktoren":["Autonomie","Wirksamkeit"]}}},"summary":{"title":"Sprachprofil Gründerin Beispiel","status":"generated","lang":"de","dominant_limbic_type":"Dominanz","communication_style":"direkt, lösungsorientiert","needs":["Autonomie","Wirksamkeit"],"lexical_level":"gehoben, fachsprachlich"},"raw":{"lexikalisch":{"wortschatz_niveau":"gehoben, fachsprachlich"},"pragmatisch":{"kommunikationsstil":"direkt, lösungsorientiert"},"limbisch":{"dominanter_typ":"Dominanz","motivationsfaktoren":["Autonomie","Wirksamkeit"]}}},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/get_templates":{"post":{"tags":["Interviews"],"operationId":"interviews__get_templates","summary":"Interview-Vorlagen auflisten","description":"Liefert die Interview-Vorlagen deines Unternehmens und – standardmäßig – die globalen Katalog-Vorlagen. `data.items` ist die zusammengeführte Liste (**Katalog zuerst**, dann eigene), außerdem gibt es `catalog_templates`, `company_templates` und die Zähler `total`, `catalog_count`, `company_count`. Die Einträge nennen die Felder `id`, `name`, `status`, `interviewtype`, `userprompt`, `intro`, `outtro`, `interviewquestions` (als Liste, sofern gültiges JSON), `locale`, `ispartner`, `iscatalog`, `companyid` sowie `createdAt` und `modifiedAt` als Unix-Zeitstempel. Eine Sortierung ist nicht festgelegt. Mit `include_catalog: false` erhältst du nur deine eigenen Vorlagen.\n\n**Gut zu wissen**\n\n- Die Feldnamen dieser Liste (`interviewtype`, `interviewquestions`, `companyid`) unterscheiden sich von denen in `get_template` (`interview_type`, `questions`, `company_id`).\n- Zeitstempel sind hier Unix-Sekunden, in `get_template` ISO-8601.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/get_templates \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"include_catalog\": false\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/get_templates\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"include_catalog\": False,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/get_templates\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"include_catalog\": false\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"include_catalog":{"type":"boolean","description":"Wenn `true` (Standard), werden zusätzlich die globalen Katalog-Vorlagen geliefert; bei `false` nur die Vorlagen deines Unternehmens.","default":true,"example":false}}},"example":{"include_catalog":false}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"get_templates","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":2201,"name":"Erstgespräch Beispiel GmbH","status":"draft","interviewtype":"firstcall","userprompt":"Finde heraus, ob der Betrieb zum Angebot passt.","intro":"Danke, dass Sie sich Zeit nehmen.","outtro":"Vielen Dank für das Gespräch.","interviewquestions":[{"question":"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?","wanttoreach":"Selbstbild und Kernleistung verstehen","prompt":"Fasse die Selbstbeschreibung des Unternehmers zusammen."},{"question":"Was unterscheidet Sie von Ihren Wettbewerbern?","wanttoreach":"Alleinstellungsmerkmale erkennen","prompt":"Extrahiere die genannten Alleinstellungsmerkmale."}],"locale":"de","ispartner":false,"iscatalog":false,"companyid":"12345","createdAt":1760954400,"modifiedAt":1761993000}],"catalog_templates":[],"company_templates":[{"id":2201,"name":"Erstgespräch Beispiel GmbH","status":"draft","interviewtype":"firstcall","userprompt":"Finde heraus, ob der Betrieb zum Angebot passt.","intro":"Danke, dass Sie sich Zeit nehmen.","outtro":"Vielen Dank für das Gespräch.","interviewquestions":[{"question":"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?","wanttoreach":"Selbstbild und Kernleistung verstehen","prompt":"Fasse die Selbstbeschreibung des Unternehmers zusammen."},{"question":"Was unterscheidet Sie von Ihren Wettbewerbern?","wanttoreach":"Alleinstellungsmerkmale erkennen","prompt":"Extrahiere die genannten Alleinstellungsmerkmale."}],"locale":"de","ispartner":false,"iscatalog":false,"companyid":"12345","createdAt":1760954400,"modifiedAt":1761993000}],"total":1,"catalog_count":0,"company_count":1},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/get_template":{"post":{"tags":["Interviews"],"operationId":"interviews__get_template","summary":"Eine Interview-Vorlage abrufen","description":"Lädt eine einzelne Vorlage anhand ihrer ID. Zugänglich sind Vorlagen deines Unternehmens sowie Vorlagen ohne Unternehmenszuordnung; Vorlagen eines anderen Unternehmens ergeben `success: false` („Kein Zugriff auf dieses Template“). Die Antwort enthält `id`, `name`, `status`, `interview_type`, `user_prompt`, `intro`, `outtro`, `questions`, `locale`, `is_catalog`, `company_id`, `created_at` und `updated_at` (ISO-8601). `questions` ist der gespeicherte Text; er enthält in der Regel eine JSON-Liste von Fragen mit `question`, `wanttoreach` und `prompt`. Du kannst diesen Text selbst als JSON lesen.\n\n**Gut zu wissen**\n\n- `questions` kommt als gespeicherter Text (JSON-String), nicht als bereits dekodierte Liste.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/get_template \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 2201\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/get_template\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 2201,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/get_template\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 2201\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"Numerische ID der Vorlage (Pflicht), z. B. aus `get_templates`.","example":2201}},"required":["template_id"]},"example":{"template_id":2201}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"get_template","stufe":"lesen","data":{"success":true,"data":{"id":2201,"name":"Erstgespräch Beispiel GmbH","status":"draft","interview_type":"firstcall","user_prompt":"Finde heraus, ob der Betrieb zum Angebot passt.","intro":"Danke, dass Sie sich Zeit nehmen.","outtro":"Vielen Dank für das Gespräch.","questions":"[{\"question\":\"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\"wanttoreach\":\"Selbstbild und Kernleistung verstehen\",\"prompt\":\"Fasse die Selbstbeschreibung des Unternehmers zusammen.\"},{\"question\":\"Was unterscheidet Sie von Ihren Wettbewerbern?\",\"wanttoreach\":\"Alleinstellungsmerkmale erkennen\",\"prompt\":\"Extrahiere die genannten Alleinstellungsmerkmale.\"}]","locale":"de","is_catalog":false,"company_id":"12345","created_at":"2026-10-20T10:00:00+01:00","updated_at":"2026-11-01T15:30:00+01:00"},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/create_template":{"post":{"tags":["Interviews"],"operationId":"interviews__create_template","summary":"Interview-Vorlage anlegen","description":"Legt eine neue Vorlage für dein Unternehmen an und gibt sie im Format von `get_template` zurück (`message: \"Template erstellt\"`). Alle Angaben stehen im Objekt `data`. Berücksichtigt werden die Schlüssel `name`, `status`, `interviewtype`, `userprompt`, `intro`, `outtro`, `interviewquestions`, `locale`, `ispartner` und `iscatalog`; andere Schlüssel werden ignoriert. Ohne `status` wird `draft`, ohne `locale` `de` gesetzt. `interviewquestions` darf eine Liste sein (wird als JSON gespeichert) oder ein fertiger JSON-Text. Der Interviewtyp stammt laut Datenmodell aus: `interview`, `firstcall`, `job`, `position`, `offer`, `partner`, `book`, `personality`, `future`, `past`, `present`, `course`. Vorlagen mit denselben Daten kannst du auch über `import_template_from_json` anlegen.\n\n**Gut zu wissen**\n\n- Setze `iscatalog` nur, wenn du genau weißt, was es bewirkt: Katalog-Vorlagen sind Systemvorlagen und erscheinen in der Katalogliste (`get_templates`).\n- Es gibt keine Pflichtangabe in `data`, ein sinnvoller Aufruf enthält mindestens `name`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/create_template \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"data\": {\n        \"name\": \"Bedarfsanalyse Beispiel AG\",\n        \"status\": \"draft\",\n        \"interviewtype\": \"firstcall\",\n        \"locale\": \"de\",\n        \"intro\": \"Schön, dass wir sprechen.\",\n        \"outtro\": \"Danke für Ihre Zeit.\",\n        \"interviewquestions\": [\n            {\n                \"question\": \"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\n                \"wanttoreach\": \"Selbstbild und Kernleistung verstehen\",\n                \"prompt\": \"Fasse die Selbstbeschreibung des Unternehmers zusammen.\"\n            },\n            {\n                \"question\": \"Was unterscheidet Sie von Ihren Wettbewerbern?\",\n                \"wanttoreach\": \"Alleinstellungsmerkmale erkennen\",\n                \"prompt\": \"Extrahiere die genannten Alleinstellungsmerkmale.\"\n            }\n        ]\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/create_template\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"data\": {\n            \"name\": \"Bedarfsanalyse Beispiel AG\",\n            \"status\": \"draft\",\n            \"interviewtype\": \"firstcall\",\n            \"locale\": \"de\",\n            \"intro\": \"Schön, dass wir sprechen.\",\n            \"outtro\": \"Danke für Ihre Zeit.\",\n            \"interviewquestions\": [\n                {\n                    \"question\": \"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\n                    \"wanttoreach\": \"Selbstbild und Kernleistung verstehen\",\n                    \"prompt\": \"Fasse die Selbstbeschreibung des Unternehmers zusammen.\",\n                },\n                {\n                    \"question\": \"Was unterscheidet Sie von Ihren Wettbewerbern?\",\n                    \"wanttoreach\": \"Alleinstellungsmerkmale erkennen\",\n                    \"prompt\": \"Extrahiere die genannten Alleinstellungsmerkmale.\",\n                },\n            ],\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/create_template\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"data\": {\n          \"name\": \"Bedarfsanalyse Beispiel AG\",\n          \"status\": \"draft\",\n          \"interviewtype\": \"firstcall\",\n          \"locale\": \"de\",\n          \"intro\": \"Schön, dass wir sprechen.\",\n          \"outtro\": \"Danke für Ihre Zeit.\",\n          \"interviewquestions\": [\n              {\n                  \"question\": \"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\n                  \"wanttoreach\": \"Selbstbild und Kernleistung verstehen\",\n                  \"prompt\": \"Fasse die Selbstbeschreibung des Unternehmers zusammen.\"\n              },\n              {\n                  \"question\": \"Was unterscheidet Sie von Ihren Wettbewerbern?\",\n                  \"wanttoreach\": \"Alleinstellungsmerkmale erkennen\",\n                  \"prompt\": \"Extrahiere die genannten Alleinstellungsmerkmale.\"\n              }\n          ]\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"Objekt mit den Vorlagenfeldern: `name`, `status`, `interviewtype`, `userprompt`, `intro`, `outtro`, `interviewquestions` (Liste von Objekten mit `question`, `wanttoreach`, `prompt` oder JSON-Text), `locale`, `ispartner`, `iscatalog`.","example":{"name":"Bedarfsanalyse Beispiel AG","status":"draft","interviewtype":"firstcall","locale":"de","intro":"Schön, dass wir sprechen.","outtro":"Danke für Ihre Zeit.","interviewquestions":[{"question":"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?","wanttoreach":"Selbstbild und Kernleistung verstehen","prompt":"Fasse die Selbstbeschreibung des Unternehmers zusammen."},{"question":"Was unterscheidet Sie von Ihren Wettbewerbern?","wanttoreach":"Alleinstellungsmerkmale erkennen","prompt":"Extrahiere die genannten Alleinstellungsmerkmale."}]}}},"required":["data"]},"example":{"data":{"name":"Bedarfsanalyse Beispiel AG","status":"draft","interviewtype":"firstcall","locale":"de","intro":"Schön, dass wir sprechen.","outtro":"Danke für Ihre Zeit.","interviewquestions":[{"question":"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?","wanttoreach":"Selbstbild und Kernleistung verstehen","prompt":"Fasse die Selbstbeschreibung des Unternehmers zusammen."},{"question":"Was unterscheidet Sie von Ihren Wettbewerbern?","wanttoreach":"Alleinstellungsmerkmale erkennen","prompt":"Extrahiere die genannten Alleinstellungsmerkmale."}]}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"create_template","stufe":"schreiben","data":{"success":true,"data":{"id":2210,"name":"Bedarfsanalyse Beispiel AG","status":"draft","interview_type":"firstcall","user_prompt":null,"intro":"Schön, dass wir sprechen.","outtro":"Danke für Ihre Zeit.","questions":"[{\"question\":\"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\"wanttoreach\":\"Selbstbild und Kernleistung verstehen\",\"prompt\":\"Fasse die Selbstbeschreibung des Unternehmers zusammen.\"},{\"question\":\"Was unterscheidet Sie von Ihren Wettbewerbern?\",\"wanttoreach\":\"Alleinstellungsmerkmale erkennen\",\"prompt\":\"Extrahiere die genannten Alleinstellungsmerkmale.\"}]","locale":"de","is_catalog":false,"company_id":"12345","created_at":"2026-10-20T10:00:00+01:00","updated_at":"2026-11-01T15:30:00+01:00"},"message":"Template erstellt","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/update_template":{"post":{"tags":["Interviews"],"operationId":"interviews__update_template","summary":"Interview-Vorlage ändern","description":"Ändert Felder einer bestehenden Vorlage und gibt sie im Format von `get_template` zurück (`message: \"Template aktualisiert\"`). In `data` stehen nur die Felder, die sich ändern: `name`, `status`, `interviewtype`, `userprompt`, `intro`, `outtro`, `interviewquestions`, `locale`, `ispartner`, `iscatalog`. Schlüssel mit `null` und unbekannte Schlüssel werden ignoriert. `interviewquestions` ersetzt die vorhandene Fragenliste vollständig – übergib also immer die komplette Liste (Liste von Objekten oder JSON-Text). Bereits laufende Interviews behalten ihre kopierten Fragen; die Änderung wirkt nur auf neu angelegte Interviews.\n\n**Gut zu wissen**\n\n- Bestehende Interviews sind von Vorlagenänderungen nicht betroffen.\n- Ändere nur Vorlagen deines eigenen Unternehmens; Katalog-Vorlagen sind Systemvorlagen.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/update_template \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 2201,\n    \"data\": {\n        \"status\": \"active\",\n        \"outtro\": \"Vielen Dank, wir melden uns in den nächsten Tagen.\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/update_template\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 2201,\n        \"data\": {\n            \"status\": \"active\",\n            \"outtro\": \"Vielen Dank, wir melden uns in den nächsten Tagen.\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/update_template\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 2201,\n      \"data\": {\n          \"status\": \"active\",\n          \"outtro\": \"Vielen Dank, wir melden uns in den nächsten Tagen.\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"Numerische ID der zu ändernden Vorlage (Pflicht).","example":2201},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern (`name`, `status`, `interviewtype`, `userprompt`, `intro`, `outtro`, `interviewquestions`, `locale`, `ispartner`, `iscatalog`). `interviewquestions` ersetzt die gesamte Liste.","example":{"status":"active","outtro":"Vielen Dank, wir melden uns in den nächsten Tagen."}}},"required":["template_id","data"]},"example":{"template_id":2201,"data":{"status":"active","outtro":"Vielen Dank, wir melden uns in den nächsten Tagen."}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"update_template","stufe":"schreiben","data":{"success":true,"data":{"id":2201,"name":"Erstgespräch Beispiel GmbH","status":"active","interview_type":"firstcall","user_prompt":"Finde heraus, ob der Betrieb zum Angebot passt.","intro":"Danke, dass Sie sich Zeit nehmen.","outtro":"Vielen Dank, wir melden uns in den nächsten Tagen.","questions":"[{\"question\":\"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\"wanttoreach\":\"Selbstbild und Kernleistung verstehen\",\"prompt\":\"Fasse die Selbstbeschreibung des Unternehmers zusammen.\"},{\"question\":\"Was unterscheidet Sie von Ihren Wettbewerbern?\",\"wanttoreach\":\"Alleinstellungsmerkmale erkennen\",\"prompt\":\"Extrahiere die genannten Alleinstellungsmerkmale.\"}]","locale":"de","is_catalog":false,"company_id":"12345","created_at":"2026-10-20T10:00:00+01:00","updated_at":"2026-11-09T10:15:42+01:00"},"message":"Template aktualisiert","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/delete_template":{"post":{"tags":["Interviews"],"operationId":"interviews__delete_template","summary":"Interview-Vorlage löschen","description":"Löscht eine Vorlage **endgültig**. Die Funktion verlangt die ausdrückliche Bestätigung (`bestaetigt: true`). Bei Erfolg enthält `data` die ID der gelöschten Vorlage (`template_id`) und `message` lautet „Template gelöscht“. Eine unbekannte ID ergibt `success: false` mit dem Hinweis „Template not found“. Bereits angelegte Interviews bleiben bestehen; sie behalten die Fragen, die bei ihrer Erstellung kopiert wurden, und verweisen weiterhin auf die alte ID. Möchtest du eine Vorlage nur ablegen, setze mit `update_template` den Status um, statt zu löschen.\n\n**Gut zu wissen**\n\n- Das Löschen ist nicht rückgängig zu machen.\n- Lösche nur Vorlagen deines eigenen Unternehmens; Katalog-Vorlagen sind Systemvorlagen.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/delete_template \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 2210,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/delete_template\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 2210,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/delete_template\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 2210,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"Numerische ID der zu löschenden Vorlage (Pflicht).","example":2210},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["template_id","bestaetigt"]},"example":{"template_id":2210,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"delete_template","stufe":"loeschen","data":{"success":true,"data":{"template_id":2210},"message":"Template gelöscht","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/duplicate_template":{"post":{"tags":["Interviews"],"operationId":"interviews__duplicate_template","summary":"Interview-Vorlage duplizieren","description":"Erstellt eine Kopie einer zugänglichen Vorlage (eigene oder ohne Unternehmenszuordnung) und gibt sie im Format von `get_template` zurück (`message: \"Template kopiert\"`). Kopiert werden Status, Interviewtyp, Auftrags-Prompt, Einleitung, Ausleitung, Fragen, Sprache und die Partner-Kennzeichnung. Die Kopie ist nie eine Katalog-Vorlage (`is_catalog: false`). Ohne `name` heißt sie „<Originalname> (Kopie)“. Die Kopie übernimmt das Unternehmen der Quelle (`company_id`); bei einer Quelle ohne Unternehmen hat auch die Kopie keines. Typischer Einsatz: eine Vorlage anpassen, ohne das Original zu verändern.\n\n**Gut zu wissen**\n\n- Der Status wird von der Quelle übernommen und nicht auf `draft` zurückgesetzt.\n- Die Kopie wird in der Liste `company_templates` nur angezeigt, wenn ihr `company_id` zu deinem Unternehmen gehört.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/duplicate_template \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 2201,\n    \"name\": \"Erstgespräch Beispiel GmbH – Variante B\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/duplicate_template\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 2201,\n        \"name\": \"Erstgespräch Beispiel GmbH – Variante B\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/duplicate_template\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 2201,\n      \"name\": \"Erstgespräch Beispiel GmbH – Variante B\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"Numerische ID der Quellvorlage (Pflicht).","example":2201},"name":{"type":"string","description":"Optionaler Name der Kopie. Ohne Angabe: Originalname plus ` (Kopie)`.","example":"Erstgespräch Beispiel GmbH – Variante B"}},"required":["template_id"]},"example":{"template_id":2201,"name":"Erstgespräch Beispiel GmbH – Variante B"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"duplicate_template","stufe":"schreiben","data":{"success":true,"data":{"id":2211,"name":"Erstgespräch Beispiel GmbH – Variante B","status":"draft","interview_type":"firstcall","user_prompt":"Finde heraus, ob der Betrieb zum Angebot passt.","intro":"Danke, dass Sie sich Zeit nehmen.","outtro":"Vielen Dank für das Gespräch.","questions":"[{\"question\":\"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\"wanttoreach\":\"Selbstbild und Kernleistung verstehen\",\"prompt\":\"Fasse die Selbstbeschreibung des Unternehmers zusammen.\"},{\"question\":\"Was unterscheidet Sie von Ihren Wettbewerbern?\",\"wanttoreach\":\"Alleinstellungsmerkmale erkennen\",\"prompt\":\"Extrahiere die genannten Alleinstellungsmerkmale.\"}]","locale":"de","is_catalog":false,"company_id":"12345","created_at":"2026-10-20T10:00:00+01:00","updated_at":"2026-11-01T15:30:00+01:00"},"message":"Template kopiert","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/export_template_as_json":{"post":{"tags":["Interviews"],"operationId":"interviews__export_template_as_json","summary":"Interview-Vorlage exportieren","description":"Gibt eine Vorlage in derselben Struktur wie `get_template` zurück, damit du sie sichern oder in einem anderen Unternehmen wieder einspielen kannst. Die Antwort enthält `id`, `name`, `status`, `interview_type`, `user_prompt`, `intro`, `outtro`, `questions` (gespeicherter Text), `locale`, `is_catalog`, `company_id`, `created_at`, `updated_at`. Die Funktion verändert nichts. Für den Re-Import mit `import_template_from_json` musst du die Felder in die dort erwarteten Schlüssel übersetzen (`interviewtype`, `userprompt`, `interviewquestions` ohne Unterstriche).\n\n**Gut zu wissen**\n\n- Export- und Import-Schlüssel stimmen nicht überein: Der Export nutzt `interview_type`, `user_prompt`, `questions`, der Import erwartet `interviewtype`, `userprompt`, `interviewquestions`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/export_template_as_json \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 2201\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/export_template_as_json\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 2201,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/export_template_as_json\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 2201\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"Numerische ID der zu exportierenden Vorlage (Pflicht). Zugänglich sind eigene Vorlagen und solche ohne Unternehmenszuordnung.","example":2201}},"required":["template_id"]},"example":{"template_id":2201}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"export_template_as_json","stufe":"lesen","data":{"success":true,"data":{"id":2201,"name":"Erstgespräch Beispiel GmbH","status":"draft","interview_type":"firstcall","user_prompt":"Finde heraus, ob der Betrieb zum Angebot passt.","intro":"Danke, dass Sie sich Zeit nehmen.","outtro":"Vielen Dank für das Gespräch.","questions":"[{\"question\":\"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\"wanttoreach\":\"Selbstbild und Kernleistung verstehen\",\"prompt\":\"Fasse die Selbstbeschreibung des Unternehmers zusammen.\"},{\"question\":\"Was unterscheidet Sie von Ihren Wettbewerbern?\",\"wanttoreach\":\"Alleinstellungsmerkmale erkennen\",\"prompt\":\"Extrahiere die genannten Alleinstellungsmerkmale.\"}]","locale":"de","is_catalog":false,"company_id":"12345","created_at":"2026-10-20T10:00:00+01:00","updated_at":"2026-11-01T15:30:00+01:00"},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/import_template_from_json":{"post":{"tags":["Interviews"],"operationId":"interviews__import_template_from_json","summary":"Interview-Vorlage importieren","description":"Legt aus einem Datenobjekt eine neue Vorlage für dein Unternehmen an und gibt sie im Format von `get_template` zurück (`message: \"Template importiert\"`). Der Aufruf verhält sich wie `create_template`: berücksichtigt werden `name`, `status`, `interviewtype`, `userprompt`, `intro`, `outtro`, `interviewquestions` (Liste oder JSON-Text), `locale`, `ispartner`, `iscatalog`. Ohne `status` wird `draft`, ohne `locale` `de` gesetzt. Es entsteht immer eine neue Vorlage, eine vorhandene wird nie überschrieben. Der Schlüssel `id` und andere Fremdschlüssel werden ignoriert.\n\n**Gut zu wissen**\n\n- Der Export liefert andere Schlüsselnamen als der Import erwartet; passe `interview_type`, `user_prompt` und `questions` vorher an.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/import_template_from_json \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"data\": {\n        \"name\": \"Importierte Vorlage Beispiel\",\n        \"interviewtype\": \"offer\",\n        \"locale\": \"de\",\n        \"interviewquestions\": [\n            {\n                \"question\": \"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\n                \"wanttoreach\": \"Selbstbild und Kernleistung verstehen\",\n                \"prompt\": \"Fasse die Selbstbeschreibung des Unternehmers zusammen.\"\n            },\n            {\n                \"question\": \"Was unterscheidet Sie von Ihren Wettbewerbern?\",\n                \"wanttoreach\": \"Alleinstellungsmerkmale erkennen\",\n                \"prompt\": \"Extrahiere die genannten Alleinstellungsmerkmale.\"\n            }\n        ]\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/import_template_from_json\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"data\": {\n            \"name\": \"Importierte Vorlage Beispiel\",\n            \"interviewtype\": \"offer\",\n            \"locale\": \"de\",\n            \"interviewquestions\": [\n                {\n                    \"question\": \"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\n                    \"wanttoreach\": \"Selbstbild und Kernleistung verstehen\",\n                    \"prompt\": \"Fasse die Selbstbeschreibung des Unternehmers zusammen.\",\n                },\n                {\n                    \"question\": \"Was unterscheidet Sie von Ihren Wettbewerbern?\",\n                    \"wanttoreach\": \"Alleinstellungsmerkmale erkennen\",\n                    \"prompt\": \"Extrahiere die genannten Alleinstellungsmerkmale.\",\n                },\n            ],\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/import_template_from_json\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"data\": {\n          \"name\": \"Importierte Vorlage Beispiel\",\n          \"interviewtype\": \"offer\",\n          \"locale\": \"de\",\n          \"interviewquestions\": [\n              {\n                  \"question\": \"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\n                  \"wanttoreach\": \"Selbstbild und Kernleistung verstehen\",\n                  \"prompt\": \"Fasse die Selbstbeschreibung des Unternehmers zusammen.\"\n              },\n              {\n                  \"question\": \"Was unterscheidet Sie von Ihren Wettbewerbern?\",\n                  \"wanttoreach\": \"Alleinstellungsmerkmale erkennen\",\n                  \"prompt\": \"Extrahiere die genannten Alleinstellungsmerkmale.\"\n              }\n          ]\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"Objekt mit den Vorlagenfeldern (`name`, `status`, `interviewtype`, `userprompt`, `intro`, `outtro`, `interviewquestions`, `locale`, `ispartner`, `iscatalog`). Fragen sind eine Liste von Objekten mit `question`, `wanttoreach`, `prompt`.","example":{"name":"Importierte Vorlage Beispiel","interviewtype":"offer","locale":"de","interviewquestions":[{"question":"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?","wanttoreach":"Selbstbild und Kernleistung verstehen","prompt":"Fasse die Selbstbeschreibung des Unternehmers zusammen."},{"question":"Was unterscheidet Sie von Ihren Wettbewerbern?","wanttoreach":"Alleinstellungsmerkmale erkennen","prompt":"Extrahiere die genannten Alleinstellungsmerkmale."}]}}},"required":["data"]},"example":{"data":{"name":"Importierte Vorlage Beispiel","interviewtype":"offer","locale":"de","interviewquestions":[{"question":"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?","wanttoreach":"Selbstbild und Kernleistung verstehen","prompt":"Fasse die Selbstbeschreibung des Unternehmers zusammen."},{"question":"Was unterscheidet Sie von Ihren Wettbewerbern?","wanttoreach":"Alleinstellungsmerkmale erkennen","prompt":"Extrahiere die genannten Alleinstellungsmerkmale."}]}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"import_template_from_json","stufe":"schreiben","data":{"success":true,"data":{"id":2212,"name":"Importierte Vorlage Beispiel","status":"draft","interview_type":"offer","user_prompt":null,"intro":null,"outtro":null,"questions":"[{\"question\":\"Wie würden Sie Ihr Unternehmen in zwei Sätzen beschreiben?\",\"wanttoreach\":\"Selbstbild und Kernleistung verstehen\",\"prompt\":\"Fasse die Selbstbeschreibung des Unternehmers zusammen.\"},{\"question\":\"Was unterscheidet Sie von Ihren Wettbewerbern?\",\"wanttoreach\":\"Alleinstellungsmerkmale erkennen\",\"prompt\":\"Extrahiere die genannten Alleinstellungsmerkmale.\"}]","locale":"de","is_catalog":false,"company_id":"12345","created_at":"2026-10-20T10:00:00+01:00","updated_at":"2026-11-01T15:30:00+01:00"},"message":"Template importiert","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/get_interviews":{"post":{"tags":["Interviews"],"operationId":"interviews__get_interviews","summary":"Interviews auflisten","description":"Listet die Interviews deines Unternehmens. `data` enthält `items`, `total` (Gesamtzahl mit gesetztem Filter), `limit` und `offset`. Jeder Eintrag hat `id`, `name`, `status`, `is_active`, `company_id`, `interview_person`, `locale`, `template_id`, `transcript`, `insights`, `created_at`, `updated_at`. Achtung: Die Liste enthält bereits das **vollständige Transkript** und die Insights jedes Interviews und kann daher groß werden – wähle `limit` entsprechend klein. Ohne Angabe werden 20 Einträge geliefert, eine feste Sortierung ist nicht festgelegt.\n\n**Gut zu wissen**\n\n- `transcript` und `insights` sind in der Liste enthalten, nicht nur in `get_interview`.\n- Für eine verlässliche Seiteneinteilung `limit` und `offset` zusammen verwenden.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/get_interviews \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"new\",\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/get_interviews\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"new\",\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/get_interviews\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"new\",\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Optionaler Filter auf einen exakten Status-Text. Neue Interviews haben den Status `new`; weitere Werte vergibst du selbst über `update_interview`.","example":"new"},"limit":{"type":"integer","description":"Maximale Anzahl Interviews pro Antwort. Standard 20, Minimum 1.","example":10},"offset":{"type":"integer","description":"Anzahl zu überspringender Interviews für die Seiteneinteilung. Standard 0."}}},"example":{"status":"new","limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"get_interviews","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":3310,"name":"Erstgespräch Frau Beispiel","status":"new","is_active":true,"company_id":"12345","interview_person":"Anna Beispiel","locale":"de","template_id":2201,"transcript":null,"insights":null,"created_at":"2026-11-05T09:00:00+01:00","updated_at":"2026-11-05T09:00:00+01:00"}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/get_interview":{"post":{"tags":["Interviews"],"operationId":"interviews__get_interview","summary":"Ein Interview abrufen","description":"Lädt ein einzelnes Interview deines Unternehmens und liefert `id`, `name`, `status`, `is_active`, `company_id`, `interview_person`, `locale`, `template_id`, das Transkript (`transcript`, Text oder `null`), die Insights (`insights`, Text oder `null`) sowie `created_at` und `updated_at`. Interviews anderer Unternehmen sind nicht zugänglich und ergeben `success: false` („nicht gefunden oder kein Zugriff“). Die Qualitätsauswertung (Bewertung, beantwortete und fehlende Fragen) ist nicht Teil dieser Antwort; sie liefert `get_interview_insights`.\n\n**Gut zu wissen**\n\n- Das Transkript kann sehr lang sein.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/get_interview \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"interview_id\": 3310\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/get_interview\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"interview_id\": 3310,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/get_interview\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"interview_id\": 3310\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"interview_id":{"type":"integer","description":"Numerische ID des Interviews (Pflicht), z. B. aus `get_interviews` oder `create_interview`.","example":3310}},"required":["interview_id"]},"example":{"interview_id":3310}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"get_interview","stufe":"lesen","data":{"success":true,"data":{"id":3310,"name":"Erstgespräch Frau Beispiel","status":"transcribed","is_active":true,"company_id":"12345","interview_person":"Anna Beispiel","locale":"de","template_id":2201,"transcript":"Interviewer: Erzählen Sie mir von Ihrem Betrieb.\nAnna Beispiel: Wir bauen seit 2009 Küchen für Privatkunden.","insights":"Kernleistung: Küchenbau für Privatkunden; Gründung 2009.","created_at":"2026-11-05T09:00:00+01:00","updated_at":"2026-11-05T09:00:00+01:00"},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/create_interview":{"post":{"tags":["Interviews"],"operationId":"interviews__create_interview","summary":"Interview anlegen","description":"Legt ein neues Interview für dein Unternehmen an und gibt es zurück (`message: \"Interview erstellt\"`). Nur `name` ist Pflicht. Mit `template_id` verknüpfst du eine Vorlage: Interviewtyp und Fragenliste werden aus der Vorlage in das Interview kopiert (die Vorlage muss dir zugänglich sein). Fehlt `status`, wird `new` gesetzt; `locale` ist standardmäßig `de`; das Interview ist aktiv. `lead_id` verknüpft einen Lead. Als Nebenwirkung wird – falls dein Unternehmen ein Mitglied hinterlegt hat – eine Benachrichtigung „Interview bereit“ an dieses Mitglied ausgelöst; schlägt das fehl, wird das Interview trotzdem angelegt.\n\n**Gut zu wissen**\n\n- Die Antwort enthält die kopierten Fragen nicht; sie sind im Interview gespeichert und werden z. B. von der Qualitätsauswertung genutzt.\n- `name`, `interview_person`, `locale` und `status` werden HTML-maskiert gespeichert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/create_interview \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Erstgespräch Frau Beispiel\",\n    \"template_id\": 2201,\n    \"interview_person\": \"Anna Beispiel\",\n    \"locale\": \"de\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/create_interview\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"name\": \"Erstgespräch Frau Beispiel\",\n        \"template_id\": 2201,\n        \"interview_person\": \"Anna Beispiel\",\n        \"locale\": \"de\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/create_interview\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"name\": \"Erstgespräch Frau Beispiel\",\n      \"template_id\": 2201,\n      \"interview_person\": \"Anna Beispiel\",\n      \"locale\": \"de\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name des Interviews (Pflicht), z. B. ein Titel mit Gesprächspartner.","example":"Erstgespräch Frau Beispiel"},"template_id":{"type":"integer","description":"Optionale ID einer Vorlage. Typ und Fragen werden daraus übernommen. Muss zu deinem Unternehmen gehören oder global sein, sonst Fehler.","example":2201},"interview_person":{"type":"string","description":"Name des Gesprächspartners als Freitext.","example":"Anna Beispiel"},"locale":{"type":"string","description":"Sprachcode des Interviews, z. B. `de` oder `en`. Standard `de`.","default":"de","example":"de"},"status":{"type":"string","description":"Anfangsstatus als Freitext. Standard `new`."},"lead_id":{"type":"integer","description":"Optionale ID eines Leads, auf den sich das Interview bezieht. Es wird nicht geprüft, ob der Lead existiert."}},"required":["name"]},"example":{"name":"Erstgespräch Frau Beispiel","template_id":2201,"interview_person":"Anna Beispiel","locale":"de"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"create_interview","stufe":"schreiben","data":{"success":true,"data":{"id":3311,"name":"Erstgespräch Frau Beispiel","status":"new","is_active":true,"company_id":"12345","interview_person":"Anna Beispiel","locale":"de","template_id":2201,"transcript":null,"insights":null,"created_at":"2026-11-05T09:00:00+01:00","updated_at":"2026-11-05T09:00:00+01:00"},"message":"Interview erstellt","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/update_interview":{"post":{"tags":["Interviews"],"operationId":"interviews__update_interview","summary":"Interview aktualisieren","description":"Ändert Felder eines Interviews deines Unternehmens und gibt es vollständig zurück (`message: \"Interview aktualisiert\"`). In `data` stehen nur die Felder, die sich ändern: `name`, `status`, `transcript`, `interview_person`, `locale`, `template_id`, `insights`. Schlüssel mit `null` und unbekannte Schlüssel werden ignoriert. Mit `transcript` legst du den Gesprächstext ab, mit `insights` Erkenntnisse oder Notizen. Eine Qualitätsauswertung wird dadurch nicht automatisch gestartet. Eine Änderung von `template_id` setzt nur die Verknüpfung; die Fragen des Interviews werden dabei nicht neu kopiert. Der Status ist ein Freitext ohne feste Werteliste.\n\n**Gut zu wissen**\n\n- `transcript` und `insights` ersetzen den bisherigen Inhalt vollständig.\n- `template_id` wird nicht auf Existenz geprüft.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/update_interview \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"interview_id\": 3311,\n    \"data\": {\n        \"status\": \"transcribed\",\n        \"transcript\": \"Interviewer: Was ist Ihr wichtigstes Ziel?\\nAnna Beispiel: Mehr Planbarkeit im Auftragseingang.\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/update_interview\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"interview_id\": 3311,\n        \"data\": {\n            \"status\": \"transcribed\",\n            \"transcript\": \"Interviewer: Was ist Ihr wichtigstes Ziel?\\nAnna Beispiel: Mehr Planbarkeit im Auftragseingang.\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/update_interview\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"interview_id\": 3311,\n      \"data\": {\n          \"status\": \"transcribed\",\n          \"transcript\": \"Interviewer: Was ist Ihr wichtigstes Ziel?\\nAnna Beispiel: Mehr Planbarkeit im Auftragseingang.\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"interview_id":{"type":"integer","description":"Numerische ID des zu ändernden Interviews (Pflicht).","example":3311},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern: `name`, `status`, `interview_person`, `locale`, `template_id` (Ganzzahl), `transcript` (Text), `insights` (Text). Mindestens ein bekannter Schlüssel ist sinnvoll.","example":{"status":"transcribed","transcript":"Interviewer: Was ist Ihr wichtigstes Ziel?\nAnna Beispiel: Mehr Planbarkeit im Auftragseingang."}}},"required":["interview_id","data"]},"example":{"interview_id":3311,"data":{"status":"transcribed","transcript":"Interviewer: Was ist Ihr wichtigstes Ziel?\nAnna Beispiel: Mehr Planbarkeit im Auftragseingang."}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"update_interview","stufe":"schreiben","data":{"success":true,"data":{"id":3311,"name":"Erstgespräch Frau Beispiel","status":"transcribed","is_active":true,"company_id":"12345","interview_person":"Anna Beispiel","locale":"de","template_id":2201,"transcript":"Interviewer: Was ist Ihr wichtigstes Ziel?\nAnna Beispiel: Mehr Planbarkeit im Auftragseingang.","insights":null,"created_at":"2026-11-05T09:00:00+01:00","updated_at":"2026-11-09T10:15:42+01:00"},"message":"Interview aktualisiert","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/delete_interview":{"post":{"tags":["Interviews"],"operationId":"interviews__delete_interview","summary":"Interview löschen","description":"Löscht ein Interview deines Unternehmens **endgültig** einschließlich Transkript und Insights. Die Funktion verlangt die ausdrückliche Bestätigung (`bestaetigt: true`). Bei Erfolg enthält `data` die ID des gelöschten Interviews (`interview_id`) und `message` lautet „Interview gelöscht“. Interviews anderer Unternehmen oder unbekannte IDs ergeben `success: false`. Abgeleitete Ergebnisse wie bereits erzeugte Sprachprofile oder Handbuch-Seiten werden nicht automatisch entfernt. Sichere bei Bedarf vorher das Transkript über `get_interview`.\n\n**Gut zu wissen**\n\n- Das Löschen ist nicht rückgängig zu machen.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/delete_interview \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"interview_id\": 3311,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/delete_interview\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"interview_id\": 3311,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/delete_interview\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"interview_id\": 3311,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"interview_id":{"type":"integer","description":"Numerische ID des zu löschenden Interviews (Pflicht).","example":3311},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["interview_id","bestaetigt"]},"example":{"interview_id":3311,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"delete_interview","stufe":"loeschen","data":{"success":true,"data":{"interview_id":3311},"message":"Interview gelöscht","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/interviews/get_interview_insights":{"post":{"tags":["Interviews"],"operationId":"interviews__get_interview_insights","summary":"Insights und Qualitätswerte lesen","description":"Liefert die Auswertungsergebnisse eines Interviews deines Unternehmens, ohne etwas zu berechnen oder zu verändern. `data` enthält `insights` (gespeicherte Erkenntnisse/Notizen), `quality_analysis` (Ergebnis der Qualitätsanalyse), `quality_score` (Zahl), `answered_questions`, `missing_questions` und `implicit_answers` (die drei Fragenlisten aus der Auswertung), `follow_up_required` (Boolean, ob ein Nachgespräch nötig ist) und `last_quality_check` (Zeitpunkt der letzten Prüfung im ISO-8601-Format oder `null`). Felder, die noch nicht ausgewertet wurden, sind `null`. Die Listenfelder sind gespeicherte Texte (in der Regel JSON).\n\n**Gut zu wissen**\n\n- Ohne bisherige Qualitätsprüfung sind alle Auswertungsfelder `null` und `follow_up_required` ist `false`.\n- `quality_analysis`, `answered_questions`, `missing_questions` und `implicit_answers` sind gespeicherte Texte; dekodiere sie bei Bedarf als JSON.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/interviews/get_interview_insights \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"interview_id\": 3310\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/interviews/get_interview_insights\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"interview_id\": 3310,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/interviews/get_interview_insights\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"interview_id\": 3310\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"interview_id":{"type":"integer","description":"Numerische ID des Interviews (Pflicht). Es muss zu deinem Unternehmen gehören.","example":3310}},"required":["interview_id"]},"example":{"interview_id":3310}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"interviews","werkzeug":"get_interview_insights","stufe":"lesen","data":{"success":true,"data":{"insights":"Kernleistung: Küchenbau für Privatkunden.","quality_analysis":"{\"coverage_score\":78,\"recommendations\":[\"Nach Preisspanne fragen\"]}","quality_score":78,"answered_questions":"[\"Wie würden Sie Ihr Unternehmen beschreiben?\"]","missing_questions":"[\"Was unterscheidet Sie vom Wettbewerb?\"]","implicit_answers":"[]","follow_up_required":true,"last_quality_check":"2026-11-06T17:45:10+01:00"},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/get_courses":{"post":{"tags":["Akademie"],"operationId":"academy__get_courses","summary":"Kurse auflisten","description":"Liefert die Kurse deiner Firma, neueste zuerst (Sortierung nach Anlagedatum absteigend). Die Antwort enthält `items` mit je einem Kurs in der Kurzform (id, title, status, lang, price, currency, is_free, duration, course_type, company_id, interview_id, created_at, updated_at), außerdem `total` (Gesamtzahl der Treffer ohne Seitenbegrenzung), `limit` und `offset`. Beschreibungstexte, Lernziele und Prompts sind in der Liste nicht enthalten; dafür rufst du `get_course` mit der ID auf. Du kannst nach Status filtern und per Suchbegriff in Titel und Beschreibung suchen. Standardmäßig kommen 25 Kurse; zum Blättern erhöhst du `offset` um `limit`.\n\n**Gut zu wissen**\n\n- Ohne `limit` werden 25 Kurse geliefert; eine Obergrenze gibt es im Code nicht, der Mindestwert ist 1.\n- `status` vergleicht exakt auf Gleichheit. Im Datenmodell des Kurses sind `draft` und `publish` als Auswahlwerte angelegt.\n- `search` sucht mit „enthält“ in `title` und `description`, nicht in den übrigen Feldern.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/get_courses \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"draft\",\n    \"search\": \"Kalkulation\",\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/get_courses\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"draft\",\n        \"search\": \"Kalkulation\",\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/get_courses\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"draft\",\n      \"search\": \"Kalkulation\",\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Optionaler Statusfilter, exakte Übereinstimmung, z. B. `draft`. Leer oder weggelassen: alle Status. Welche Werte tatsächlich vorkommen, siehst du in den Kursen selbst; `list_course_statuses` liefert nur eine feste Beispielliste.","example":"draft"},"search":{"type":"string","description":"Optionaler Suchbegriff. Gefunden werden Kurse, deren Titel oder Beschreibung den Begriff enthalten (Teilstring, ohne Beachtung weiterer Felder). Leer oder weggelassen: keine Einschränkung.","example":"Kalkulation"},"limit":{"type":"integer","description":"Maximale Anzahl Kurse pro Antwort, ganze Zahl. Standard 25, Mindestwert 1 (kleinere Werte werden auf 1 angehoben). Eine feste Obergrenze gibt es nicht.","example":10},"offset":{"type":"integer","description":"Anzahl Kurse, die übersprungen werden, ganze Zahl ab 0 (Standard 0, negative Werte werden zu 0). Zum Blättern setzt du `offset` auf das Vielfache von `limit`."}}},"example":{"status":"draft","search":"Kalkulation","limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"get_courses","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":4711,"title":"Einführung in die Angebotskalkulation","status":"draft","lang":"de","price":"490","currency":"EUR","is_free":false,"duration":"6 Stunden","course_type":"internal","company_id":1001,"interview_id":null,"created_at":"2026-11-03T09:15:00+01:00","updated_at":"2026-11-05T14:02:31+01:00"}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/get_course":{"post":{"tags":["Akademie"],"operationId":"academy__get_course","summary":"Kurs im Detail abrufen","description":"Liefert einen einzelnen Kurs deiner Firma mit allen Inhaltsfeldern. Neben den Feldern der Listenansicht (id, title, status, lang, price, currency, is_free, duration, course_type, company_id, interview_id, created_at, updated_at) enthält die Antwort short_description, description, meta_description, learnings, target_audience, benefit, image_prompt und video_prompt. Die Module des Kurses sind nicht enthalten; die holst du mit `get_course_modules`. Existiert die ID nicht oder gehört der Kurs einer anderen Firma, kommt `success: false` mit der Meldung „Kurs nicht gefunden oder kein Zugriff“. Zeitstempel sind ISO-8601-Zeichenketten.\n\n**Gut zu wissen**\n\n- `price`, `duration` und `lang` sind im Datenmodell Textfelder und kommen als Text zurück.\n- Nicht gesetzte Felder erscheinen als `null`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/get_course \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"course_id\": 4711\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/get_course\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"course_id\": 4711,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/get_course\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"course_id\": 4711\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"course_id":{"type":"integer","description":"ID des Kurses (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_courses` oder aus der Antwort von `create_course`. Der Kurs muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kurs nicht gefunden oder kein Zugriff“.","example":4711}},"required":["course_id"]},"example":{"course_id":4711}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"get_course","stufe":"lesen","data":{"success":true,"data":{"id":4711,"title":"Einführung in die Angebotskalkulation","status":"draft","lang":"de","price":"490","currency":"EUR","is_free":false,"duration":"6 Stunden","course_type":"internal","company_id":1001,"interview_id":null,"created_at":"2026-11-03T09:15:00+01:00","updated_at":"2026-11-05T14:02:31+01:00","short_description":"Von der Anfrage zum belastbaren Angebot in sechs Schritten.","description":"<p>Du lernst, Aufwände sauber zu schätzen und Angebote nachvollziehbar aufzubauen.</p>","meta_description":"Online-Kurs zur Angebotskalkulation für Handwerksbetriebe.","learnings":"<ul><li>Aufwände schätzen</li><li>Angebote gliedern</li></ul>","target_audience":"Inhaber und Büroleitungen kleiner Betriebe","benefit":"Weniger Rückfragen und verlässlichere Preise","image_prompt":null,"video_prompt":null},"message":null,"timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/create_course":{"post":{"tags":["Akademie"],"operationId":"academy__create_course","summary":"Kurs anlegen","description":"Legt einen neuen Kurs für deine Firma an und liefert ihn in der Detailansicht zurück (wie `get_course`, inklusive der neuen `id`). Pflicht ist nur `title`. Der Kurs wird als veröffentlichtes Objekt im Ordner /courses abgelegt; der interne Schlüssel wird aus dem Titel gebildet und bei Kollisionen mit einer Nummer ergänzt. Optional setzt du Status, Sprache, Preis, Währung und das Kennzeichen „kostenlos“. Beschreibungstexte, Lernziele, Zielgruppe, Nutzen und Prompts übergibst du gesammelt im Objekt `content`. Feldnamen wie `duration` oder `coursetype` kann diese Funktion nicht setzen; dafür nutzt du anschließend `update_course`. Module und Lektionen legst du danach mit `create_module` und `create_lesson` an.\n\n**Gut zu wissen**\n\n- Der Titel wird HTML-maskiert gespeichert: Zeichen wie `&`, `<` oder `\"` werden zu HTML-Entitäten (z. B. `&amp;`).\n- Ohne `status` bleibt der Status leer (`null`). Das Datenmodell kennt `draft` und `publish`.\n- Die Texte in `content` werden unverändert gespeichert (kein Maskieren).\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/create_course \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Einführung in die Angebotskalkulation\",\n    \"status\": \"draft\",\n    \"lang\": \"de\",\n    \"price\": 490,\n    \"currency\": \"EUR\",\n    \"is_free\": false,\n    \"content\": {\n        \"short_description\": \"Von der Anfrage zum belastbaren Angebot in sechs Schritten.\",\n        \"target_audience\": \"Inhaber und Büroleitungen kleiner Betriebe\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/create_course\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"title\": \"Einführung in die Angebotskalkulation\",\n        \"status\": \"draft\",\n        \"lang\": \"de\",\n        \"price\": 490,\n        \"currency\": \"EUR\",\n        \"is_free\": False,\n        \"content\": {\n            \"short_description\": \"Von der Anfrage zum belastbaren Angebot in sechs Schritten.\",\n            \"target_audience\": \"Inhaber und Büroleitungen kleiner Betriebe\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/create_course\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"title\": \"Einführung in die Angebotskalkulation\",\n      \"status\": \"draft\",\n      \"lang\": \"de\",\n      \"price\": 490,\n      \"currency\": \"EUR\",\n      \"is_free\": false,\n      \"content\": {\n          \"short_description\": \"Von der Anfrage zum belastbaren Angebot in sechs Schritten.\",\n          \"target_audience\": \"Inhaber und Büroleitungen kleiner Betriebe\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Titel des Kurses (Pflicht, nicht leer). Wird getrimmt und HTML-maskiert gespeichert; aus dem Titel entsteht auch der interne Objektschlüssel.","example":"Einführung in die Angebotskalkulation"},"status":{"type":"string","description":"Optionaler Kursstatus als Text, z. B. `draft`. Im Datenmodell sind `draft` und `publish` als Auswahlwerte vorgesehen. Weggelassen bleibt der Status leer.","example":"draft"},"lang":{"type":"string","description":"Optionale Sprache des Kurses als Text, z. B. `de` oder `en`. Es wird kein Format geprüft; der Wert wird so gespeichert (getrimmt, HTML-maskiert).","example":"de"},"price":{"type":"number","description":"Optionaler Preis als Zahl, z. B. `490` oder `49.9`. Wird als Dezimalzahl übernommen; die Währung gibst du getrennt in `currency` an.","example":490},"currency":{"type":"string","description":"Optionale Währung als Text, z. B. `EUR`. Es erfolgt keine Prüfung gegen eine Währungsliste.","example":"EUR"},"is_free":{"type":"boolean","description":"Optional `true`, wenn der Kurs kostenlos ist, sonst `false`. Wird nur gesetzt, wenn der Schlüssel im Körper vorhanden ist; weggelassen bleibt der Standard des Datenmodells.","example":false},"content":{"type":"object","description":"Optionales Objekt mit Inhaltsfeldern. Erkannt werden die Schlüssel `short_description`, `description`, `meta_description`, `learnings`, `target_audience`, `benefit`, `image_prompt` und `video_prompt` (jeweils Text, HTML bei description/learnings möglich). Unbekannte Schlüssel werden ignoriert.","example":{"short_description":"Von der Anfrage zum belastbaren Angebot in sechs Schritten.","target_audience":"Inhaber und Büroleitungen kleiner Betriebe"}}},"required":["title"]},"example":{"title":"Einführung in die Angebotskalkulation","status":"draft","lang":"de","price":490,"currency":"EUR","is_free":false,"content":{"short_description":"Von der Anfrage zum belastbaren Angebot in sechs Schritten.","target_audience":"Inhaber und Büroleitungen kleiner Betriebe"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"create_course","stufe":"schreiben","data":{"success":true,"data":{"id":4711,"title":"Einführung in die Angebotskalkulation","status":"draft","lang":"de","price":"490","currency":"EUR","is_free":false,"duration":null,"course_type":"internal","company_id":1001,"interview_id":null,"created_at":"2026-11-03T09:15:00+01:00","updated_at":"2026-11-05T14:02:31+01:00","short_description":"Von der Anfrage zum belastbaren Angebot in sechs Schritten.","description":"<p>Du lernst, Aufwände sauber zu schätzen und Angebote nachvollziehbar aufzubauen.</p>","meta_description":"Online-Kurs zur Angebotskalkulation für Handwerksbetriebe.","learnings":"<ul><li>Aufwände schätzen</li><li>Angebote gliedern</li></ul>","target_audience":"Inhaber und Büroleitungen kleiner Betriebe","benefit":"Weniger Rückfragen und verlässlichere Preise","image_prompt":null,"video_prompt":null},"message":"Kurs erstellt","timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/update_course":{"post":{"tags":["Akademie"],"operationId":"academy__update_course","summary":"Kurs aktualisieren","description":"Ändert Felder eines bestehenden Kurses deiner Firma und liefert den Kurs anschließend in der Detailansicht zurück. Im Objekt `data` übergibst du nur die Felder, die sich ändern sollen; nicht genannte Felder bleiben unverändert. Direkt in `data` kannst du title, status, lang, coursetype, accessibility, price, currency, is_free und duration setzen. Alle Inhaltstexte (short_description, description, meta_description, learnings, target_audience, benefit, image_prompt, video_prompt) setzt du verschachtelt unter `data.content`. Ein leeres `data` führt zu einem Fehler („Keine Daten zum Aktualisieren übergeben“). Unbekannte Schlüssel werden still ignoriert.\n\n**Gut zu wissen**\n\n- Inhaltstexte direkt in `data` (z. B. `data.description`) werden ignoriert; sie gehören nach `data.content`.\n- Textwerte (title, status, lang, coursetype, accessibility, currency, duration) werden HTML-maskiert gespeichert.\n- Im Datenmodell hat `coursetype` die Werte internal, external, onboarding, howto und `accessibility` die Werte Public und Privat.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/update_course \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"course_id\": 4711,\n    \"data\": {\n        \"status\": \"publish\",\n        \"price\": 390,\n        \"duration\": \"6 Stunden\",\n        \"content\": {\n            \"benefit\": \"Weniger Rückfragen und verlässlichere Preise\"\n        }\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/update_course\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"course_id\": 4711,\n        \"data\": {\n            \"status\": \"publish\",\n            \"price\": 390,\n            \"duration\": \"6 Stunden\",\n            \"content\": {\n                \"benefit\": \"Weniger Rückfragen und verlässlichere Preise\",\n            },\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/update_course\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"course_id\": 4711,\n      \"data\": {\n          \"status\": \"publish\",\n          \"price\": 390,\n          \"duration\": \"6 Stunden\",\n          \"content\": {\n              \"benefit\": \"Weniger Rückfragen und verlässlichere Preise\"\n          }\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"course_id":{"type":"integer","description":"ID des Kurses (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_courses` oder aus der Antwort von `create_course`. Der Kurs muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kurs nicht gefunden oder kein Zugriff“.","example":4711},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern (Pflicht, nicht leer). Erkannt werden title, status, lang, coursetype, accessibility, price (Zahl), currency, is_free (boolean), duration (Text) sowie `content` als Unterobjekt mit short_description, description, meta_description, learnings, target_audience, benefit, image_prompt und video_prompt.","example":{"status":"publish","price":390,"duration":"6 Stunden","content":{"benefit":"Weniger Rückfragen und verlässlichere Preise"}}}},"required":["course_id","data"]},"example":{"course_id":4711,"data":{"status":"publish","price":390,"duration":"6 Stunden","content":{"benefit":"Weniger Rückfragen und verlässlichere Preise"}}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"update_course","stufe":"schreiben","data":{"success":true,"data":{"id":4711,"title":"Einführung in die Angebotskalkulation","status":"publish","lang":"de","price":"390","currency":"EUR","is_free":false,"duration":"6 Stunden","course_type":"internal","company_id":1001,"interview_id":null,"created_at":"2026-11-03T09:15:00+01:00","updated_at":"2026-11-05T14:02:31+01:00","short_description":"Von der Anfrage zum belastbaren Angebot in sechs Schritten.","description":"<p>Du lernst, Aufwände sauber zu schätzen und Angebote nachvollziehbar aufzubauen.</p>","meta_description":"Online-Kurs zur Angebotskalkulation für Handwerksbetriebe.","learnings":"<ul><li>Aufwände schätzen</li><li>Angebote gliedern</li></ul>","target_audience":"Inhaber und Büroleitungen kleiner Betriebe","benefit":"Weniger Rückfragen und verlässlichere Preise","image_prompt":null,"video_prompt":null},"message":"Kurs aktualisiert","timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/delete_course":{"post":{"tags":["Akademie"],"operationId":"academy__delete_course","summary":"Kurs löschen","description":"Löscht den angegebenen Kurs deiner Firma dauerhaft. Die Antwort enthält `data: null` und die Meldung „Kurs gelöscht“. Die Funktion löscht ausschließlich das Kursobjekt selbst. Die Module und Lektionen des Kurses liegen in eigenen Ordnern und werden im Code nicht mitgelöscht; sie bleiben als verwaiste Objekte bestehen. Da der Zugriff über den Kurs geprüft wird, sind sie danach über die Funktionen dieses Bereichs nicht mehr erreichbar. Wenn du die Struktur sauber entfernen willst, löschst du zuerst die Lektionen (`delete_lesson`), dann die Module (`delete_module`) und zuletzt den Kurs. Die Aktion verlangt `\"bestaetigt\": true`.\n\n**Gut zu wissen**\n\n- Das Löschen kann nicht rückgängig gemacht werden.\n- Module und Lektionen werden nicht automatisch mitgelöscht; lösche sie vorher selbst.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/delete_course \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"course_id\": 4711,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/delete_course\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"course_id\": 4711,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/delete_course\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"course_id\": 4711,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"course_id":{"type":"integer","description":"ID des Kurses (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_courses` oder aus der Antwort von `create_course`. Der Kurs muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kurs nicht gefunden oder kein Zugriff“.","example":4711},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["course_id","bestaetigt"]},"example":{"course_id":4711,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"delete_course","stufe":"loeschen","data":{"success":true,"data":null,"message":"Kurs gelöscht","timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/get_course_modules":{"post":{"tags":["Akademie"],"operationId":"academy__get_course_modules","summary":"Module eines Kurses auflisten","description":"Liefert alle Module des angegebenen Kurses, aufsteigend nach dem Feld `order` sortiert. Die Antwort enthält `items` mit je einem Modul (id, course_id, title, description, order, created_at, updated_at) und `count` mit der Anzahl. Die Lektionen der Module sind in dieser Liste nicht enthalten; sie bekommst du mit `get_module_lessons` oder über `get_module`. Es gibt keine Seiteneinteilung, es werden immer alle Module des Kurses geliefert. Der Kurs muss zu deiner Firma gehören, sonst kommt „Kurs nicht gefunden oder kein Zugriff“.\n\n**Gut zu wissen**\n\n- `order` ist im Datenmodell ein Textfeld und kommt als Text zurück; die Sortierung erfolgt über dieses Feld.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/get_course_modules \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"course_id\": 4711\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/get_course_modules\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"course_id\": 4711,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/get_course_modules\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"course_id\": 4711\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"course_id":{"type":"integer","description":"ID des Kurses (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_courses` oder aus der Antwort von `create_course`. Der Kurs muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kurs nicht gefunden oder kein Zugriff“.","example":4711}},"required":["course_id"]},"example":{"course_id":4711}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"get_course_modules","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":5120,"course_id":4711,"title":"Grundlagen der Kalkulation","description":"Begriffe, Stundensätze und Zuschläge.","order":"1","created_at":"2026-11-03T09:20:00+01:00","updated_at":"2026-11-03T09:20:00+01:00"},{"id":5121,"course_id":4711,"title":"Angebote aufbauen","description":"Gliederung und Positionen.","order":"2","created_at":"2026-11-03T09:20:00+01:00","updated_at":"2026-11-03T09:20:00+01:00"}],"count":2},"message":null,"timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/get_module":{"post":{"tags":["Akademie"],"operationId":"academy__get_module","summary":"Modul im Detail abrufen","description":"Liefert ein einzelnes Modul samt seiner Lektionen. Die Antwort enthält die Modulfelder (id, course_id, title, description, order, created_at, updated_at) und zusätzlich `lessons`: die Lektionen des Moduls, aufsteigend nach `order` sortiert, jeweils in der Kurzform (id, module_id, title, order, created_at, updated_at) ohne description und learnings. Der Zugriff wird über den zugehörigen Kurs geprüft: Existiert das Modul nicht, antwortet die Funktion mit „Modul nicht gefunden“, gehört der Kurs einer anderen Firma (oder existiert nicht mehr), mit „Kein Zugriff auf Modul“.\n\n**Gut zu wissen**\n\n- Die Beschreibung und die Lernziele einer Lektion liefert dieser Bereich nur in der Antwort von `create_lesson` und `update_lesson`; eine Einzelabfrage einer Lektion gibt es nicht.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/get_module \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"module_id\": 5120\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/get_module\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"module_id\": 5120,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/get_module\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"module_id\": 5120\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"module_id":{"type":"integer","description":"ID des Moduls (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_course_modules` oder aus der Antwort von `create_module`. Zugriff besteht nur, wenn der zugehörige Kurs zur Firma deines Schlüssels gehört.","example":5120}},"required":["module_id"]},"example":{"module_id":5120}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"get_module","stufe":"lesen","data":{"success":true,"data":{"id":5120,"course_id":4711,"title":"Grundlagen der Kalkulation","description":"Begriffe, Stundensätze und Zuschläge.","order":"1","created_at":"2026-11-03T09:20:00+01:00","updated_at":"2026-11-03T09:20:00+01:00","lessons":[{"id":6301,"module_id":5120,"title":"Stundensätze richtig ermitteln","order":"1","created_at":"2026-11-03T09:25:00+01:00","updated_at":"2026-11-04T08:10:00+01:00"},{"id":6302,"module_id":5120,"title":"Gemeinkosten zuordnen","order":"2","created_at":"2026-11-03T09:25:00+01:00","updated_at":"2026-11-04T08:10:00+01:00"}]},"message":null,"timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/create_module":{"post":{"tags":["Akademie"],"operationId":"academy__create_module","summary":"Modul anlegen","description":"Legt ein neues Modul in einem Kurs deiner Firma an und liefert es zurück (Modulfelder plus `lessons`, bei einem neuen Modul eine leere Liste). Pflicht sind `course_id` und `title`. Mit `order` bestimmst du die Position im Kurs, mit `description` ergänzt du eine Beschreibung. Das Modul wird als veröffentlichtes Objekt im Ordner /modules abgelegt und über die Kurs-ID mit dem Kurs verknüpft. Ohne `order` bleibt die Position leer, was die Sortierung in `get_course_modules` beeinflusst; vergib deshalb fortlaufende Positionen. Anschließend legst du mit `create_lesson` Lektionen darin an.\n\n**Gut zu wissen**\n\n- Der Titel wird HTML-maskiert gespeichert; die Beschreibung bleibt unverändert.\n- Die Funktion prüft nicht, ob die Position `order` bereits vergeben ist.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/create_module \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"course_id\": 4711,\n    \"title\": \"Grundlagen der Kalkulation\",\n    \"order\": 1,\n    \"description\": \"Begriffe, Stundensätze und Zuschläge.\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/create_module\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"course_id\": 4711,\n        \"title\": \"Grundlagen der Kalkulation\",\n        \"order\": 1,\n        \"description\": \"Begriffe, Stundensätze und Zuschläge.\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/create_module\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"course_id\": 4711,\n      \"title\": \"Grundlagen der Kalkulation\",\n      \"order\": 1,\n      \"description\": \"Begriffe, Stundensätze und Zuschläge.\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"course_id":{"type":"integer","description":"ID des Kurses (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_courses` oder aus der Antwort von `create_course`. Der Kurs muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kurs nicht gefunden oder kein Zugriff“.","example":4711},"title":{"type":"string","description":"Titel des Moduls (Pflicht, nicht leer). Wird getrimmt und HTML-maskiert gespeichert.","example":"Grundlagen der Kalkulation"},"order":{"type":"integer","description":"Optionale Position im Kurs als ganze Zahl, z. B. `1` für das erste Modul. Es wird nicht auf Doppelungen geprüft. Weggelassen bleibt die Position leer.","example":1},"description":{"type":"string","description":"Optionale Beschreibung des Moduls als Text. Wird unverändert gespeichert (kein Maskieren).","example":"Begriffe, Stundensätze und Zuschläge."}},"required":["course_id","title"]},"example":{"course_id":4711,"title":"Grundlagen der Kalkulation","order":1,"description":"Begriffe, Stundensätze und Zuschläge."}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"create_module","stufe":"schreiben","data":{"success":true,"data":{"id":5120,"course_id":4711,"title":"Grundlagen der Kalkulation","description":"Begriffe, Stundensätze und Zuschläge.","order":"1","created_at":"2026-11-03T09:20:00+01:00","updated_at":"2026-11-03T09:20:00+01:00","lessons":[]},"message":"Modul erstellt","timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/update_module":{"post":{"tags":["Akademie"],"operationId":"academy__update_module","summary":"Modul aktualisieren","description":"Ändert Felder eines bestehenden Moduls und liefert es anschließend inklusive seiner Lektionen zurück. In `data` übergibst du nur die Felder, die sich ändern sollen: `title`, `description` und `order` werden ausgewertet, alle anderen Schlüssel ignoriert die Funktion still. Ein leeres `data` führt zu dem Fehler „Keine Daten zum Aktualisieren übergeben“. Der Zugriff wird über den zugehörigen Kurs geprüft. Den Kurs, zu dem das Modul gehört, kannst du hiermit nicht ändern. Mit `order` ordnest du Module neu; die Funktion verschiebt keine anderen Module mit.\n\n**Gut zu wissen**\n\n- Der Titel wird HTML-maskiert gespeichert; die Beschreibung bleibt unverändert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/update_module \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"module_id\": 5120,\n    \"data\": {\n        \"title\": \"Grundlagen der Angebotskalkulation\",\n        \"order\": 2\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/update_module\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"module_id\": 5120,\n        \"data\": {\n            \"title\": \"Grundlagen der Angebotskalkulation\",\n            \"order\": 2,\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/update_module\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"module_id\": 5120,\n      \"data\": {\n          \"title\": \"Grundlagen der Angebotskalkulation\",\n          \"order\": 2\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"module_id":{"type":"integer","description":"ID des Moduls (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_course_modules` oder aus der Antwort von `create_module`. Zugriff besteht nur, wenn der zugehörige Kurs zur Firma deines Schlüssels gehört.","example":5120},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern (Pflicht, nicht leer). Erkannt werden `title` (Text), `description` (Text) und `order` (ganze Zahl). Andere Schlüssel haben keine Wirkung.","example":{"title":"Grundlagen der Angebotskalkulation","order":2}}},"required":["module_id","data"]},"example":{"module_id":5120,"data":{"title":"Grundlagen der Angebotskalkulation","order":2}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"update_module","stufe":"schreiben","data":{"success":true,"data":{"id":5120,"course_id":4711,"title":"Grundlagen der Angebotskalkulation","description":"Begriffe, Stundensätze und Zuschläge.","order":"2","created_at":"2026-11-03T09:20:00+01:00","updated_at":"2026-11-03T09:20:00+01:00","lessons":[{"id":6301,"module_id":5120,"title":"Stundensätze richtig ermitteln","order":"1","created_at":"2026-11-03T09:25:00+01:00","updated_at":"2026-11-04T08:10:00+01:00"}]},"message":"Modul aktualisiert","timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/delete_module":{"post":{"tags":["Akademie"],"operationId":"academy__delete_module","summary":"Modul löschen","description":"Löscht das angegebene Modul dauerhaft. Die Antwort enthält `data: null` und die Meldung „Modul gelöscht“. Der Zugriff wird über den zugehörigen Kurs geprüft. Gelöscht wird nur das Modulobjekt; seine Lektionen liegen in einem eigenen Ordner und werden im Code nicht mitgelöscht. Sie bleiben als verwaiste Objekte zurück und sind über die Funktionen dieses Bereichs nicht mehr erreichbar, weil sie nur über ihr Modul gefunden werden. Willst du die Inhalte sauber entfernen, löschst du zuerst die Lektionen mit `delete_lesson`. Die Aktion verlangt `\"bestaetigt\": true`.\n\n**Gut zu wissen**\n\n- Das Löschen kann nicht rückgängig gemacht werden.\n- Lektionen des Moduls werden nicht automatisch mitgelöscht.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/delete_module \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"module_id\": 5120,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/delete_module\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"module_id\": 5120,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/delete_module\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"module_id\": 5120,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"module_id":{"type":"integer","description":"ID des Moduls (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_course_modules` oder aus der Antwort von `create_module`. Zugriff besteht nur, wenn der zugehörige Kurs zur Firma deines Schlüssels gehört.","example":5120},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["module_id","bestaetigt"]},"example":{"module_id":5120,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"delete_module","stufe":"loeschen","data":{"success":true,"data":null,"message":"Modul gelöscht","timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/get_module_lessons":{"post":{"tags":["Akademie"],"operationId":"academy__get_module_lessons","summary":"Lektionen eines Moduls auflisten","description":"Liefert alle Lektionen des angegebenen Moduls, aufsteigend nach `order` sortiert. Die Antwort enthält `items` mit je einer Lektion in der Kurzform (id, module_id, title, order, created_at, updated_at) und `count` mit der Anzahl. Beschreibung und Lernziele der Lektionen sind in dieser Liste nicht enthalten. Es gibt keine Seiteneinteilung; es werden immer alle Lektionen des Moduls geliefert. Der Zugriff wird über Modul und Kurs geprüft: ein unbekanntes Modul ergibt „Modul nicht gefunden“, ein Modul eines fremden Kurses „Kein Zugriff auf Modul“.\n\n**Gut zu wissen**\n\n- `order` ist im Datenmodell ein Textfeld und kommt als Text zurück.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/get_module_lessons \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"module_id\": 5120\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/get_module_lessons\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"module_id\": 5120,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/get_module_lessons\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"module_id\": 5120\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"module_id":{"type":"integer","description":"ID des Moduls (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_course_modules` oder aus der Antwort von `create_module`. Zugriff besteht nur, wenn der zugehörige Kurs zur Firma deines Schlüssels gehört.","example":5120}},"required":["module_id"]},"example":{"module_id":5120}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"get_module_lessons","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":6301,"module_id":5120,"title":"Stundensätze richtig ermitteln","order":"1","created_at":"2026-11-03T09:25:00+01:00","updated_at":"2026-11-04T08:10:00+01:00"},{"id":6302,"module_id":5120,"title":"Gemeinkosten zuordnen","order":"2","created_at":"2026-11-03T09:25:00+01:00","updated_at":"2026-11-04T08:10:00+01:00"}],"count":2},"message":null,"timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/create_lesson":{"post":{"tags":["Akademie"],"operationId":"academy__create_lesson","summary":"Lektion anlegen","description":"Legt eine neue Lektion in einem Modul an und liefert sie in der Detailansicht zurück (id, module_id, title, order, created_at, updated_at sowie description und learnings). Pflicht sind `module_id` und `title`. Optional setzt du Position, Beschreibung und Lernziele. Die Lektion wird als veröffentlichtes Objekt im Ordner /lessons abgelegt und über die Modul-ID verknüpft. Der Zugriff wird über Modul und Kurs deiner Firma geprüft. Weitere Felder der Lektion (etwa Inhalt oder Video) kann diese Funktion nicht setzen. Ohne `order` bleibt die Position leer; vergib fortlaufende Werte, damit die Reihenfolge in `get_module_lessons` stimmt.\n\n**Gut zu wissen**\n\n- Der Titel wird HTML-maskiert gespeichert; Beschreibung und Lernziele bleiben unverändert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/create_lesson \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"module_id\": 5120,\n    \"title\": \"Stundensätze richtig ermitteln\",\n    \"order\": 1,\n    \"description\": \"Wie du aus Lohn- und Gemeinkosten einen kostendeckenden Stundensatz ableitest.\",\n    \"learnings\": \"Stundensatz herleiten, Gemeinkosten zuordnen\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/create_lesson\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"module_id\": 5120,\n        \"title\": \"Stundensätze richtig ermitteln\",\n        \"order\": 1,\n        \"description\": \"Wie du aus Lohn- und Gemeinkosten einen kostendeckenden Stundensatz ableitest.\",\n        \"learnings\": \"Stundensatz herleiten, Gemeinkosten zuordnen\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/create_lesson\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"module_id\": 5120,\n      \"title\": \"Stundensätze richtig ermitteln\",\n      \"order\": 1,\n      \"description\": \"Wie du aus Lohn- und Gemeinkosten einen kostendeckenden Stundensatz ableitest.\",\n      \"learnings\": \"Stundensatz herleiten, Gemeinkosten zuordnen\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"module_id":{"type":"integer","description":"ID des Moduls (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_course_modules` oder aus der Antwort von `create_module`. Zugriff besteht nur, wenn der zugehörige Kurs zur Firma deines Schlüssels gehört.","example":5120},"title":{"type":"string","description":"Titel der Lektion (Pflicht, nicht leer). Wird getrimmt und HTML-maskiert gespeichert.","example":"Stundensätze richtig ermitteln"},"order":{"type":"integer","description":"Optionale Position innerhalb des Moduls als ganze Zahl, z. B. `1`. Doppelte Positionen werden nicht geprüft. Weggelassen bleibt die Position leer.","example":1},"description":{"type":"string","description":"Optionale Beschreibung der Lektion als Text. Wird unverändert gespeichert (kein Maskieren).","example":"Wie du aus Lohn- und Gemeinkosten einen kostendeckenden Stundensatz ableitest."},"learnings":{"type":"string","description":"Optionale Lernziele der Lektion als Text (im Datenmodell ein Formatierungsfeld, HTML ist möglich). Wird unverändert gespeichert.","example":"Stundensatz herleiten, Gemeinkosten zuordnen"}},"required":["module_id","title"]},"example":{"module_id":5120,"title":"Stundensätze richtig ermitteln","order":1,"description":"Wie du aus Lohn- und Gemeinkosten einen kostendeckenden Stundensatz ableitest.","learnings":"Stundensatz herleiten, Gemeinkosten zuordnen"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"create_lesson","stufe":"schreiben","data":{"success":true,"data":{"id":6301,"module_id":5120,"title":"Stundensätze richtig ermitteln","order":"1","created_at":"2026-11-03T09:25:00+01:00","updated_at":"2026-11-04T08:10:00+01:00","description":"Wie du aus Lohn- und Gemeinkosten einen kostendeckenden Stundensatz ableitest.","learnings":"Stundensatz herleiten, Gemeinkosten zuordnen"},"message":"Lektion erstellt","timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/update_lesson":{"post":{"tags":["Akademie"],"operationId":"academy__update_lesson","summary":"Lektion aktualisieren","description":"Ändert Felder einer bestehenden Lektion und liefert sie anschließend in der Detailansicht zurück (inklusive description und learnings). In `data` übergibst du nur die Felder, die sich ändern sollen; ausgewertet werden `title`, `description`, `order` und `learnings`, alle anderen Schlüssel werden still ignoriert. Ein leeres `data` führt zu dem Fehler „Keine Daten zum Aktualisieren übergeben“. Der Zugriff wird über Modul und Kurs geprüft: Existiert die Lektion oder ihr Modul nicht, kommt „Lektion nicht gefunden“ bzw. „Zugehöriges Modul nicht gefunden“, bei fremdem Kurs „Kein Zugriff auf Lektion“. Die Zuordnung zu einem anderen Modul ist hierüber nicht möglich.\n\n**Gut zu wissen**\n\n- Das ist der einzige Weg neben `create_lesson`, die Beschreibung und die Lernziele einer Lektion zu sehen: Sie stehen in der Antwort.\n- Der Titel wird HTML-maskiert gespeichert; Beschreibung und Lernziele bleiben unverändert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/update_lesson \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"lesson_id\": 6301,\n    \"data\": {\n        \"learnings\": \"Stundensatz herleiten, Gemeinkosten zuordnen, Zuschläge prüfen\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/update_lesson\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"lesson_id\": 6301,\n        \"data\": {\n            \"learnings\": \"Stundensatz herleiten, Gemeinkosten zuordnen, Zuschläge prüfen\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/update_lesson\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"lesson_id\": 6301,\n      \"data\": {\n          \"learnings\": \"Stundensatz herleiten, Gemeinkosten zuordnen, Zuschläge prüfen\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lesson_id":{"type":"integer","description":"ID der Lektion (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_module_lessons` oder `get_module` (Feld lessons) bzw. aus der Antwort von `create_lesson`. Zugriff besteht nur, wenn Modul und Kurs zur Firma deines Schlüssels gehören.","example":6301},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern (Pflicht, nicht leer). Erkannt werden `title` (Text), `description` (Text), `order` (ganze Zahl) und `learnings` (Text). Andere Schlüssel haben keine Wirkung.","example":{"learnings":"Stundensatz herleiten, Gemeinkosten zuordnen, Zuschläge prüfen"}}},"required":["lesson_id","data"]},"example":{"lesson_id":6301,"data":{"learnings":"Stundensatz herleiten, Gemeinkosten zuordnen, Zuschläge prüfen"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"update_lesson","stufe":"schreiben","data":{"success":true,"data":{"id":6301,"module_id":5120,"title":"Stundensätze richtig ermitteln","order":"1","created_at":"2026-11-03T09:25:00+01:00","updated_at":"2026-11-04T08:10:00+01:00","description":"Wie du aus Lohn- und Gemeinkosten einen kostendeckenden Stundensatz ableitest.","learnings":"Stundensatz herleiten, Gemeinkosten zuordnen, Zuschläge prüfen"},"message":"Lektion aktualisiert","timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/delete_lesson":{"post":{"tags":["Akademie"],"operationId":"academy__delete_lesson","summary":"Lektion löschen","description":"Löscht die angegebene Lektion dauerhaft. Die Antwort enthält `data: null` und die Meldung „Lektion gelöscht“. Der Zugriff wird über Modul und Kurs deiner Firma geprüft, bevor gelöscht wird. Andere Lektionen des Moduls werden nicht angepasst; ihre Positionen (`order`) bleiben unverändert, es entsteht also gegebenenfalls eine Lücke in der Nummerierung. Die Aktion verlangt `\"bestaetigt\": true`. Wenn du ein ganzes Modul oder einen Kurs entfernen willst, löschst du zuerst alle Lektionen und danach Modul und Kurs.\n\n**Gut zu wissen**\n\n- Das Löschen kann nicht rückgängig gemacht werden.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/delete_lesson \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"lesson_id\": 6301,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/delete_lesson\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"lesson_id\": 6301,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/delete_lesson\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"lesson_id\": 6301,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lesson_id":{"type":"integer","description":"ID der Lektion (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_module_lessons` oder `get_module` (Feld lessons) bzw. aus der Antwort von `create_lesson`. Zugriff besteht nur, wenn Modul und Kurs zur Firma deines Schlüssels gehören.","example":6301},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["lesson_id","bestaetigt"]},"example":{"lesson_id":6301,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"delete_lesson","stufe":"loeschen","data":{"success":true,"data":null,"message":"Lektion gelöscht","timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/summarize_course":{"post":{"tags":["Akademie"],"operationId":"academy__summarize_course","summary":"Kurs zusammenfassen","description":"Liefert eine strukturierte Übersicht eines Kurses deiner Firma, ohne etwas zu verändern und ohne einen KI-Aufruf. Die Antwort enthält zwei Teile: `course` mit der vollen Detailansicht des Kurses (wie `get_course`) und `summary` mit den wichtigsten Kenndaten: title, status, lang, type (der Kurstyp), is_free, price, currency, duration, learnings, target_audience und benefit. Module und Lektionen sind nicht enthalten; die Struktur holst du mit `get_course_modules` und `get_module`. Die Funktion ist im Katalog als schreibende Aktion eingestuft, verändert aber keine Daten.\n\n**Gut zu wissen**\n\n- Trotz Einstufung als „schreiben“ ist die Funktion rein lesend.\n- `summary.type` entspricht `course.course_type`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/summarize_course \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"course_id\": 4711\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/summarize_course\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"course_id\": 4711,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/summarize_course\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"course_id\": 4711\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"course_id":{"type":"integer","description":"ID des Kurses (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_courses` oder aus der Antwort von `create_course`. Der Kurs muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kurs nicht gefunden oder kein Zugriff“.","example":4711}},"required":["course_id"]},"example":{"course_id":4711}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"summarize_course","stufe":"lesen","data":{"success":true,"data":{"course":{"id":4711,"title":"Einführung in die Angebotskalkulation","status":"draft","lang":"de","price":"490","currency":"EUR","is_free":false,"duration":"6 Stunden","course_type":"internal","company_id":1001,"interview_id":null,"created_at":"2026-11-03T09:15:00+01:00","updated_at":"2026-11-05T14:02:31+01:00","short_description":"Von der Anfrage zum belastbaren Angebot in sechs Schritten.","description":"<p>Du lernst, Aufwände sauber zu schätzen und Angebote nachvollziehbar aufzubauen.</p>","meta_description":"Online-Kurs zur Angebotskalkulation für Handwerksbetriebe.","learnings":"<ul><li>Aufwände schätzen</li><li>Angebote gliedern</li></ul>","target_audience":"Inhaber und Büroleitungen kleiner Betriebe","benefit":"Weniger Rückfragen und verlässlichere Preise","image_prompt":null,"video_prompt":null},"summary":{"title":"Einführung in die Angebotskalkulation","status":"draft","lang":"de","type":"internal","is_free":false,"price":"490","currency":"EUR","duration":"6 Stunden","learnings":"<ul><li>Aufwände schätzen</li><li>Angebote gliedern</li></ul>","target_audience":"Inhaber und Büroleitungen kleiner Betriebe","benefit":"Weniger Rückfragen und verlässlichere Preise"}},"message":null,"timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/academy/list_course_statuses":{"post":{"tags":["Akademie"],"operationId":"academy__list_course_statuses","summary":"Bekannte Kursstatus auflisten","description":"Liefert eine feste Liste von Statuswerten für Kurse: `draft`, `review`, `published` und `archived`. Die Liste ist im Code hinterlegt und wird nicht aus deinen Daten gebildet; die Funktion braucht keine Parameter und greift nicht auf Kurse zu. Sie dient als Orientierung für den Filter `status` in `get_courses` und für das Feld `status` bei `create_course` bzw. `update_course`. Beachte, dass die Funktionen den Status nicht gegen diese Liste prüfen: Sie speichern den übergebenen Text, und das Auswahlfeld im Datenmodell des Kurses kennt die Werte `draft` und `publish`.\n\n**Gut zu wissen**\n\n- Die Liste weicht vom Datenmodell ab: dort heißen die Werte `draft` und `publish`, nicht `published`. Prüfe vorhandene Kurse, bevor du nach einem Status filterst.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/academy/list_course_statuses \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/academy/list_course_statuses\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/academy/list_course_statuses\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"academy","werkzeug":"list_course_statuses","stufe":"lesen","data":{"success":true,"data":{"statuses":["draft","review","published","archived"]},"message":null,"timestamp":"2026-11-09 10:42:17","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/get_campaigns":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__get_campaigns","summary":"Kampagnen auflisten","description":"Liefert die Werbekampagnen deiner Firma, neueste zuerst (Sortierung nach Anlagedatum absteigend). Die Antwort enthält `items` mit je einer Kampagne (id, name, description, provider, campaign_type, content, is_active, keyword_id, template_id, document_id, budget, channels, company_id, created_at, updated_at), außerdem `total` (Gesamtzahl der Treffer ohne Seitenbegrenzung), `limit` und `offset`. Du kannst nach Aktivstatus und nach Provider filtern. Standardmäßig kommen 20 Kampagnen; zum Blättern erhöhst du `offset` um `limit`. Anzeigengruppen und Anzeigen sind nicht Teil der Liste; sie hängen an der Kampagne und werden mit `create_google_search_campaign` miterzeugt.\n\n**Gut zu wissen**\n\n- Ohne `limit` werden 20 Kampagnen geliefert; eine Obergrenze gibt es im Code nicht, der Mindestwert ist 1.\n- `status` kennt nur `active` und `inactive`; jeder andere Wert wird ohne Fehlermeldung ignoriert und liefert alle Kampagnen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/get_campaigns \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"active\",\n    \"provider\": \"google\",\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/get_campaigns\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"active\",\n        \"provider\": \"google\",\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/get_campaigns\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"active\",\n      \"provider\": \"google\",\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Optionaler Filter auf den Aktivstatus: `active` (nur aktive) oder `inactive` (nur inaktive); Groß-/Kleinschreibung egal. Andere Werte und leer: kein Statusfilter.","example":"active"},"provider":{"type":"string","description":"Optionaler Filter auf den Provider, exakte Übereinstimmung, z. B. `google`. Im Datenmodell sind google, facebook, instagram, tiktok, linkedin und x vorgesehen. Leer oder weggelassen: alle Provider.","example":"google"},"limit":{"type":"integer","description":"Maximale Anzahl Kampagnen pro Antwort, ganze Zahl. Standard 20, Mindestwert 1 (kleinere Werte werden auf 1 angehoben). Eine feste Obergrenze gibt es nicht.","example":10},"offset":{"type":"integer","description":"Anzahl Kampagnen, die übersprungen werden, ganze Zahl ab 0 (Standard 0, negative Werte werden zu 0). Zum Blättern setzt du `offset` auf das Vielfache von `limit`."}}},"example":{"status":"active","provider":"google","limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"get_campaigns","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":8201,"name":"Herbstaktion Heizungswartung","description":null,"provider":"google","campaign_type":"search","content":"","is_active":true,"keyword_id":"3105","template_id":"2210","document_id":"9087","budget":"25","channels":"ppc","company_id":"1001","created_at":"2026-11-04T10:12:00+01:00","updated_at":"2026-11-06T16:40:12+01:00"}],"total":1,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/get_campaign":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__get_campaign","summary":"Kampagne im Detail abrufen","description":"Liefert eine einzelne Kampagne deiner Firma mit allen Feldern: id, name, description, provider, campaign_type, content, is_active, keyword_id, template_id, document_id, budget, channels, company_id, created_at und updated_at. Bei Kampagnen aus `create_google_search_campaign` ist `content` leer, weil die Anzeigentexte in den zugehörigen Anzeigengruppen und Anzeigen liegen; bei manuell angelegten oder älteren Kampagnen steht dort der gespeicherte Inhalt. Existiert die ID nicht oder gehört die Kampagne einer anderen Firma, kommt `success: false` mit „Kampagne nicht gefunden oder kein Zugriff“. Zeitstempel sind ISO-8601-Zeichenketten.\n\n**Gut zu wissen**\n\n- keyword_id, template_id, document_id, budget und company_id sind im Datenmodell Textfelder und kommen als Text zurück.\n- Nicht gesetzte Felder erscheinen als `null`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/get_campaign \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"campaign_id\": 8201\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/get_campaign\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"campaign_id\": 8201,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/get_campaign\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"campaign_id\": 8201\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"type":"integer","description":"ID der Kampagne (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_campaigns` oder aus der Antwort von `create_campaign`. Die Kampagne muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kampagne nicht gefunden oder kein Zugriff“.","example":8201}},"required":["campaign_id"]},"example":{"campaign_id":8201}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"get_campaign","stufe":"lesen","data":{"success":true,"data":{"id":8201,"name":"Herbstaktion Heizungswartung","description":null,"provider":"google","campaign_type":"search","content":"","is_active":true,"keyword_id":"3105","template_id":"2210","document_id":"9087","budget":"25","channels":"ppc","company_id":"1001","created_at":"2026-11-04T10:12:00+01:00","updated_at":"2026-11-06T16:40:12+01:00"},"message":null,"timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/create_campaign":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__create_campaign","summary":"Kampagne manuell anlegen","description":"Legt eine neue Kampagne für deine Firma an und liefert sie zurück (alle Felder wie bei `get_campaign`, inklusive der neuen `id`). Pflicht ist nur `name`. Ohne weitere Angaben entsteht eine aktive Kampagne mit Provider und Typ `custom` und leerem Inhalt. Anzeigengruppen oder Anzeigen werden dabei nicht erzeugt; die Funktion legt nur das Kampagnenobjekt an. Wenn du eine fertige Suchkampagne mit KI-generierten Anzeigentexten möchtest, nutzt du stattdessen `create_google_search_campaign`. Verknüpfungen zu Keyword, Vorlage und Dokument sind optional und werden als IDs gespeichert, ohne dass geprüft wird, ob die Objekte existieren oder dir gehören.\n\n**Gut zu wissen**\n\n- Die Kampagne ist standardmäßig aktiv (`is_active` ist `true`); setze `is_active: false`, wenn sie zunächst ruhen soll.\n- Der interne Schlüssel entsteht aus der Uhrzeit in Sekunden; zwei Aufrufe in derselben Sekunde können am gleichen Schlüssel scheitern.\n- Der Name wird HTML-maskiert gespeichert; `content` und `channels` bleiben unverändert.\n- Der Standardwert `custom` für provider und campaign_type steht nicht in den Auswahllisten des Datenmodells (provider: google, facebook, instagram, tiktok, linkedin, x; Typ: image, video, lead, text, search).\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/create_campaign \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Herbstaktion Heizungswartung\",\n    \"provider\": \"google\",\n    \"campaign_type\": \"search\",\n    \"budget\": 25,\n    \"channels\": \"ppc\",\n    \"is_active\": false\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/create_campaign\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"name\": \"Herbstaktion Heizungswartung\",\n        \"provider\": \"google\",\n        \"campaign_type\": \"search\",\n        \"budget\": 25,\n        \"channels\": \"ppc\",\n        \"is_active\": False,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/create_campaign\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"name\": \"Herbstaktion Heizungswartung\",\n      \"provider\": \"google\",\n      \"campaign_type\": \"search\",\n      \"budget\": 25,\n      \"channels\": \"ppc\",\n      \"is_active\": false\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name der Kampagne (Pflicht, nicht leer). Wird getrimmt und HTML-maskiert gespeichert.","example":"Herbstaktion Heizungswartung"},"provider":{"type":"string","description":"Optionaler Provider als Text, z. B. `google`. Standard `custom`. Im Datenmodell vorgesehen sind google, facebook, instagram, tiktok, linkedin und x; es wird nicht gegen diese Liste geprüft.","example":"google"},"campaign_type":{"type":"string","description":"Optionaler Kampagnentyp als Text, z. B. `search`. Standard `custom`. Im Datenmodell vorgesehen sind image, video, lead, text und search; es wird nicht gegen diese Liste geprüft.","example":"search"},"content":{"type":"string","description":"Optionaler Kampagneninhalt als Text (z. B. Anzeigentext oder JSON als Zeichenkette). Wird unverändert gespeichert. Standard: leere Zeichenkette."},"keyword_id":{"type":"integer","description":"Optionale ID eines Keyword-Objekts als ganze Zahl. Sie wird nur gespeichert; es erfolgt keine Prüfung, ob das Keyword existiert oder zu deiner Firma gehört."},"template_id":{"type":"integer","description":"Optionale ID einer Landingpage-Vorlage als ganze Zahl. Sie wird nur gespeichert, ohne Existenz- oder Zugehörigkeitsprüfung."},"document_id":{"type":"integer","description":"Optionale ID des verknüpften Dokuments (Landingpage) als ganze Zahl. Sie wird nur gespeichert, ohne Existenz- oder Zugehörigkeitsprüfung."},"budget":{"type":"number","description":"Optionales Budget als Zahl, z. B. `25` oder `49.9`. Die Funktion gibt die Zahl ohne Währungs- oder Zeitraumangabe weiter; die Bedeutung (z. B. Tagesbudget) legst du selbst fest.","example":25},"channels":{"type":"string","description":"Optionale Kanalangabe als Text. Im Datenmodell ist ein Auswahlfeld mit den Werten `ppc`, `socialmedia` und `email` vorgesehen; der Wert wird nicht gegen diese Liste geprüft.","example":"ppc"},"is_active":{"type":"boolean","description":"Aktivstatus der neuen Kampagne, `true` oder `false`. Standard `true`. Wird nur ausgewertet, wenn der Schlüssel im Körper steht.","default":true,"example":false}},"required":["name"]},"example":{"name":"Herbstaktion Heizungswartung","provider":"google","campaign_type":"search","budget":25,"channels":"ppc","is_active":false}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"create_campaign","stufe":"schreiben","data":{"success":true,"data":{"id":8201,"name":"Herbstaktion Heizungswartung","description":null,"provider":"google","campaign_type":"search","content":"","is_active":false,"keyword_id":null,"template_id":null,"document_id":null,"budget":25,"channels":"ppc","company_id":"1001","created_at":"2026-11-04T10:12:00+01:00","updated_at":"2026-11-06T16:40:12+01:00"},"message":"Kampagne erstellt","timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/update_campaign":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__update_campaign","summary":"Kampagne aktualisieren","description":"Ändert Felder einer bestehenden Kampagne und liefert sie anschließend vollständig zurück. In `data` übergibst du nur die Felder, die sich ändern sollen. Ausgewertet werden name, provider, campaign_type, content, is_active, keyword_id, template_id, document_id, budget, channels und description; andere Schlüssel werden still ignoriert. Felder mit dem Wert `null` zählen als nicht übergeben und lassen sich daher nicht leeren. Ein leeres `data` erzeugt keinen Fehler: die Kampagne wird unverändert gespeichert und zurückgegeben. Die Funktion ändert nur das Kampagnenobjekt, nicht die Anzeigengruppen oder Anzeigen darunter. Zum Ein- und Ausschalten nutzt du alternativ `activate_campaign` und `pause_campaign`.\n\n**Gut zu wissen**\n\n- `is_active: false` in `data` funktioniert; nur `null` wird übersprungen.\n- name, provider, campaign_type werden HTML-maskiert gespeichert; content, budget, channels und description bleiben unverändert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/update_campaign \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"campaign_id\": 8201,\n    \"data\": {\n        \"name\": \"Herbstaktion Heizungswartung 2026\",\n        \"budget\": 40,\n        \"description\": \"Fokus auf Wartungsverträge\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/update_campaign\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"campaign_id\": 8201,\n        \"data\": {\n            \"name\": \"Herbstaktion Heizungswartung 2026\",\n            \"budget\": 40,\n            \"description\": \"Fokus auf Wartungsverträge\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/update_campaign\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"campaign_id\": 8201,\n      \"data\": {\n          \"name\": \"Herbstaktion Heizungswartung 2026\",\n          \"budget\": 40,\n          \"description\": \"Fokus auf Wartungsverträge\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"type":"integer","description":"ID der Kampagne (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_campaigns` oder aus der Antwort von `create_campaign`. Die Kampagne muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kampagne nicht gefunden oder kein Zugriff“.","example":8201},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern (Pflicht). Erkannt werden `name`, `provider`, `campaign_type`, `content` (Text), `is_active` (boolean), `keyword_id`, `template_id`, `document_id` (ganze Zahlen), `budget` (Zahl), `channels` (Text) und `description` (Text). Andere Schlüssel haben keine Wirkung.","example":{"name":"Herbstaktion Heizungswartung 2026","budget":40,"description":"Fokus auf Wartungsverträge"}}},"required":["campaign_id","data"]},"example":{"campaign_id":8201,"data":{"name":"Herbstaktion Heizungswartung 2026","budget":40,"description":"Fokus auf Wartungsverträge"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"update_campaign","stufe":"schreiben","data":{"success":true,"data":{"id":8201,"name":"Herbstaktion Heizungswartung 2026","description":"Fokus auf Wartungsverträge","provider":"google","campaign_type":"search","content":"","is_active":true,"keyword_id":"3105","template_id":"2210","document_id":"9087","budget":40,"channels":"ppc","company_id":"1001","created_at":"2026-11-04T10:12:00+01:00","updated_at":"2026-11-06T16:40:12+01:00"},"message":"Kampagne aktualisiert","timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/delete_campaign":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__delete_campaign","summary":"Kampagne löschen","description":"Löscht die angegebene Kampagne deiner Firma dauerhaft. Die Antwort enthält in `data` nur die `campaign_id` der gelöschten Kampagne und die Meldung „Kampagne gelöscht“. Gelöscht wird das Kampagnenobjekt; im Code gibt es keinen Schritt, der zugehörige Anzeigengruppen oder Anzeigen mitlöscht. Die Aktion verlangt `\"bestaetigt\": true`. Wenn du die Kampagne nur anhalten willst, nutze `pause_campaign`, wenn du sie als Vorlage für eine neue verwenden willst, `copy_campaign`.\n\n**Gut zu wissen**\n\n- Das Löschen kann nicht rückgängig gemacht werden.\n- Zum bloßen Stoppen genügt `pause_campaign`.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/delete_campaign \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"campaign_id\": 8201,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/delete_campaign\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"campaign_id\": 8201,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/delete_campaign\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"campaign_id\": 8201,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"type":"integer","description":"ID der Kampagne (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_campaigns` oder aus der Antwort von `create_campaign`. Die Kampagne muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kampagne nicht gefunden oder kein Zugriff“.","example":8201},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["campaign_id","bestaetigt"]},"example":{"campaign_id":8201,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"delete_campaign","stufe":"loeschen","data":{"success":true,"data":{"campaign_id":8201},"message":"Kampagne gelöscht","timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/copy_campaign":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__copy_campaign","summary":"Kampagne kopieren","description":"Legt eine Kopie einer bestehenden Kampagne deiner Firma an und liefert die Kopie zurück. Übernommen werden Beschreibung, Provider, Typ, Inhalt, Keyword-, Vorlagen- und Dokument-ID, Budget und Kanäle. Anzeigengruppen und Anzeigen werden nicht mitkopiert, ebenso wenig die Zuordnung zu Anzeigengruppen; eine Kopie einer Suchkampagne mit Anzeigengruppen enthält deshalb nur das Kampagnenobjekt. Ohne `name` heißt die Kopie „<Originalname> (Kopie)“. Ohne `is_active` übernimmt die Kopie den Aktivstatus des Originals. Die Kopie wird im selben Ordner wie das Original angelegt.\n\n**Gut zu wissen**\n\n- Eine Kopie einer aktiven Kampagne ist ohne `is_active: false` ebenfalls aktiv.\n- Der Name wird HTML-maskiert gespeichert.\n- Zwei Kopien in derselben Sekunde mit gleichem Namen können am gleichen internen Schlüssel scheitern.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/copy_campaign \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"campaign_id\": 8201,\n    \"name\": \"Frühjahrsaktion Heizungswartung\",\n    \"is_active\": false\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/copy_campaign\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"campaign_id\": 8201,\n        \"name\": \"Frühjahrsaktion Heizungswartung\",\n        \"is_active\": False,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/copy_campaign\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"campaign_id\": 8201,\n      \"name\": \"Frühjahrsaktion Heizungswartung\",\n      \"is_active\": false\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"type":"integer","description":"ID der Kampagne (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_campaigns` oder aus der Antwort von `create_campaign`. Die Kampagne muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kampagne nicht gefunden oder kein Zugriff“.","example":8201},"name":{"type":"string","description":"Optionaler Name der Kopie. Weggelassen heißt sie „<Name der Quelle> (Kopie)“. Wird getrimmt und HTML-maskiert gespeichert.","example":"Frühjahrsaktion Heizungswartung"},"is_active":{"type":"boolean","description":"Optionaler Aktivstatus der Kopie, `true` oder `false`. Weggelassen übernimmt die Kopie den Aktivstatus der Quellkampagne.","example":false}},"required":["campaign_id"]},"example":{"campaign_id":8201,"name":"Frühjahrsaktion Heizungswartung","is_active":false}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"copy_campaign","stufe":"schreiben","data":{"success":true,"data":{"id":8202,"name":"Frühjahrsaktion Heizungswartung","description":null,"provider":"google","campaign_type":"search","content":"","is_active":false,"keyword_id":"3105","template_id":"2210","document_id":"9087","budget":"25","channels":"ppc","company_id":"1001","created_at":"2026-11-04T10:12:00+01:00","updated_at":"2026-11-06T16:40:12+01:00"},"message":"Kampagne kopiert","timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/create_google_search_campaign":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__create_google_search_campaign","summary":"Google-Suchkampagne mit KI-Texten anlegen","description":"Erzeugt aus Vorlage, Keyword und Dokument eine komplette Suchkampagne: Zuerst lässt die Funktion aus Keyword und Firmenprofil KI-Anzeigentexte (deutsch und englisch) erzeugen; der Aufruf dauert daher länger als andere Funktionen. Danach entsteht eine Kampagne mit Name „<Domain der Firma> - <Keyword>“, Provider `google`, Typ `search`, aktiv, mit einer Anzeigengruppe (Gebotsstrategie manuelles CPC, Gebot 0,50, Keyword mit Übereinstimmung EXACT) und einer responsiven Suchanzeige im Status `pending`. Überschriften (bis 9, je max. 30 Zeichen), Beschreibungen (bis 4, je max. 90 Zeichen) und Pfade (je max. 15 Zeichen) kommen aus dem deutschen Teil der KI-Antwort. Zurück kommt die Kampagne (Felder wie `get_campaign`); `content` ist leer, weil die Texte in der Anzeige liegen. Kann die zuletzt angelegte passende Kampagne nicht gefunden werden, ist `data` `null`.\n\n**Gut zu wissen**\n\n- Die Kampagne wird sofort mit `is_active: true` angelegt. Die Ziel-URL der Anzeige bleibt leer (finalUrl/displayUrl).\n- Liefert die KI kein verwertbares Ergebnis, bricht die Funktion mit „Invalid AI content format“ ab; die Kampagne ist zu diesem Zeitpunkt bereits angelegt, aber ohne Anzeigengruppe und Anzeige.\n- Keyword-, Vorlagen- und Dokument-ID werden nicht auf Zugehörigkeit zu deiner Firma geprüft.\n- Die Rückgabe sucht die Kampagne per Firma, Vorlage und Keyword; bei mehreren gleichartigen Kampagnen ist es die jüngste.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/create_google_search_campaign \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": 2210,\n    \"keyword_id\": 3105,\n    \"document_id\": 9087\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/create_google_search_campaign\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"template_id\": 2210,\n        \"keyword_id\": 3105,\n        \"document_id\": 9087,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/create_google_search_campaign\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"template_id\": 2210,\n      \"keyword_id\": 3105,\n      \"document_id\": 9087\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"integer","description":"ID der Landingpage-Vorlage (ganze Zahl, Pflicht). Sie wird an der Kampagne gespeichert und dient zusammen mit dem Keyword zum Wiederfinden der angelegten Kampagne. Es wird nicht geprüft, ob die Vorlage existiert.","example":2210},"keyword_id":{"type":"integer","description":"ID des Keyword-Objekts (ganze Zahl, Pflicht). Aus dem Keyword (Suchbegriff, Intention, Schmerz, Lösung) entstehen die Anzeigentexte; der Suchbegriff wird als Keyword der Anzeigengruppe eingetragen.","example":3105},"document_id":{"type":"integer","description":"ID des verknüpften Dokuments bzw. der Landingpage (ganze Zahl, Pflicht). Sie wird an der Kampagne gespeichert; die Ziel-URL der Anzeige wird dadurch nicht gesetzt.","example":9087}},"required":["template_id","keyword_id","document_id"]},"example":{"template_id":2210,"keyword_id":3105,"document_id":9087}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"create_google_search_campaign","stufe":"schreiben","data":{"success":true,"data":{"id":8203,"name":"musterbetrieb-heizung.example.com - heizungswartung kosten","description":null,"provider":"google","campaign_type":"search","content":"","is_active":true,"keyword_id":"3105","template_id":"2210","document_id":"9087","budget":null,"channels":null,"company_id":"1001","created_at":"2026-11-04T10:12:00+01:00","updated_at":"2026-11-06T16:40:12+01:00"},"message":"Google Search Kampagne erstellt","timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/update_google_search_campaign":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__update_google_search_campaign","summary":"Google-Suchkampagne neu texten","description":"Erzeugt für eine bestehende Suchkampagne neue KI-Anzeigentexte aus dem Keyword der Kampagne und dem Firmenprofil und überschreibt damit die vorhandenen Texte. Der Aufruf dauert länger als andere Funktionen. Bei Kampagnen mit Anzeigengruppen wird in jeder Anzeigengruppe die erste vorhandene Anzeige aktualisiert (Überschriften 1–9, Beschreibungen 1–4, Pfade), und zwar nur die Felder, für die die KI einen Text liefert; gibt es keine Anzeige, wird eine neue angelegt. Bei Kampagnen ohne Anzeigengruppen wird das Feld `content` mit dem neuen Ergebnis überschrieben. Zurück kommt die Kampagne (Felder wie `get_campaign`).\n\n**Gut zu wissen**\n\n- Bisherige Texte werden ohne Rückfrage überschrieben; es gibt keine Vorschau.\n- Die Kampagne braucht eine gültige `keyword_id`; fehlt sie, scheitert der Aufruf.\n- Im Antwortobjekt der Kampagne ändert sich bei Kampagnen mit Anzeigengruppen nichts Sichtbares, weil die Texte in der Anzeige liegen.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/update_google_search_campaign \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"campaign_id\": 8203\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/update_google_search_campaign\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"campaign_id\": 8203,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/update_google_search_campaign\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"campaign_id\": 8203\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"type":"integer","description":"ID der Kampagne (ganze Zahl, Pflicht). Sie muss zu deiner Firma gehören und ein Keyword (`keyword_id`) hinterlegt haben, aus dem die neuen Texte entstehen. Typischerweise ist es eine Kampagne aus `create_google_search_campaign`.","example":8203}},"required":["campaign_id"]},"example":{"campaign_id":8203}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"update_google_search_campaign","stufe":"schreiben","data":{"success":true,"data":{"id":8203,"name":"musterbetrieb-heizung.example.com - heizungswartung kosten","description":null,"provider":"google","campaign_type":"search","content":"","is_active":true,"keyword_id":"3105","template_id":"2210","document_id":"9087","budget":null,"channels":null,"company_id":"1001","created_at":"2026-11-04T10:12:00+01:00","updated_at":"2026-11-06T16:40:12+01:00"},"message":"Google Kampagne aktualisiert","timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/activate_campaign":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__activate_campaign","summary":"Kampagne aktivieren","description":"Setzt das Feld `is_active` der Kampagne auf `true` und liefert die Kampagne vollständig zurück (Meldung „Kampagne aktiviert“). Die Funktion ändert ausschließlich dieses Kennzeichen am Kampagnenobjekt; weitere Schritte gehören nicht zu diesem Aufruf. Ist die Kampagne bereits aktiv, bleibt sie unverändert und die Funktion meldet trotzdem Erfolg. Die Aktion zählt zur Rechtestufe „veröffentlichen“ und verlangt `\"bestaetigt\": true`. Das Gegenstück ist `pause_campaign`; den Aktivstatus kannst du auch über `update_campaign` oder beim Anlegen setzen.\n\n**Gut zu wissen**\n\n- Prüfe vor dem Aktivieren Inhalt und Budget der Kampagne mit `get_campaign`.\n\n**Recht am Schlüssel:** `veroeffentlichen`. Der Aufruf wirkt nach außen und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/activate_campaign \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"campaign_id\": 8201,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/activate_campaign\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"campaign_id\": 8201,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/activate_campaign\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"campaign_id\": 8201,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"veroeffentlichen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"type":"integer","description":"ID der Kampagne (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_campaigns` oder aus der Antwort von `create_campaign`. Die Kampagne muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kampagne nicht gefunden oder kein Zugriff“.","example":8201},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["campaign_id","bestaetigt"]},"example":{"campaign_id":8201,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"activate_campaign","stufe":"veroeffentlichen","data":{"success":true,"data":{"id":8201,"name":"Herbstaktion Heizungswartung","description":null,"provider":"google","campaign_type":"search","content":"","is_active":true,"keyword_id":"3105","template_id":"2210","document_id":"9087","budget":"25","channels":"ppc","company_id":"1001","created_at":"2026-11-04T10:12:00+01:00","updated_at":"2026-11-06T16:40:12+01:00"},"message":"Kampagne aktiviert","timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „veroeffentlichen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/pause_campaign":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__pause_campaign","summary":"Kampagne pausieren","description":"Setzt das Feld `is_active` der Kampagne auf `false` und liefert die Kampagne vollständig zurück (Meldung „Kampagne pausiert“). Die Funktion ändert ausschließlich dieses Kennzeichen am Kampagnenobjekt; Inhalt, Budget und Verknüpfungen bleiben erhalten, sodass du die Kampagne später mit `activate_campaign` wieder einschalten kannst. Ist die Kampagne bereits pausiert, bleibt alles unverändert und die Funktion meldet trotzdem Erfolg. Anders als das Löschen ist das Pausieren umkehrbar. In `get_campaigns` findest du pausierte Kampagnen mit dem Filter `status: inactive`.\n\n**Gut zu wissen**\n\n- Zum bloßen Stoppen ist Pausieren besser als `delete_campaign`, weil es umkehrbar ist.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/pause_campaign \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"campaign_id\": 8201\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/pause_campaign\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"campaign_id\": 8201,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/pause_campaign\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"campaign_id\": 8201\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"type":"integer","description":"ID der Kampagne (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_campaigns` oder aus der Antwort von `create_campaign`. Die Kampagne muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kampagne nicht gefunden oder kein Zugriff“.","example":8201}},"required":["campaign_id"]},"example":{"campaign_id":8201}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"pause_campaign","stufe":"schreiben","data":{"success":true,"data":{"id":8201,"name":"Herbstaktion Heizungswartung","description":null,"provider":"google","campaign_type":"search","content":"","is_active":false,"keyword_id":"3105","template_id":"2210","document_id":"9087","budget":"25","channels":"ppc","company_id":"1001","created_at":"2026-11-04T10:12:00+01:00","updated_at":"2026-11-06T16:40:12+01:00"},"message":"Kampagne pausiert","timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/get_campaign_performance":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__get_campaign_performance","summary":"Kampagnen-Performance abrufen (Platzhalter)","description":"Liefert Leistungskennzahlen zu einer Kampagne deiner Firma. Die Funktion ist derzeit ein Platzhalter: `impressions`, `clicks` und `conversions` sind immer 0, weil keine Werbeplattform angebunden ist. Echt ist nur `status`: `active`, wenn die Kampagne aktiv ist, sonst `paused`, außerdem `campaign_id`. Verlasse dich daher nicht auf die Zahlen für Auswertungen. Die Funktion prüft aber, ob die Kampagne existiert und zu deiner Firma gehört (sonst „Kampagne nicht gefunden oder kein Zugriff“). Für eine Übersicht über alle Kampagnen nutzt du `get_campaign_stats`.\n\n**Gut zu wissen**\n\n- impressions, clicks und conversions sind fest 0 (Platzhalter).\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/get_campaign_performance \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"campaign_id\": 8201\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/get_campaign_performance\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"campaign_id\": 8201,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/get_campaign_performance\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"campaign_id\": 8201\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"type":"integer","description":"ID der Kampagne (Pimcore-Objekt-ID, ganze Zahl). Du bekommst sie aus `get_campaigns` oder aus der Antwort von `create_campaign`. Die Kampagne muss zur Firma deines Schlüssels gehören, sonst antwortet die Funktion mit „Kampagne nicht gefunden oder kein Zugriff“.","example":8201}},"required":["campaign_id"]},"example":{"campaign_id":8201}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"get_campaign_performance","stufe":"lesen","data":{"success":true,"data":{"campaign_id":8201,"impressions":0,"clicks":0,"conversions":0,"status":"active"},"message":null,"timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/campaigns/get_campaign_stats":{"post":{"tags":["Werbekampagnen"],"operationId":"campaigns__get_campaign_stats","summary":"Kampagnen-Statistik der Firma","description":"Zählt alle Kampagnen deiner Firma und liefert aggregierte Zahlen: `total` (Anzahl aller Kampagnen), `active` (aktive), `inactive` (inaktive, also total minus active) und `by_provider` (Objekt mit der Anzahl je Provider). Kampagnen ohne Provider werden unter `unknown` gezählt. Die Funktion braucht keine Parameter und liefert nur Bestandszahlen, keine Leistungsdaten wie Klicks oder Kosten. Intern werden alle Kampagnen der Firma geladen und gezählt; bei sehr vielen Kampagnen dauert der Aufruf entsprechend länger. Einzelne Kampagnen listest du mit `get_campaigns`.\n\n**Gut zu wissen**\n\n- Die Zahlen beziehen sich ausschließlich auf deine Firma.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/campaigns/get_campaign_stats \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/campaigns/get_campaign_stats\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/campaigns/get_campaign_stats\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"campaigns","werkzeug":"get_campaign_stats","stufe":"lesen","data":{"success":true,"data":{"total":7,"active":4,"inactive":3,"by_provider":{"google":5,"facebook":1,"custom":1}},"message":null,"timestamp":"2026-11-09 11:08:45","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/get_leads":{"post":{"tags":["CRM"],"operationId":"crm__get_leads","summary":"Leads auflisten und filtern","description":"Liefert die Leads deines Unternehmens, neueste zuerst (Sortierung nach Anlagedatum absteigend). Du kannst nach Status, Pipeline, Marketplace-Status und einem Suchbegriff filtern; alle gesetzten Filter gelten gleichzeitig (UND). Die Suche vergleicht per Teilstring mit Vorname, Nachname, E-Mail und Firma. Ohne `limit` kommen 25 Einträge, die Seiteneinteilung steuerst du mit `limit` und `offset`. Die Antwort enthält `items` (Kurzform eines Leads, ohne Beschreibung, Adresse und Metadaten), `total` (Gesamtzahl der Treffer ohne Seitenbegrenzung), `limit` und `offset`. Für alle Felder eines Leads rufst du anschließend `get_lead` mit der `id` auf.\n\n**Gut zu wissen**\n\n- Es gibt keine Obergrenze für `limit` (Minimum 1). Hole große Bestände besser in Seiten und blättere mit `offset`.\n- Filter mit leerem Wert oder `0` werden ignoriert (z. B. `pipeline_id: 0`).\n- Alle Filter und die Suche gelten nur für Leads deines Unternehmens; der Mandant kommt aus dem Schlüssel.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/get_leads \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"new\",\n    \"search\": \"Beispiel\",\n    \"limit\": 10,\n    \"offset\": 0\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/get_leads\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"new\",\n        \"search\": \"Beispiel\",\n        \"limit\": 10,\n        \"offset\": 0,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/get_leads\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"new\",\n      \"search\": \"Beispiel\",\n      \"limit\": 10,\n      \"offset\": 0\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Exakter Status-Wert, nach dem gefiltert wird, z. B. `new`, `won` oder `lost`. Welche Werte vorkommen, hängt von deiner Pipeline ab; es wird nur auf Gleichheit geprüft. Leer lassen für alle Status.","example":"new"},"pipeline_id":{"type":"integer","description":"ID der CRM-Pipeline, deren Leads du sehen willst (Ganzzahl). Die IDs liefert `list_pipelines`. Ohne Angabe werden Leads aller Pipelines geliefert."},"search":{"type":"string","description":"Suchbegriff, der als Teilstring in Vorname, Nachname, E-Mail-Adresse oder Firma gesucht wird (ohne Beachtung der Groß-/Kleinschreibung, je nach Datenbank-Einstellung). Sonderzeichen wie & oder Anführungszeichen werden vor der Suche HTML-codiert.","example":"Beispiel"},"marketplace_status":{"type":"string","description":"Exakter Marketplace-Status des Leads, z. B. wenn Leads im Lead-Marktplatz angeboten werden. Es wird nur auf Gleichheit geprüft; leer lassen für alle."},"limit":{"type":"integer","description":"Maximale Anzahl der zurückgegebenen Leads (Ganzzahl ab 1, Standard 25). Werte unter 1 werden auf 1 angehoben; ein Maximum gibt es nicht.","example":10},"offset":{"type":"integer","description":"Anzahl der Leads, die übersprungen werden (Ganzzahl ab 0, Standard 0). Zusammen mit `limit` zum Blättern: Seite 2 bei `limit` 25 ist `offset` 25.","example":0}}},"example":{"status":"new","search":"Beispiel","limit":10,"offset":0}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"get_leads","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":4711,"firstname":"Mira","lastname":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550123","company":"Muster Werkzeugbau GmbH","status":"new","pipeline_id":"3","marketplace_status":null,"price":0,"city":"Düsseldorf","country":"DE","accepted":false,"rejected":false,"creation_date":"2026-11-03T09:12:44+01:00","modification_date":"2026-11-07T16:40:02+01:00"},{"id":4698,"firstname":"Jonas","lastname":"Muster","email":"jonas.muster@example.com","phone":null,"company":"Muster & Söhne KG","status":"new","pipeline_id":"3","marketplace_status":null,"price":0,"city":"Düsseldorf","country":"DE","accepted":false,"rejected":false,"creation_date":"2026-11-03T09:12:44+01:00","modification_date":"2026-11-07T16:40:02+01:00"}],"total":37,"limit":10,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/get_lead":{"post":{"tags":["CRM"],"operationId":"crm__get_lead","summary":"Einen Lead im Detail abrufen","description":"Lädt einen einzelnen Lead über seine ID und liefert alle verfügbaren Felder. Zusätzlich zur Kurzform aus `get_leads` (id, Name, E-Mail, Telefon, Firma, Status, Pipeline, Marketplace-Status, Preis, Ort, Land, accepted/rejected, Anlage- und Änderungsdatum) enthält die Antwort `description`, `title`, `address` (Straße, Hausnummer, PLZ, Stadt, Land), `meta` (Google-Klick-ID, Keyword-ID, Landingpage, Client-IP, IP-Daten), `flags` (Spam-Kennzeichen, Annahme-/Ablehnungszeitpunkt, Ablehnungsgrund) und `engagement` (letzte Aktivität, nächste Empfehlung, Chat-Daten). Gehört der Lead nicht zu deinem Unternehmen oder existiert er nicht, kommt ein Fehler ohne weitere Angaben.\n\n**Gut zu wissen**\n\n- Fehlerfälle erscheinen als HTTP 200 mit `success: false` und einem Text in `error` (z. B. „Fehler beim Laden des Leads: Lead nicht gefunden oder kein Zugriff“).\n- Die Lead-ID erhältst du aus `get_leads` oder `create_lead`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/get_lead \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"lead_id\": 4711\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/get_lead\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"lead_id\": 4711,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/get_lead\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"lead_id\": 4711\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lead_id":{"type":"integer","description":"Numerische ID des Leads, wie sie in `id` von `get_leads` und `create_lead` steht. Leads anderer Unternehmen werden nicht herausgegeben und führen zum selben Fehler wie eine unbekannte ID.","example":4711}},"required":["lead_id"]},"example":{"lead_id":4711}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"get_lead","stufe":"lesen","data":{"success":true,"data":{"id":4711,"firstname":"Mira","lastname":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550123","company":"Muster Werkzeugbau GmbH","status":"new","pipeline_id":"3","marketplace_status":null,"price":0,"city":"Düsseldorf","country":"DE","accepted":false,"rejected":false,"creation_date":"2026-11-03T09:12:44+01:00","modification_date":"2026-11-07T16:40:02+01:00","description":"Anfrage über das Kontaktformular, wünscht Rückruf.","title":"Einkaufsleiterin","address":{"street":"Beispielstraße","street_number":"12","zip":"40210","city":"Düsseldorf","country":"DE"},"meta":{"gclid":null,"keyword_id":null,"landingpage":"https://www.example.com/angebot","client_ip":null,"ip_data":[]},"flags":{"is_spam":false,"accepted_time":null,"rejected_time":null,"rejection_type":null},"engagement":{"last_activity":null,"next_recommendation":null,"chat_data":null}},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/create_lead":{"post":{"tags":["CRM"],"operationId":"crm__create_lead","summary":"Lead anlegen","description":"Legt einen neuen Lead für dein Unternehmen an und liefert ihn in der Detailform (wie `get_lead`) zurück; die Meldung lautet „Lead erstellt“. Alle Angaben stehen im Objekt `data`, das mindestens ein Feld enthalten muss. Der Lead wird veröffentlicht und deinem Unternehmen zugeordnet; ein Status wird nicht automatisch gesetzt, gib ihn bei Bedarf in `data` mit. Der interne Schlüssel des Leads wird aus der E-Mail-Adresse, sonst aus dem Nachnamen gebildet und bei Kollisionen nummeriert. Es findet keine Dublettenprüfung statt: Dieselbe E-Mail-Adresse kann mehrfach angelegt werden. Zum späteren Ändern nutzt du `update_lead`, für Status und Pipeline `change_lead_status`.\n\n**Gut zu wissen**\n\n- Textfelder müssen als Text (String) gesendet werden. Eine Zahl für `zip` führt zu einem Fehler (HTTP 422).\n- Text wird getrimmt und HTML-codiert gespeichert (aus `&` wird `&amp;`).\n- Erlaubte Felder in `data`: firstname, lastname, email, phone, mobile, company, status, anrede, title, description, leadtype, landingpage, gclid, currentWorker, marketplaceStatus, url, street, streetnumber, zip, city, country; außerdem isspam, accepted, rejected (Wahrheitswerte), acceptedtime, rejectedtime, last_activity (Datum), keyword_id, pipeline_id (oder pipelineid), price, interview_score, next_recommendation, chatdata, ipdata. Unbekannte Felder werden ignoriert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/create_lead \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"data\": {\n        \"firstname\": \"Mira\",\n        \"lastname\": \"Beispiel\",\n        \"email\": \"mira.beispiel@example.com\",\n        \"phone\": \"+49 211 5550123\",\n        \"company\": \"Muster Werkzeugbau GmbH\",\n        \"status\": \"new\",\n        \"description\": \"Anfrage über das Kontaktformular, wünscht Rückruf.\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/create_lead\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"data\": {\n            \"firstname\": \"Mira\",\n            \"lastname\": \"Beispiel\",\n            \"email\": \"mira.beispiel@example.com\",\n            \"phone\": \"+49 211 5550123\",\n            \"company\": \"Muster Werkzeugbau GmbH\",\n            \"status\": \"new\",\n            \"description\": \"Anfrage über das Kontaktformular, wünscht Rückruf.\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/create_lead\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"data\": {\n          \"firstname\": \"Mira\",\n          \"lastname\": \"Beispiel\",\n          \"email\": \"mira.beispiel@example.com\",\n          \"phone\": \"+49 211 5550123\",\n          \"company\": \"Muster Werkzeugbau GmbH\",\n          \"status\": \"new\",\n          \"description\": \"Anfrage über das Kontaktformular, wünscht Rückruf.\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"Objekt mit den Lead-Feldern, z. B. `firstname`, `lastname`, `email`, `phone`, `company`, `status`, `description`, `pipeline_id`. Muss mindestens ein Feld enthalten, sonst Fehler „Leaddaten fehlen“. Text als String senden; Zahlenfelder wie `price` oder `keyword_id` als Zahl; `chatdata` und `ipdata` dürfen auch Objekte sein und werden als JSON gespeichert.","example":{"firstname":"Mira","lastname":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550123","company":"Muster Werkzeugbau GmbH","status":"new","description":"Anfrage über das Kontaktformular, wünscht Rückruf."}}},"required":["data"]},"example":{"data":{"firstname":"Mira","lastname":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550123","company":"Muster Werkzeugbau GmbH","status":"new","description":"Anfrage über das Kontaktformular, wünscht Rückruf."}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"create_lead","stufe":"schreiben","data":{"success":true,"data":{"id":4711,"firstname":"Mira","lastname":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550123","company":"Muster Werkzeugbau GmbH","status":"new","pipeline_id":"3","marketplace_status":null,"price":0,"city":"Düsseldorf","country":"DE","accepted":false,"rejected":false,"creation_date":"2026-11-03T09:12:44+01:00","modification_date":"2026-11-07T16:40:02+01:00","description":"Anfrage über das Kontaktformular, wünscht Rückruf.","title":"Einkaufsleiterin","address":{"street":"Beispielstraße","street_number":"12","zip":"40210","city":"Düsseldorf","country":"DE"},"meta":{"gclid":null,"keyword_id":null,"landingpage":"https://www.example.com/angebot","client_ip":null,"ip_data":[]},"flags":{"is_spam":false,"accepted_time":null,"rejected_time":null,"rejection_type":null},"engagement":{"last_activity":null,"next_recommendation":null,"chat_data":null}},"message":"Lead erstellt","timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/update_lead":{"post":{"tags":["CRM"],"operationId":"crm__update_lead","summary":"Lead aktualisieren","description":"Ändert Felder eines bestehenden Leads deines Unternehmens und liefert den Lead in der Detailform zurück (Meldung „Lead aktualisiert“). Es werden nur die Felder überschrieben, die du sendest; alle anderen bleiben unverändert. Die zu ändernden Felder stehen in `data`. Fehlt `data`, liest die Funktion alle übrigen Felder des Körpers (außer `lead_id`) als Änderungen, sodass auch ein flacher Körper wie `{\"lead_id\": 4711, \"phone\": \"…\"}` funktioniert. Ohne jede Änderung kommt der Fehler „Keine Daten zum Aktualisieren übergeben“. Für reine Status- oder Pipeline-Wechsel gibt es die schlankere Funktion `change_lead_status`.\n\n**Gut zu wissen**\n\n- Dieselben Feldnamen wie bei `create_lead`; Textfelder bitte als String senden (sonst HTTP 422).\n- Beim Ändern der Pipeline über `data` heißt das Feld `pipeline_id` oder `pipelineid`; der Wert wird als Text gespeichert.\n- Leads anderer Unternehmen sind nicht änderbar (Fehler „Lead nicht gefunden oder kein Zugriff“).\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/update_lead \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"lead_id\": 4711,\n    \"data\": {\n        \"phone\": \"+49 211 5550199\",\n        \"status\": \"contacted\",\n        \"description\": \"Rückruf am 10.11. vereinbart.\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/update_lead\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"lead_id\": 4711,\n        \"data\": {\n            \"phone\": \"+49 211 5550199\",\n            \"status\": \"contacted\",\n            \"description\": \"Rückruf am 10.11. vereinbart.\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/update_lead\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"lead_id\": 4711,\n      \"data\": {\n          \"phone\": \"+49 211 5550199\",\n          \"status\": \"contacted\",\n          \"description\": \"Rückruf am 10.11. vereinbart.\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lead_id":{"type":"integer","description":"Numerische ID des Leads, den du ändern willst (aus `get_leads`). Der Lead muss zu deinem Unternehmen gehören.","example":4711},"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern, z. B. `{\"phone\": \"+49 211 5550199\", \"status\": \"contacted\"}`. Nicht genannte Felder bleiben unverändert. Zum Leeren eines Textfelds sende einen leeren String. Ohne `data` werden die übrigen Körperfelder als Änderungen verwendet.","example":{"phone":"+49 211 5550199","status":"contacted","description":"Rückruf am 10.11. vereinbart."}}},"required":["lead_id","data"]},"example":{"lead_id":4711,"data":{"phone":"+49 211 5550199","status":"contacted","description":"Rückruf am 10.11. vereinbart."}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"update_lead","stufe":"schreiben","data":{"success":true,"data":{"id":4711,"firstname":"Mira","lastname":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550199","company":"Muster Werkzeugbau GmbH","status":"contacted","pipeline_id":"3","marketplace_status":null,"price":0,"city":"Düsseldorf","country":"DE","accepted":false,"rejected":false,"creation_date":"2026-11-03T09:12:44+01:00","modification_date":"2026-11-07T16:40:02+01:00","description":"Rückruf am 10.11. vereinbart.","title":"Einkaufsleiterin","address":{"street":"Beispielstraße","street_number":"12","zip":"40210","city":"Düsseldorf","country":"DE"},"meta":{"gclid":null,"keyword_id":null,"landingpage":"https://www.example.com/angebot","client_ip":null,"ip_data":[]},"flags":{"is_spam":false,"accepted_time":null,"rejected_time":null,"rejection_type":null},"engagement":{"last_activity":null,"next_recommendation":null,"chat_data":null}},"message":"Lead aktualisiert","timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/delete_lead":{"post":{"tags":["CRM"],"operationId":"crm__delete_lead","summary":"Lead dauerhaft löschen","description":"Löscht einen Lead deines Unternehmens unwiderruflich. Die Funktion prüft zuerst, dass der Lead existiert und zu deinem Unternehmen gehört, und löscht ihn dann. Im Erfolgsfall ist `data` leer (`null`) und die Meldung lautet „Lead gelöscht“. Es gibt keinen Papierkorb auf API-Ebene: Ein gelöschter Lead lässt sich über die Plattform-API nicht wiederherstellen. Die Funktion hat die Rechte-Stufe „löschen“; dein Schlüssel muss dieses Recht haben, und im Körper ist zusätzlich `\"bestaetigt\": true` erforderlich, sonst antwortet die API mit HTTP 409. Wenn du einen Lead nur aus der aktiven Bearbeitung nehmen willst, setze stattdessen mit `change_lead_status` einen Status wie `lost`.\n\n**Gut zu wissen**\n\n- Ohne `\"bestaetigt\": true` wird nichts gelöscht (HTTP 409).\n- Termine, die auf den Lead verweisen, werden von dieser Funktion nicht angefasst.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/delete_lead \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"lead_id\": 4698,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/delete_lead\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"lead_id\": 4698,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/delete_lead\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"lead_id\": 4698,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lead_id":{"type":"integer","description":"Numerische ID des zu löschenden Leads (aus `get_leads`). Leads anderer Unternehmen und unbekannte IDs führen zu einem Fehler und werden nicht gelöscht.","example":4698},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["lead_id","bestaetigt"]},"example":{"lead_id":4698,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"delete_lead","stufe":"loeschen","data":{"success":true,"data":null,"message":"Lead gelöscht","timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/change_lead_status":{"post":{"tags":["CRM"],"operationId":"crm__change_lead_status","summary":"Status oder Pipeline eines Leads ändern","description":"Setzt bei einem Lead deines Unternehmens den Status, die Pipeline und/oder den Marketplace-Status. Nur die Angaben, die du mitsendest, werden geändert; fehlt eine, bleibt der bisherige Wert. Die Funktion speichert den Lead und liefert ihn in der Detailform zurück (Meldung „Lead-Status aktualisiert“). Es wird nicht geprüft, ob der Status oder die Pipeline in deinem System vorgesehen sind; verwende die Werte aus `list_pipelines` und aus bestehenden Leads. Sendest du keine der drei Angaben, wird der Lead unverändert gespeichert und zurückgegeben.\n\n**Gut zu wissen**\n\n- Die Pipeline-ID prüfst du vorher mit `list_pipelines`; eine nicht existierende ID wird trotzdem gespeichert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/change_lead_status \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"lead_id\": 4711,\n    \"status\": \"qualified\",\n    \"pipeline_id\": 3\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/change_lead_status\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"lead_id\": 4711,\n        \"status\": \"qualified\",\n        \"pipeline_id\": 3,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/change_lead_status\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"lead_id\": 4711,\n      \"status\": \"qualified\",\n      \"pipeline_id\": 3\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lead_id":{"type":"integer","description":"Numerische ID des Leads (aus `get_leads`). Pflichtangabe; der Lead muss zu deinem Unternehmen gehören.","example":4711},"status":{"type":"string","description":"Neuer Status als Text, z. B. `contacted`, `qualified`, `won` oder `lost`. Die zulässigen Werte hängen von deiner Pipeline ab und werden nicht serverseitig geprüft. Weglassen, um den Status zu behalten.","example":"qualified"},"pipeline_id":{"type":"integer","description":"ID der Pipeline, in die der Lead verschoben wird (Ganzzahl, siehe `list_pipelines`). Weglassen, um die Pipeline zu behalten.","example":3},"marketplace_status":{"type":"string","description":"Neuer Marketplace-Status als Text. Weglassen, um den bisherigen Wert zu behalten."}},"required":["lead_id"]},"example":{"lead_id":4711,"status":"qualified","pipeline_id":3}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"change_lead_status","stufe":"schreiben","data":{"success":true,"data":{"id":4711,"firstname":"Mira","lastname":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550123","company":"Muster Werkzeugbau GmbH","status":"qualified","pipeline_id":"3","marketplace_status":null,"price":0,"city":"Düsseldorf","country":"DE","accepted":false,"rejected":false,"creation_date":"2026-11-03T09:12:44+01:00","modification_date":"2026-11-07T16:40:02+01:00","description":"Anfrage über das Kontaktformular, wünscht Rückruf.","title":"Einkaufsleiterin","address":{"street":"Beispielstraße","street_number":"12","zip":"40210","city":"Düsseldorf","country":"DE"},"meta":{"gclid":null,"keyword_id":null,"landingpage":"https://www.example.com/angebot","client_ip":null,"ip_data":[]},"flags":{"is_spam":false,"accepted_time":null,"rejected_time":null,"rejection_type":null},"engagement":{"last_activity":null,"next_recommendation":null,"chat_data":null}},"message":"Lead-Status aktualisiert","timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/get_appointments":{"post":{"tags":["CRM"],"operationId":"crm__get_appointments","summary":"Termine auflisten","description":"Liefert die Termine deines Unternehmens, aufsteigend nach Terminzeit sortiert (frühester zuerst). Filter: Status, Lead, Zeitraum (`from`/`to`, jeweils einschließlich) – alle gesetzten Filter gelten gleichzeitig. Ohne `limit` kommen 25 Termine; mit `limit` und `offset` blätterst du. Die Antwort enthält `items` (jeder Termin mit id, date, appointment_type, status, duration, channel, contact, meeting_link, lead_id, member_id, interview_id, company_id, notes, outcome, Kennzeichen, Buchungs- und Zeitangaben), `total`, `limit` und `offset`. Datumsangaben kommen im ISO-8601-Format. Zum Anlegen dient `schedule_appointment`, zum Ändern `update_appointment`, zum Absagen `cancel_appointment`.\n\n**Gut zu wissen**\n\n- Über die Plattform-API setzt der Server immer `member_id` deines Schlüssels. Weil die Funktion einen vorhandenen `member_id` als Filter auf das Terminfeld „Mitglied“ anwendet, siehst du hier nur Termine, die dieser Person zugeordnet sind.\n- `from` und `to` werden mit der Zeitzone des Servers interpretiert, wenn du keinen Offset angibst.\n- Es gibt keine Obergrenze für `limit` (Minimum 1).\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/get_appointments \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"scheduled\",\n    \"from\": \"2026-11-09T00:00:00+01:00\",\n    \"to\": \"2026-11-30T23:59:59+01:00\",\n    \"limit\": 20\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/get_appointments\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"scheduled\",\n        \"from\": \"2026-11-09T00:00:00+01:00\",\n        \"to\": \"2026-11-30T23:59:59+01:00\",\n        \"limit\": 20,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/get_appointments\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"scheduled\",\n      \"from\": \"2026-11-09T00:00:00+01:00\",\n      \"to\": \"2026-11-30T23:59:59+01:00\",\n      \"limit\": 20\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Exakter Termin-Status, z. B. `scheduled`, `confirmed`, `cancelled`. Es wird auf Gleichheit geprüft; leer lassen für alle Status (abgesagte Termine eingeschlossen).","example":"scheduled"},"lead_id":{"type":"integer","description":"ID des Leads, dessen Termine du sehen willst (Ganzzahl, aus `get_leads`). Ohne Angabe werden Termine aller Leads geliefert."},"from":{"type":"string","description":"Beginn des Zeitraums als ISO-8601-Zeitpunkt, z. B. `2026-11-01T00:00:00+01:00`; auch ein reines Datum `2026-11-01` oder ein Unix-Zeitstempel wird verstanden. Es werden Termine ab diesem Zeitpunkt geliefert. Ungültige Werte führen zu einem Fehler.","example":"2026-11-09T00:00:00+01:00"},"to":{"type":"string","description":"Ende des Zeitraums, gleiches Format wie `from`. Es werden Termine bis einschließlich diesem Zeitpunkt geliefert; ein reines Datum bedeutet 00:00 Uhr dieses Tages.","example":"2026-11-30T23:59:59+01:00"},"limit":{"type":"integer","description":"Maximale Anzahl der Termine (Ganzzahl ab 1, Standard 25). Ein Maximum gibt es nicht.","example":20},"offset":{"type":"integer","description":"Anzahl der zu überspringenden Termine (Ganzzahl ab 0, Standard 0), zum Blättern zusammen mit `limit`."}}},"example":{"status":"scheduled","from":"2026-11-09T00:00:00+01:00","to":"2026-11-30T23:59:59+01:00","limit":20}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"get_appointments","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":5120,"date":"2026-11-12T10:00:00+01:00","appointment_type":"firstcall","status":"scheduled","duration":"45","channel":"video","contact":"Mira Beispiel (mira.beispiel@example.com)","meeting_link":"https://meet.example.com/erstgespraech-mira","lead_id":4711,"member_id":321,"interview_id":null,"company_id":489,"notes":"Interne Notiz: Preisfrage klären.","outcome":null,"follow_up_required":false,"reminder_sent":false,"booked_at":"2026-11-09T10:15:00+01:00","booked_by":null,"actual_start_time":null,"actual_end_time":null,"assigned_worker":null,"hash":null,"client_ip":null,"created_at":"2026-11-09T10:15:00+01:00","updated_at":"2026-11-09T10:15:00+01:00"}],"total":1,"limit":20,"offset":0},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/schedule_appointment":{"post":{"tags":["CRM"],"operationId":"crm__schedule_appointment","summary":"Termin anlegen","description":"Legt einen neuen Termin für dein Unternehmen an und liefert ihn zurück (Meldung „Termin erstellt“). Pflicht ist nur `date`. Ohne Angabe bekommt der Termin den Status `scheduled`, und `booked_at` wird auf den Zeitpunkt des Aufrufs gesetzt. Die Person deines Schlüssels wird als `member_id` eingetragen. Über `lead_id` verknüpfst du den Termin mit einem Lead, über `interview_id` mit einem Interview. Die Funktion prüft weder Verfügbarkeit noch Überschneidungen – nutze vorher `check_availability`. Zum Ändern gibt es `update_appointment`, zum Absagen `cancel_appointment`.\n\n**Gut zu wissen**\n\n- Keine Konfliktprüfung: Doppelbuchungen sind möglich.\n- `notes` wird von HTML-Tags befreit gespeichert; andere Textfelder werden HTML-codiert.\n- `duration` wird als Text gespeichert und in der Antwort als Text (`\"45\"`) geliefert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/schedule_appointment \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"date\": \"2026-11-12T10:00:00+01:00\",\n    \"appointment_type\": \"firstcall\",\n    \"duration\": 45,\n    \"lead_id\": 4711,\n    \"channel\": \"video\",\n    \"meeting_link\": \"https://meet.example.com/erstgespraech-mira\",\n    \"notes\": \"Interne Notiz: Preisfrage klären.\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/schedule_appointment\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"date\": \"2026-11-12T10:00:00+01:00\",\n        \"appointment_type\": \"firstcall\",\n        \"duration\": 45,\n        \"lead_id\": 4711,\n        \"channel\": \"video\",\n        \"meeting_link\": \"https://meet.example.com/erstgespraech-mira\",\n        \"notes\": \"Interne Notiz: Preisfrage klären.\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/schedule_appointment\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"date\": \"2026-11-12T10:00:00+01:00\",\n      \"appointment_type\": \"firstcall\",\n      \"duration\": 45,\n      \"lead_id\": 4711,\n      \"channel\": \"video\",\n      \"meeting_link\": \"https://meet.example.com/erstgespraech-mira\",\n      \"notes\": \"Interne Notiz: Preisfrage klären.\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string","description":"Startzeit des Termins als ISO-8601-Zeitpunkt mit Offset, z. B. `2026-11-12T10:00:00+01:00`. Ohne Offset gilt die Zeitzone des Servers. Auch Unix-Zeitstempel werden akzeptiert. Pflichtangabe.","examples":["2025-10-13T09:30:00+02:00"],"example":"2026-11-12T10:00:00+01:00"},"appointment_type":{"type":"string","description":"Art des Termins als Text, z. B. `firstcall`, `followup`, `interview`, `consulting`. Der Wert wird auch zur Bildung des internen Schlüssels verwendet. Frei wählbar, es gibt keine feste Liste.","example":"firstcall"},"status":{"type":"string","description":"Status des Termins, z. B. `scheduled` (Standard, wenn weggelassen) oder `confirmed`. Es wird nicht gegen eine feste Liste geprüft."},"duration":{"type":"integer","description":"Dauer in Minuten (Ganzzahl), z. B. 45. Fehlt sie, bleibt das Feld leer; `check_availability` rechnet bei leerer Dauer mit 45 Minuten.","example":45},"lead_id":{"type":"integer","description":"ID des verknüpften Leads (Ganzzahl, aus `get_leads`). Es wird nicht geprüft, ob der Lead zu deinem Unternehmen gehört – übergib nur eigene IDs.","example":4711},"interview_id":{"type":"integer","description":"Optionale ID eines bestehenden Interviews, das mit dem Termin verknüpft wird (Ganzzahl). Es wird kein Interview angelegt."},"channel":{"type":"string","description":"Kanal des Termins als Text, z. B. `video`, `phone`, `onsite`. Frei wählbar.","example":"video"},"meeting_link":{"type":"string","description":"Link zum Online-Meeting oder Adresse des Treffpunkts als Text. Wird unverändert (HTML-codiert) gespeichert, nicht auf Gültigkeit geprüft.","example":"https://meet.example.com/erstgespraech-mira"},"contact":{"type":"string","description":"Kontaktperson oder Kurzbeschreibung als Text, z. B. `Mira Beispiel (mira.beispiel@example.com)`. Erscheint in `check_availability` unter den belegten Zeitfenstern."},"notes":{"type":"string","description":"Interne Notizen zum Termin. HTML-Tags werden entfernt. Spätere Absagegründe (`cancel_appointment`) werden an diesen Text angehängt.","example":"Interne Notiz: Preisfrage klären."},"follow_up_required":{"type":"boolean","description":"Wahrheitswert: `true`, wenn nach dem Termin eine Nachbearbeitung nötig ist. Standard ist `false`."}},"required":["date"]},"example":{"date":"2026-11-12T10:00:00+01:00","appointment_type":"firstcall","duration":45,"lead_id":4711,"channel":"video","meeting_link":"https://meet.example.com/erstgespraech-mira","notes":"Interne Notiz: Preisfrage klären."}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"schedule_appointment","stufe":"schreiben","data":{"success":true,"data":{"id":5120,"date":"2026-11-12T10:00:00+01:00","appointment_type":"firstcall","status":"scheduled","duration":"45","channel":"video","contact":"Mira Beispiel (mira.beispiel@example.com)","meeting_link":"https://meet.example.com/erstgespraech-mira","lead_id":4711,"member_id":321,"interview_id":null,"company_id":489,"notes":"Interne Notiz: Preisfrage klären.","outcome":null,"follow_up_required":false,"reminder_sent":false,"booked_at":"2026-11-09T10:15:00+01:00","booked_by":null,"actual_start_time":null,"actual_end_time":null,"assigned_worker":null,"hash":null,"client_ip":null,"created_at":"2026-11-09T10:15:00+01:00","updated_at":"2026-11-09T10:15:00+01:00"},"message":"Termin erstellt","timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/update_appointment":{"post":{"tags":["CRM"],"operationId":"crm__update_appointment","summary":"Termin ändern","description":"Ändert Felder eines bestehenden Termins deines Unternehmens und liefert den Termin zurück (Meldung „Termin aktualisiert“). Alle Änderungen stehen im Objekt `changes`; nur die dort genannten Felder werden überschrieben. Neben den Feldern von `schedule_appointment` (date, appointment_type, status, duration, lead_id, interview_id, channel, meeting_link, contact, notes, follow_up_required) werden auch member_id, reminder_sent, booked_at, booked_by, actual_start_time, actual_end_time, assigned_worker, outcome, hash und client_ip akzeptiert. Notizen (`notes`) werden ersetzt, nicht angehängt. Um einen Termin abzusagen, ist `cancel_appointment` besser geeignet, weil es zusätzlich das Ende festhält und den Grund notiert.\n\n**Gut zu wissen**\n\n- Enthält `changes` eine `company_id`, die nicht deinem Unternehmen entspricht, bricht die Funktion mit einem Fehler ab.\n- Leere `changes` führen zu keinem Fehler, der Termin wird dann unverändert gespeichert.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/update_appointment \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"appointment_id\": 5120,\n    \"changes\": {\n        \"date\": \"2026-11-13T14:00:00+01:00\",\n        \"status\": \"confirmed\",\n        \"duration\": 60\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/update_appointment\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"appointment_id\": 5120,\n        \"changes\": {\n            \"date\": \"2026-11-13T14:00:00+01:00\",\n            \"status\": \"confirmed\",\n            \"duration\": 60,\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/update_appointment\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"appointment_id\": 5120,\n      \"changes\": {\n          \"date\": \"2026-11-13T14:00:00+01:00\",\n          \"status\": \"confirmed\",\n          \"duration\": 60\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"appointment_id":{"type":"integer","description":"Numerische ID des Termins (Feld `id` aus `get_appointments`). Der Termin muss zu deinem Unternehmen gehören, sonst Fehler „Termin nicht gefunden oder kein Zugriff“.","example":5120},"changes":{"type":"object","description":"Objekt mit den zu ändernden Feldern, z. B. `{\"date\": \"2026-11-13T14:00:00+01:00\", \"status\": \"confirmed\"}`. Datumsfelder als ISO-8601-Zeitpunkt, Wahrheitswerte als `true`/`false`, `duration` in Minuten. Mit `null` bei `lead_id`, `member_id` oder `interview_id` löst du die Verknüpfung.","example":{"date":"2026-11-13T14:00:00+01:00","status":"confirmed","duration":60}}},"required":["appointment_id","changes"]},"example":{"appointment_id":5120,"changes":{"date":"2026-11-13T14:00:00+01:00","status":"confirmed","duration":60}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"update_appointment","stufe":"schreiben","data":{"success":true,"data":{"id":5120,"date":"2026-11-13T14:00:00+01:00","appointment_type":"firstcall","status":"confirmed","duration":"60","channel":"video","contact":"Mira Beispiel (mira.beispiel@example.com)","meeting_link":"https://meet.example.com/erstgespraech-mira","lead_id":4711,"member_id":321,"interview_id":null,"company_id":489,"notes":"Interne Notiz: Preisfrage klären.","outcome":null,"follow_up_required":false,"reminder_sent":false,"booked_at":"2026-11-09T10:15:00+01:00","booked_by":null,"actual_start_time":null,"actual_end_time":null,"assigned_worker":null,"hash":null,"client_ip":null,"created_at":"2026-11-09T10:15:00+01:00","updated_at":"2026-11-09T11:02:10+01:00"},"message":"Termin aktualisiert","timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/cancel_appointment":{"post":{"tags":["CRM"],"operationId":"crm__cancel_appointment","summary":"Termin absagen","description":"Markiert einen Termin deines Unternehmens als abgesagt und liefert ihn zurück (Meldung „Termin storniert“). Der Status wird auf `cancelled` gesetzt, sofern du keinen eigenen Status mitgibst, und `actual_end_time` erhält den Zeitpunkt des Aufrufs. Gibst du einen `reason` an, wird er mit Zeitstempel und dem Präfix „Absage“ an die vorhandenen Notizen des Termins angehängt (`[2026-11-09 11:05] Absage: …`). Der Termin wird nicht gelöscht und bleibt über `get_appointments` auffindbar; abgesagte Termine blockieren in `check_availability` keinen Zeitraum mehr. Es werden keine Benachrichtigungen an Beteiligte verschickt.\n\n**Gut zu wissen**\n\n- Es wird nur der Datensatz geändert; Einladungen oder Kalendereinträge außerhalb der Plattform bleiben bestehen.\n- Ein bereits abgesagter Termin kann erneut abgesagt werden; der Grund wird dann nochmals angehängt.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/cancel_appointment \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"appointment_id\": 5120,\n    \"reason\": \"Kunde hat um Verschiebung gebeten.\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/cancel_appointment\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"appointment_id\": 5120,\n        \"reason\": \"Kunde hat um Verschiebung gebeten.\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/cancel_appointment\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"appointment_id\": 5120,\n      \"reason\": \"Kunde hat um Verschiebung gebeten.\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"appointment_id":{"type":"integer","description":"Numerische ID des Termins (Feld `id` aus `get_appointments`). Pflichtangabe; der Termin muss zu deinem Unternehmen gehören.","example":5120},"reason":{"type":"string","description":"Grund der Absage als Text. Wird mit Datum und Uhrzeit an die Notizen des Termins angehängt; HTML-Tags werden entfernt. Leerer Text wird ignoriert.","example":"Kunde hat um Verschiebung gebeten."},"status":{"type":"string","description":"Optional ein eigener Status statt `cancelled`, z. B. `no-show`. Ist der Wert leer, gilt `cancelled`. `cancelled` und `no-show` gelten in `check_availability` als frei."}},"required":["appointment_id"]},"example":{"appointment_id":5120,"reason":"Kunde hat um Verschiebung gebeten."}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"cancel_appointment","stufe":"schreiben","data":{"success":true,"data":{"id":5120,"date":"2026-11-12T10:00:00+01:00","appointment_type":"firstcall","status":"cancelled","duration":"45","channel":"video","contact":"Mira Beispiel (mira.beispiel@example.com)","meeting_link":"https://meet.example.com/erstgespraech-mira","lead_id":4711,"member_id":321,"interview_id":null,"company_id":489,"notes":"Interne Notiz: Preisfrage klären.\n\n[2026-11-09 11:05] Absage: Kunde hat um Verschiebung gebeten.","outcome":null,"follow_up_required":false,"reminder_sent":false,"booked_at":"2026-11-09T10:15:00+01:00","booked_by":null,"actual_start_time":null,"actual_end_time":"2026-11-09T11:05:12+01:00","assigned_worker":null,"hash":null,"client_ip":null,"created_at":"2026-11-09T10:15:00+01:00","updated_at":"2026-11-09T10:15:00+01:00"},"message":"Termin storniert","timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/list_pipelines":{"post":{"tags":["CRM"],"operationId":"crm__list_pipelines","summary":"CRM-Pipelines auflisten","description":"Listet alle CRM-Pipelines, die für dein Unternehmen gelten: eigene Pipelines und global geteilte Pipelines ohne Unternehmenszuordnung. Sortiert wird nach Anlagedatum, neueste zuerst; eine Seiteneinteilung gibt es nicht. Jeder Eintrag enthält `id`, `name`, `description`, `is_default`, `pipelinetype`, `stages` (die Stufen der Pipeline, aus dem gespeicherten JSON gelesen; bei ungültigem JSON der Rohtext, bei leerem Feld eine leere Liste), `business_rules` und `sales_process_rules`. Die `id` verwendest du als `pipeline_id` in `get_leads`, `change_lead_status` und `update_lead`. Die Funktion hat keine Parameter und verändert nichts.\n\n**Gut zu wissen**\n\n- Der genaue Aufbau von `stages` ist je Pipeline frei gestaltet; lies ihn aus der Antwort, statt Annahmen zu treffen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/list_pipelines \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/list_pipelines\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/list_pipelines\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"list_pipelines","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":3,"name":"Neukunden","description":"Standard-Pipeline für Anfragen","is_default":true,"pipelinetype":"sales","stages":[{"key":"new","label":"Neu"},{"key":"contacted","label":"Kontaktiert"},{"key":"won","label":"Gewonnen"}],"business_rules":null,"sales_process_rules":null}]},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/list_workflows":{"post":{"tags":["CRM"],"operationId":"crm__list_workflows","summary":"CRM-Workflows auflisten","description":"Listet die CRM-Workflows deines Unternehmens sowie global geteilte Workflows ohne Unternehmenszuordnung, neueste zuerst und ohne Seiteneinteilung. Jeder Eintrag enthält `id`, `name`, `description`, `pipeline_id` (die Pipeline, zu der der Workflow gehört), `is_active` und `archive`. Die Funktion liefert nur die Stammdaten der Workflows, nicht deren Schritte oder Auslöser, und sie startet keinen Workflow. Die Pipeline-IDs kannst du mit `list_pipelines` auflösen. Die Funktion hat keine Parameter.\n\n**Gut zu wissen**\n\n- Archivierte Workflows werden mit `archive: true` mitgeliefert und nicht herausgefiltert.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/list_workflows \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/list_workflows\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/list_workflows\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"list_workflows","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":12,"name":"Erstkontakt innerhalb von 24 Stunden","description":"Erinnert an den Rückruf bei neuen Anfragen","pipeline_id":"3","is_active":true,"archive":false}]},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/list_email_templates":{"post":{"tags":["CRM"],"operationId":"crm__list_email_templates","summary":"E-Mail-Vorlagen auflisten","description":"Listet die CRM-E-Mail-Vorlagen deines Unternehmens und global geteilte Vorlagen ohne Unternehmenszuordnung, neueste zuerst und ohne Seiteneinteilung. Mit `active_only: true` kommen nur aktive Vorlagen. Jeder Eintrag enthält `id`, `name`, `subject`, `from`, `to`, `cc`, `bcc` und `is_active`. Den Inhalt (Text) der Vorlage liefert die Funktion nicht. Sie versendet auch nichts; sie dient dazu, vorhandene Vorlagen und ihre Betreffzeilen zu kennen.\n\n**Gut zu wissen**\n\n- Ohne `active_only` werden auch deaktivierte Vorlagen geliefert.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/list_email_templates \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"active_only\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/list_email_templates\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"active_only\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/list_email_templates\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"active_only\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"active_only":{"type":"boolean","description":"Wahrheitswert. Bei `true` werden nur aktive Vorlagen geliefert, bei `false` oder ohne Angabe alle Vorlagen, auch deaktivierte.","example":true}}},"example":{"active_only":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"list_email_templates","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":88,"name":"Rückruf-Bestätigung","subject":"Ihre Anfrage bei Muster Werkzeugbau","from":"vertrieb@example.com","to":null,"cc":null,"bcc":null,"is_active":true}]},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/summarize_lead":{"post":{"tags":["CRM"],"operationId":"crm__summarize_lead","summary":"Lead zusammenfassen","description":"Liefert zu einem Lead zwei Teile: `lead` (die vollständige Detailform wie in `get_lead`) und `summary`, eine kompakte Zusammenfassung mit `headline` (Vor- und Nachname), `company`, `email`, `status`, `pipeline_id`, `marketplace_status`, `last_activity`, `next_recommendation`, `notes` (die Beschreibung des Leads) und `score` (Interview-Score). Die Zusammenfassung wird aus den gespeicherten Feldern zusammengestellt; es wird kein Text neu formuliert oder berechnet. Die Funktion verändert nichts, ist aber in der Rechte-Stufe „schreiben“ eingeordnet – dein Schlüssel braucht deshalb das Schreibrecht, obwohl nur gelesen wird.\n\n**Gut zu wissen**\n\n- Benötigt das Recht „schreiben“, obwohl sie nichts ändert.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/summarize_lead \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"lead_id\": 4711\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/summarize_lead\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"lead_id\": 4711,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/summarize_lead\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"lead_id\": 4711\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lead_id":{"type":"integer","description":"Numerische ID des Leads (aus `get_leads`). Pflichtangabe; der Lead muss zu deinem Unternehmen gehören.","example":4711}},"required":["lead_id"]},"example":{"lead_id":4711}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"summarize_lead","stufe":"lesen","data":{"success":true,"data":{"lead":{"id":4711,"firstname":"Mira","lastname":"Beispiel","email":"mira.beispiel@example.com","phone":"+49 211 5550123","company":"Muster Werkzeugbau GmbH","status":"new","pipeline_id":"3","marketplace_status":null,"price":0,"city":"Düsseldorf","country":"DE","accepted":false,"rejected":false,"creation_date":"2026-11-03T09:12:44+01:00","modification_date":"2026-11-07T16:40:02+01:00","description":"Anfrage über das Kontaktformular, wünscht Rückruf.","title":"Einkaufsleiterin","address":{"street":"Beispielstraße","street_number":"12","zip":"40210","city":"Düsseldorf","country":"DE"},"meta":{"gclid":null,"keyword_id":null,"landingpage":"https://www.example.com/angebot","client_ip":null,"ip_data":[]},"flags":{"is_spam":false,"accepted_time":null,"rejected_time":null,"rejection_type":null},"engagement":{"last_activity":null,"next_recommendation":null,"chat_data":null}},"summary":{"headline":"Mira Beispiel","company":"Muster Werkzeugbau GmbH","email":"mira.beispiel@example.com","status":"new","pipeline_id":"3","marketplace_status":null,"last_activity":null,"next_recommendation":null,"notes":"Anfrage über das Kontaktformular, wünscht Rückruf.","score":null}},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/check_availability":{"post":{"tags":["CRM"],"operationId":"crm__check_availability","summary":"Freie Termine an einem Tag prüfen","description":"Prüft für einen Kalendertag, welche Zeitfenster frei sind. Berücksichtigt werden alle Termine deines Unternehmens an diesem Tag, außer solchen mit Status `cancelled` oder `no-show`. Die Funktion rechnet mit festen Geschäftszeiten von 08:00 bis 18:00 Uhr und schlägt Startzeiten im 30-Minuten-Raster vor, in die ein Termin der gewünschten Dauer (Standard 45, mindestens 15 Minuten) vollständig passt. Mit `preferred_time` prüfst du zusätzlich einen Wunschtermin. Die Antwort enthält date, preferred_time, preferred_time_available, existing_appointments (Anzahl), existing_slots (start, end, type, contact), free_slots (Startzeiten als HH:MM), duration_minutes und eine fertige Empfehlung als Satz in `recommendation`. Sie verändert nichts.\n\n**Gut zu wissen**\n\n- Benötigt das Recht „schreiben“, obwohl sie nur liest.\n- Geprüft wird der Terminbestand des ganzen Unternehmens, nicht einer einzelnen Person oder eines externen Kalenders.\n- Der Parameter `appointment_type` steht im Schema, beeinflusst das Ergebnis aber nicht.\n- Termine ohne Dauer werden mit 45 Minuten gerechnet.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/check_availability \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"date\": \"2026-11-12\",\n    \"preferred_time\": \"10:00\",\n    \"duration\": 45\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/check_availability\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"date\": \"2026-11-12\",\n        \"preferred_time\": \"10:00\",\n        \"duration\": 45,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/check_availability\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"date\": \"2026-11-12\",\n      \"preferred_time\": \"10:00\",\n      \"duration\": 45\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string","description":"Der zu prüfende Tag im Format `YYYY-MM-DD`, z. B. `2026-11-12`. Muss genau in dieser Schreibweise angegeben werden, da sie direkt mit dem Terminbestand verglichen wird. Pflichtangabe.","example":"2026-11-12"},"preferred_time":{"type":"string","description":"Gewünschte Startzeit im Format `HH:MM` (24 Stunden), z. B. `14:00`. Mit der Angabe liefert die Antwort `preferred_time_available` und eine passende Empfehlung samt bis zu drei Alternativen. Ohne Angabe ist der Wert `false`.","example":"10:00"},"duration":{"type":"integer","description":"Gewünschte Dauer in Minuten (Ganzzahl). Standard 45, Werte unter 15 werden auf 15 angehoben. Bestimmt, welche Startzeiten als frei gelten.","example":45},"appointment_type":{"type":"string","description":"Im Schema vorgesehene Terminart (z. B. `firstcall`, `interview`). Der Wert wird derzeit nicht ausgewertet; das Ergebnis ist für alle Arten gleich."}},"required":["date"]},"example":{"date":"2026-11-12","preferred_time":"10:00","duration":45}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"check_availability","stufe":"lesen","data":{"success":true,"data":{"date":"2026-11-12","preferred_time":"10:00","preferred_time_available":false,"existing_appointments":2,"existing_slots":[{"start":"09:30","end":"10:15","type":"followup","contact":"Jonas Muster"},{"start":"13:00","end":"13:45","type":"firstcall","contact":"Mira Beispiel (mira.beispiel@example.com)"}],"free_slots":["10:15","10:45","11:15","11:45","12:15","13:45","14:15","14:45","15:15","15:45","16:15","16:45","17:15"],"duration_minutes":45,"recommendation":"Der Termin um 10:00 Uhr ist leider belegt. Alternativen: 10:15, 10:45, 11:15 Uhr."},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/crm/trigger_website_crawl":{"post":{"tags":["CRM"],"operationId":"crm__trigger_website_crawl","summary":"Website-Analyse starten","description":"Reiht die automatische Website-Analyse für die Firma deines Unternehmens ein. Extrahiert werden laut Funktionsbeschreibung Impressumsdaten, Farben, Branche und ein Screenshot; die Ergebnisse stehen nicht in der Antwort, sondern werden später in den Firmendaten abgelegt. Die Funktion antwortet sofort mit `status: crawl_queued`, `company_id`, `company_name`, der verwendeten `url` und einer Meldung. Gibst du keine `url` an, wird die in der Firma hinterlegte Domain bzw. URL verwendet; fehlt auch diese, kommt der Fehler „Keine URL für Company vorhanden. Bitte eine URL angeben.“ Fehlt das Protokoll, wird `https://` ergänzt. Ist der Analysedienst nicht verfügbar, meldet die Funktion dies als Fehler.\n\n**Gut zu wissen**\n\n- Die Firma bestimmt der Server aus deinem Schlüssel; du kannst keine andere Firma angeben.\n- Die Analyse läuft asynchron – die Antwort bestätigt nur das Einreihen.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/crm/trigger_website_crawl \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"url\": \"www.example.com\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/crm/trigger_website_crawl\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"url\": \"www.example.com\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/crm/trigger_website_crawl\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"url\": \"www.example.com\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"Adresse der zu analysierenden Webseite, mit oder ohne `https://` (z. B. `www.example.com`). Optional; ohne Angabe wird die bei der Firma gespeicherte Domain bzw. URL genommen. Der Text wird getrimmt und HTML-codiert.","example":"www.example.com"}}},"example":{"url":"www.example.com"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"crm","werkzeug":"trigger_website_crawl","stufe":"schreiben","data":{"success":true,"data":{"company_id":489,"company_name":"Muster Werkzeugbau GmbH","url":"https://www.example.com","status":"crawl_queued","message":"Website-Analyse für https://www.example.com wurde gestartet. Impressum-Daten, Farben, Branche und Screenshot werden automatisch extrahiert."},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/arango/get_company_knowledge_graph":{"post":{"tags":["Wissensgraph"],"operationId":"arango__get_company_knowledge_graph","summary":"Überblick über den Wissensgraphen","description":"Zählt, wie viele Datensätze im Wissensgraphen deines Unternehmens von der Firma aus erreichbar sind, und gliedert sie nach Typ. Ausgehend vom Firmenknoten wird der Graph bis zur angegebenen Tiefe (1 bis 3, Standard 2) durchlaufen; berücksichtigt werden nur Knoten deines Unternehmens, private Knoten (z. B. private Dateien) bleiben ausgeschlossen. Die Antwort enthält `companyId`, `totalNodes` (Zahl der erreichten Knoten), `nodesByType` (Typname → Anzahl, absteigend) und `company` (Name und Domain des Firmenknotens, sonst `null`). Es werden keine einzelnen Datensätze geliefert; dafür nutzt du `search_graph`, `get_entity_context` oder `find_connections`.\n\n**Gut zu wissen**\n\n- Größere Tiefen finden mehr Knoten, brauchen aber länger. Für eine schnelle Übersicht reicht meist `get_company_summary`.\n- Fehler (z. B. nicht erreichbare Graph-Datenbank) kommen als HTTP 200 mit `success: false` und `error: \"Graph-Abfrage fehlgeschlagen\"`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/arango/get_company_knowledge_graph \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"depth\": 2\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/arango/get_company_knowledge_graph\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"depth\": 2,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/arango/get_company_knowledge_graph\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"depth\": 2\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"depth":{"type":"integer","description":"Traversierungstiefe als Ganzzahl von 1 bis 3, Standard 2. Werte außerhalb des Bereichs werden auf 1 bzw. 3 begrenzt, 0 oder nicht angegeben ergibt 2. Tiefe 1 zählt nur direkte Nachbarn der Firma, höhere Werte auch deren Nachbarn.","example":2}}},"example":{"depth":2}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"arango","werkzeug":"get_company_knowledge_graph","stufe":"lesen","data":{"success":true,"data":{"companyId":489,"totalNodes":412,"nodesByType":{"Lead":150,"Appointment":96,"Book":12,"Offer":9,"Member":4},"company":{"name":"Muster Werkzeugbau GmbH","domain":"www.example.com"}},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/arango/get_company_summary":{"post":{"tags":["Wissensgraph"],"operationId":"arango__get_company_summary","summary":"Kompakte Firmenübersicht mit Zählungen","description":"Liefert eine kompakte Bestandsübersicht deines Wissensgraphen: wie viele Dokumente es insgesamt gibt, wie viele Verbindungen (Kanten) und wie sich die Dokumente auf die Sammlungen verteilen. Die Antwort enthält `companyId`, `totalDocuments`, `totalEdges` und `collections` (Sammlungsname → Anzahl, nach Anzahl absteigend sortiert; Sammlungen ohne Einträge fehlen). Typische Sammlungen sind leads, appointments, books, podcasts, courses, offers oder testimonials. Die Funktion hat keine Parameter und ist ein guter erster Aufruf, um zu sehen, in welchen Sammlungen für `search_graph` überhaupt Daten liegen. Die Firmensammlung selbst wird nicht mitgezählt.\n\n**Gut zu wissen**\n\n- Die Namen in `collections` sind genau die Werte, die du in `search_graph` als `collection` verwenden kannst.\n- Zählungen beziehen sich auf den Stand der Graph-Datenbank, die aus den Plattformdaten gespiegelt wird; sehr frische Änderungen können noch fehlen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/arango/get_company_summary \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/arango/get_company_summary\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/arango/get_company_summary\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"arango","werkzeug":"get_company_summary","stufe":"lesen","data":{"success":true,"data":{"companyId":489,"totalDocuments":523,"totalEdges":1380,"collections":{"leads":150,"appointments":96,"calls":80,"books":12,"offers":9}},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/arango/find_connections":{"post":{"tags":["Wissensgraph"],"operationId":"arango__find_connections","summary":"Kürzeste Verbindung zwischen zwei Datensätzen","description":"Sucht im Wissensgraphen deines Unternehmens die kürzeste Verbindung zwischen zwei Datensätzen, zum Beispiel „Wie hängt Lead X mit Buch Y zusammen?“. Beide Datensätze werden über Sammlung und Schlüssel angegeben; sie werden zu den Kennungen `sammlung/schlüssel` zusammengesetzt. Der Pfad darf höchstens 4 Schritte lang sein und läuft nur über Knoten deines Unternehmens. Die Antwort enthält `from`, `to`, `path` (Liste der Knoten vom Start bis zum Ziel, jeweils mit `id` und `label`) und `pathLength` (Zahl der Schritte). Gibt es keine Verbindung innerhalb von 4 Schritten, ist `path` leer und `pathLength` 0. Ist einer der beiden Datensätze unbekannt oder gehört nicht zu deinem Unternehmen, kommt der Fehler „Datensatz nicht gefunden“.\n\n**Gut zu wissen**\n\n- Die Schlüssel findest du über `search_graph` (Feld `_key`) oder `get_entity_context`.\n- Sind Start und Ziel derselbe Datensatz, enthält `path` nur diesen einen Knoten.\n- Private Knoten und Dateisammlungen (documents, document_chunks u. a.) sind ausgeschlossen und gelten als „nicht gefunden“.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/arango/find_connections \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"from_collection\": \"leads\",\n    \"from_key\": \"lead_4711\",\n    \"to_collection\": \"books\",\n    \"to_key\": \"book_67\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/arango/find_connections\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"from_collection\": \"leads\",\n        \"from_key\": \"lead_4711\",\n        \"to_collection\": \"books\",\n        \"to_key\": \"book_67\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/arango/find_connections\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"from_collection\": \"leads\",\n      \"from_key\": \"lead_4711\",\n      \"to_collection\": \"books\",\n      \"to_key\": \"book_67\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"from_collection":{"type":"string","description":"Name der Sammlung des Startdatensatzes in Kleinschreibung, z. B. `leads`, `books`, `members`. Die möglichen Namen liefert `get_company_summary`.","example":"leads"},"from_key":{"type":"string","description":"Schlüssel (`_key`) des Startdatensatzes innerhalb der Sammlung, z. B. `lead_12345`. Wird aus `search_graph` oder `get_entity_context` übernommen.","example":"lead_4711"},"to_collection":{"type":"string","description":"Name der Sammlung des Zieldatensatzes, z. B. `books`. Gleiche Schreibweise wie bei `from_collection`.","example":"books"},"to_key":{"type":"string","description":"Schlüssel (`_key`) des Zieldatensatzes, z. B. `book_67890`. Der Datensatz muss zu deinem Unternehmen gehören.","example":"book_67"}},"required":["from_collection","from_key","to_collection","to_key"]},"example":{"from_collection":"leads","from_key":"lead_4711","to_collection":"books","to_key":"book_67"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"arango","werkzeug":"find_connections","stufe":"lesen","data":{"success":true,"data":{"from":"leads/lead_4711","to":"books/book_67","path":[{"id":"leads/lead_4711","label":"Mira"},{"id":"members/member_321","label":"Jonas"},{"id":"books/book_67","label":"Werkzeugbau im Wandel"}],"pathLength":2},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/arango/get_entity_context":{"post":{"tags":["Wissensgraph"],"operationId":"arango__get_entity_context","summary":"Nachbarn eines Datensatzes anzeigen","description":"Zeigt, womit ein Datensatz im Wissensgraphen deines Unternehmens verbunden ist – etwa alle Leads eines Mitglieds oder alle Kapitel eines Buches. Ausgehend vom Datensatz (`sammlung/schlüssel`) werden die verbundenen Knoten bis zur gewählten Tiefe (Standard 1, höchstens 3) gesammelt, höchstens 200 Knoten insgesamt. Es kommen nur Knoten deines Unternehmens zurück. Die Antwort enthält `entity` (Kennung des Startpunkts), `totalNeighbors` und `neighborsByType`: Objekt, das je Typ (z. B. `Lead`, `Appointment`) eine Liste mit `key`, `name` (Anzeigename) und `edge` (Name der Verbindungsart) enthält. Ist der Startpunkt unbekannt oder gehört nicht zu deinem Unternehmen, kommt der Fehler „Datensatz nicht gefunden“.\n\n**Gut zu wissen**\n\n- Mit den gelieferten `key`-Werten kannst du weiter navigieren oder `find_connections` aufrufen.\n- Private Knoten und Dateisammlungen erscheinen nie in den Nachbarn.\n- Ein Startpunkt ohne Verbindungen liefert `totalNeighbors: 0` und ein leeres `neighborsByType`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/arango/get_entity_context \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"collection\": \"members\",\n    \"key\": \"member_321\",\n    \"depth\": 1\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/arango/get_entity_context\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"collection\": \"members\",\n        \"key\": \"member_321\",\n        \"depth\": 1,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/arango/get_entity_context\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"collection\": \"members\",\n      \"key\": \"member_321\",\n      \"depth\": 1\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"collection":{"type":"string","description":"Name der Sammlung des Startdatensatzes in Kleinschreibung, z. B. `members`, `books`, `leads`. Die vorhandenen Sammlungen siehst du in `get_company_summary`.","example":"members"},"key":{"type":"string","description":"Schlüssel (`_key`) des Startdatensatzes, z. B. `member_123`. Erlaubt sind Buchstaben, Ziffern und einige Sonderzeichen wie `_`, `-` und `:`.","example":"member_321"},"depth":{"type":"integer","description":"Traversierungstiefe, Ganzzahl bis 3, Standard 1. Tiefe 1 liefert direkte Nachbarn, 2 auch deren Nachbarn usw. Mit der Tiefe wächst die Treffermenge schnell; gedeckelt bei 200 Knoten.","example":1}},"required":["collection","key"]},"example":{"collection":"members","key":"member_321","depth":1}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"arango","werkzeug":"get_entity_context","stufe":"lesen","data":{"success":true,"data":{"entity":"members/member_321","totalNeighbors":3,"neighborsByType":{"Lead":[{"key":"lead_4711","name":"Mira","edge":"member_has_lead"},{"key":"lead_4698","name":"Jonas","edge":"member_has_lead"}],"Book":[{"key":"book_67","name":"Werkzeugbau im Wandel","edge":"member_has_book"}]}},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/arango/search_graph":{"post":{"tags":["Wissensgraph"],"operationId":"arango__search_graph","summary":"Datensätze einer Sammlung filtern","description":"Durchsucht gezielt eine Sammlung des Wissensgraphen nach Bedingungen – immer nur Daten deines Unternehmens, private Datensätze ausgenommen. Du gibst die Sammlung an und bis zu 8 Filter (alle müssen zutreffen), optional die Rückgabefelder, ein Sortierfeld und die Anzahl. Ohne `fields` kommen die Standardfelder `_key`, `_className`, `name`, `firstname`, `lastname`, `title`, `email`, `status` und `_modificationDate` (soweit vorhanden). Die Antwort enthält `count` (alle Treffer), `shown` (zurückgegebene Treffer) und `data` (Liste der Datensätze). Standardmäßig kommen 25 Treffer, höchstens 50. Für Zusammenhänge zwischen Datensätzen nutzt du `get_entity_context` oder `find_connections`.\n\n**Gut zu wissen**\n\n- Nicht durchsuchbar sind `platform_credentials`, `edgeservers`, `emails` sowie die Dateisammlungen (documents, document_chunks, document_folders, document_concepts); eine unbekannte Sammlung ergibt den Fehler „Unbekannte Collection: …“.\n- Feldnamen mit Teilen wie pass, secret, token, apikey, credential, hash, salt oder private werden als Filter-, Sortier- und Rückgabefeld abgelehnt bzw. ausgelassen.\n- Ohne `sort_field` ist die Reihenfolge nicht festgelegt. `sort_dir` gilt nur zusammen mit `sort_field`.\n- Fehlerhafte Angaben (Feldname, Operator, Filterwert) melden einen Klartext-Fehler in `error`.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/arango/search_graph \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"collection\": \"leads\",\n    \"filters\": [\n        {\n            \"field\": \"status\",\n            \"op\": \"==\",\n            \"value\": \"won\"\n        },\n        {\n            \"field\": \"email\",\n            \"op\": \"contains\",\n            \"value\": \"example.com\"\n        }\n    ],\n    \"fields\": [\n        \"firstname\",\n        \"lastname\",\n        \"email\",\n        \"status\"\n    ],\n    \"sort_field\": \"_modificationDate\",\n    \"sort_dir\": \"DESC\",\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/arango/search_graph\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"collection\": \"leads\",\n        \"filters\": [\n            {\n                \"field\": \"status\",\n                \"op\": \"==\",\n                \"value\": \"won\",\n            },\n            {\n                \"field\": \"email\",\n                \"op\": \"contains\",\n                \"value\": \"example.com\",\n            },\n        ],\n        \"fields\": [\n            \"firstname\",\n            \"lastname\",\n            \"email\",\n            \"status\",\n        ],\n        \"sort_field\": \"_modificationDate\",\n        \"sort_dir\": \"DESC\",\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/arango/search_graph\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"collection\": \"leads\",\n      \"filters\": [\n          {\n              \"field\": \"status\",\n              \"op\": \"==\",\n              \"value\": \"won\"\n          },\n          {\n              \"field\": \"email\",\n              \"op\": \"contains\",\n              \"value\": \"example.com\"\n          }\n      ],\n      \"fields\": [\n          \"firstname\",\n          \"lastname\",\n          \"email\",\n          \"status\"\n      ],\n      \"sort_field\": \"_modificationDate\",\n      \"sort_dir\": \"DESC\",\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"collection":{"type":"string","description":"Name der Sammlung in Kleinschreibung, z. B. `leads`, `books`, `offers`, `testimonials`, `interviews`, `appointments`, `podcasts`, `courses`. Welche Sammlungen bei dir Daten haben, zeigt `get_company_summary`.","example":"leads"},"filters":{"type":"array","description":"Liste von Bedingungen, höchstens 8 (weitere werden ignoriert); alle müssen zutreffen. Jede Bedingung ist ein Objekt mit `field` (Feldname, nur Buchstaben, Ziffern, Unterstrich, höchstens 64 Zeichen) und `op`; `value` ist bei Vergleichen Pflicht. Ohne Filter werden alle Datensätze der Sammlung berücksichtigt. Erlaubte Operatoren: `==`, `!=`, `<`, `<=`, `>`, `>=`, `contains` (Teilstring ohne Beachtung der Groß-/Kleinschreibung), `exists` (Feld ist nicht leer) und `missing` (Feld fehlt oder ist leer). Bei `exists`/`missing` entfällt `value`; sonst muss es Text, Zahl oder Wahrheitswert sein.","items":{"type":"object","properties":{"field":{"type":"string","description":"Feldname, z.B. status, name, email"},"op":{"type":"string","enum":["==","!=","<","<=",">",">=","contains","exists","missing"]},"value":{"type":["string","number","boolean"],"description":"Vergleichswert (bei exists/missing weglassen)"}},"required":["field","op"]},"example":[{"field":"status","op":"==","value":"won"},{"field":"email","op":"contains","value":"example.com"}]},"fields":{"type":"array","items":{"type":"string"},"description":"Liste der Feldnamen, die je Treffer zurückgegeben werden, höchstens 20. `_key` und `_className` kommen immer mit. Fehlt die Angabe, werden Standardfelder geliefert (name, firstname, lastname, title, email, status, _modificationDate).","example":["firstname","lastname","email","status"]},"sort_field":{"type":"string","description":"Feldname, nach dem sortiert wird, z. B. `_modificationDate` für die zuletzt geänderten Datensätze zuerst. Optional; muss ein gültiger Feldname sein.","example":"_modificationDate"},"sort_dir":{"type":"string","enum":["ASC","DESC"],"description":"Sortierrichtung `ASC` (aufsteigend) oder `DESC` (absteigend, Standard). Wirkt nur, wenn `sort_field` gesetzt ist.","example":"DESC"},"limit":{"type":"integer","description":"Maximale Zahl der zurückgegebenen Treffer, Ganzzahl. Standard 25, größter Wert 50; kleinere Werte als 1 ergeben den Standard. `count` in der Antwort nennt trotzdem die Gesamtzahl.","example":10}},"required":["collection"]},"example":{"collection":"leads","filters":[{"field":"status","op":"==","value":"won"},{"field":"email","op":"contains","value":"example.com"}],"fields":["firstname","lastname","email","status"],"sort_field":"_modificationDate","sort_dir":"DESC","limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"arango","werkzeug":"search_graph","stufe":"lesen","data":{"success":true,"data":{"count":14,"shown":2,"data":[{"_key":"lead_4711","_className":"Lead","firstname":"Mira","lastname":"Beispiel","email":"mira.beispiel@example.com","status":"won"},{"_key":"lead_4698","_className":"Lead","firstname":"Jonas","lastname":"Muster","email":"jonas.muster@example.com","status":"won"}]},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/arango/get_activity_timeline":{"post":{"tags":["Wissensgraph"],"operationId":"arango__get_activity_timeline","summary":"Letzte Änderungen im Unternehmen","description":"Liefert eine chronologische Liste der zuletzt geänderten Datensätze deines Unternehmens über mehrere Typen hinweg. Berücksichtigt werden die Sammlungen leads, books, podcasts, interviews, reflections, courses, offers und testimonials. Aus jeder Sammlung werden die 5 zuletzt geänderten Datensätze geholt, alle zusammengeführt, nach Änderungszeitpunkt absteigend sortiert und auf `limit` gekürzt (Standard 20, höchstens 50; wegen der Vorauswahl gibt es insgesamt höchstens 40 Einträge). Die Antwort enthält `companyId` und `activities`; jeder Eintrag hat `_key`, `_className`, `name` (Name, Vorname oder Titel), `modified` (Änderungszeitpunkt) und `collection`. Sammlungen, die nicht abgefragt werden können, werden übersprungen.\n\n**Gut zu wissen**\n\n- Termine, Anrufe und weitere Sammlungen sind in der Zeitleiste nicht enthalten; dafür `search_graph` mit `sort_field: \"_modificationDate\"` nutzen.\n- Ein Limit über 40 liefert nicht mehr Einträge, weil nur je 5 pro Sammlung betrachtet werden.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/arango/get_activity_timeline \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"limit\": 10\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/arango/get_activity_timeline\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"limit\": 10,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/arango/get_activity_timeline\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"limit\": 10\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","description":"Anzahl der zurückgegebenen Einträge, Ganzzahl. Standard 20, größter Wert 50. Nicht angegeben oder 0 ergibt 20.","example":10}}},"example":{"limit":10}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"arango","werkzeug":"get_activity_timeline","stufe":"lesen","data":{"success":true,"data":{"companyId":489,"activities":[{"_key":"lead_4711","_className":"Lead","name":"Mira","modified":"2026-11-09T09:58:12+01:00","collection":"leads"},{"_key":"book_67","_className":"Book","name":"Werkzeugbau im Wandel","modified":"2026-11-08T17:20:45+01:00","collection":"books"}]},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/arango/wissen_suche":{"post":{"tags":["Wissensgraph"],"operationId":"arango__wissen_suche","summary":"Unternehmenswissen durchsuchen","description":"Sucht im Wissen deines Unternehmens und gibt die relevantesten Treffer mit Auszug, Quelle und Score zurück. Durchsucht werden (1) Stammartikel und Handbuch per Bedeutungssuche, soweit der Suchdienst verfügbar ist, (2) die strukturierten Unternehmensdaten – Angebote, Bücher, Kundenstimmen, Interviews und Storyfilter (Positionierung) – per Textsuche mit der gesamten Anfrage als Teilstring (fester Score 0,6), (3) als Ersatz Handbuchseiten, wenn sonst nichts gefunden wurde (Score 0,5), und zusätzlich (4) bis zu 3 Fundstellen aus deinen Dateien, soweit die Dateiablage für dein Unternehmen aktiv ist und du die Datei sehen darfst (firmenweite und eigene private Dateien). Die Treffer werden nach Score sortiert und auf `limit` gekürzt; Dateifunde kommen zusätzlich dazu. Jeder Treffer hat `collection`, `id`, `title`, `snippet` (höchstens 400 Zeichen), `score` und `source`.\n\n**Gut zu wissen**\n\n- Die Textsuche in den Unternehmensdaten verlangt, dass die gesamte Anfrage als Zeichenfolge im Text vorkommt. Kurze Stichworte treffen dort besser als ganze Sätze; die Bedeutungssuche deckt auch Umschreibungen ab.\n- Dateifunde erkennst du an `collection: \"dateien\"`; sie enthalten zusätzlich `datei_id`, `name`, `seite`, `abschnitt` und `link`.\n- Zeigt `count` 0, gibt es weder Wissensartikel noch Datenfunde noch Dateifunde zur Anfrage.\n- Die Anfrage wird getrimmt und HTML-codiert, bevor gesucht wird.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/arango/wissen_suche \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\": \"Garantie Werkzeugwartung\",\n    \"limit\": 5\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/arango/wissen_suche\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"query\": \"Garantie Werkzeugwartung\",\n        \"limit\": 5,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/arango/wissen_suche\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"query\": \"Garantie Werkzeugwartung\",\n      \"limit\": 5\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Suchanfrage in natürlicher Sprache oder als Stichwort, z. B. `Garantie Werkzeugwartung`. Pflichtangabe, nicht leer. Für die Bedeutungssuche sind ganze Fragen geeignet, für die Textsuche in den Unternehmensdaten kurze Begriffe.","example":"Garantie Werkzeugwartung"},"limit":{"type":"integer","description":"Maximale Anzahl der Treffer aus Wissensartikeln und Unternehmensdaten, Ganzzahl. Standard 5, größter Wert 15. Dateifunde (bis zu 3) kommen zusätzlich dazu.","example":5}},"required":["query"]},"example":{"query":"Garantie Werkzeugwartung","limit":5}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"arango","werkzeug":"wissen_suche","stufe":"lesen","data":{"success":true,"data":{"query":"Garantie Werkzeugwartung","count":2,"hits":[{"collection":"offers","id":"offer_88","title":"Wartungsvertrag Premium","snippet":"Inklusive jährlicher Werkzeugwartung mit 24 Monaten Garantie auf Ersatzteile.","score":0.6,"source":"offers"},{"collection":"dateien","quelle":"datei","id":312,"datei_id":312,"title":"Servicebedingungen 2026.pdf","name":"Servicebedingungen 2026.pdf","seite":3,"abschnitt":7,"snippet":"Die Garantie beginnt mit dem Tag der Übergabe …","link":"/api/dateien/312/inhalt","score":0.812,"source":"datei"}]},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/arango/wissen_graph":{"post":{"tags":["Wissensgraph"],"operationId":"arango__wissen_graph","summary":"Verknüpfte Inhalte zu einem Dokument","description":"Folgt ausgehend von einem Dokument den Verknüpfungen im Wissensgraphen deines Unternehmens und liefert alle verbundenen Inhalte, etwa verwandte Handbuchseiten, Verantwortliche, Interviews oder Regeln. Das Startdokument gibst du mit Sammlung und Schlüssel an, die du aus einem Treffer von `wissen_suche` oder aus `search_graph` übernimmst. Die Tiefe beträgt standardmäßig 2 (höchstens 3), höchstens 200 Knoten werden gesammelt, ausschließlich aus deinem Unternehmen; private Knoten und Dateisammlungen fehlen. Die Antwort enthält `origin` (Kennung des Starts), `totalLinked` und `linked`, eine Liste mit `collection`, `key`, `label` (Anzeigename), `type` (Typ des Datensatzes) und `edge` (Verbindungsart). Unbekannter Start: Fehler „Dokument nicht gefunden“.\n\n**Gut zu wissen**\n\n- Funktioniert wie `get_entity_context`, gruppiert die Ergebnisse aber nicht nach Typ, sondern liefert eine flache Liste.\n- Die `id` eines Treffers aus `wissen_suche` ist der Schlüssel; die Sammlung steht im Feld `collection` des Treffers.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/arango/wissen_graph \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"collection\": \"handbook_pages\",\n    \"key\": \"handbook_page_204\",\n    \"depth\": 2\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/arango/wissen_graph\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"collection\": \"handbook_pages\",\n        \"key\": \"handbook_page_204\",\n        \"depth\": 2,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/arango/wissen_graph\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"collection\": \"handbook_pages\",\n      \"key\": \"handbook_page_204\",\n      \"depth\": 2\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"collection":{"type":"string","description":"Sammlung des Ausgangsdokuments in Kleinschreibung, z. B. `stories` oder `handbook_pages`. Bei Treffern aus `wissen_suche` steht der Wert in `collection`; Sammlungsnamen mit Daten liefert `get_company_summary`.","example":"handbook_pages"},"key":{"type":"string","description":"Schlüssel des Ausgangsdokuments (`_key`) im Graph-Format, z. B. `handbook_page_204`. Bei Treffern aus `wissen_suche` steht er in `id`.","example":"handbook_page_204"},"depth":{"type":"integer","description":"Tiefe der Verknüpfungssuche als Ganzzahl bis 3, Standard 2. Tiefe 1 liefert nur direkt verbundene Inhalte; größere Werte liefern deutlich mehr Knoten (gedeckelt bei 200).","example":2}},"required":["collection","key"]},"example":{"collection":"handbook_pages","key":"handbook_page_204","depth":2}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"arango","werkzeug":"wissen_graph","stufe":"lesen","data":{"success":true,"data":{"origin":"handbook_pages/handbook_page_204","totalLinked":2,"linked":[{"collection":"handbook_categories","key":"handbook_category_12","label":"Service","type":"HandbookCategory","edge":"page_in_category"},{"collection":"members","key":"member_321","label":"Jonas","type":"Member","edge":"page_owned_by"}]},"message":null,"timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/business_model/get_business_model":{"post":{"tags":["Geschäftsmodell"],"operationId":"business_model__get_business_model","summary":"Geschäftsmodell lesen","description":"Liefert das aktuelle Geschäftsmodell deines Unternehmens. Gewählt wird das neueste Modell mit Status `active`; gibt es keines, das neueste Modell überhaupt (z. B. ein Entwurf). Die Antwort in `data` enthält `id`, `company_id`, `name`, `version`, `status` (`draft`, `under_review`, `active`, `archived`), `state`, `created_at` und `modified_at` (Unix-Sekunden) sowie vier Gruppen: `canvas` (die neun Canvas-Felder als Text), `limbic` (Typ, Kohärenz, emotionale Auslöser), `product_ladder` (sechs Stufen von Buch bis Lizenzierung plus Kohärenz), `performance` und `offer_profile`. Das `offer_profile` enthält Geschäftsart, Preismodell, `offer_components`, Standard-Abrechnungszyklus und -Laufzeit, `ai_hints`, `calculation_basis`, `story_defaults` und die Herkunft (`source_interview_ids`, `extracted_at`). Hat dein Unternehmen noch kein Modell, ist `data` `null` und `message` erklärt das.\n\n**Gut zu wissen**\n\n- Ohne Modell kommt `success: true` mit `data: null`, nicht ein Fehler.\n- Beträge in `calculation_basis` (Stundensatz, Preise, Kosten) sind in Cent.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/business_model/get_business_model \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/business_model/get_business_model\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/business_model/get_business_model\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"business_model","werkzeug":"get_business_model","stufe":"lesen","data":{"success":true,"data":{"id":701,"company_id":12345,"name":"Geschäftsmodell Beispiel GmbH","version":"1.0","status":"active","state":null,"created_at":1760250600,"modified_at":1762000000,"canvas":{"value_propositions":"Fertige Lösungen ohne Eigenaufwand","customer_segments":"Mittelständische Handwerksbetriebe","channels":"Empfehlung, Webseite","customer_relationships":"Persönlicher Ansprechpartner","revenue_streams":"Projektpauschalen, Monatsbeitrag","key_resources":"Fachpersonal","key_activities":"Beratung, Umsetzung","key_partnerships":"Softwarehersteller","cost_structure":"Personal, Lizenzen"},"limbic":{"primary_type":"Balance","secondary_type":"Dominanz","coherence_score":82,"emotional_triggers":{"sicherheit":"hoch"}},"product_ladder":{"level_1_book":null,"level_2_online_course":"Einführungskurs","level_3_group_coaching":null,"level_4_one_on_one":"Einzelberatung","level_5_done_for_you":"Komplettumsetzung","level_6_licensing":null,"coherence_score":70},"performance":{"metrics":[],"market_fit_score":null,"market_position":null},"offer_profile":{"business_type":"service","pricing_model":"Festpreis je Projekt plus monatliche Betreuung","offer_components":[{"name":"Einrichtung","role":"mandatory","cycle":"once","note":"Einmalige Projektpauschale"},{"name":"Monatliche Betreuung","role":"optional","cycle":"monthly","note":null}],"default_billing_cycle":"monthly","default_contract_months":"12","ai_hints":"[2026-10-12] Angebote immer mit einer Einrichtungspauschale beginnen.","source_interview_ids":[3310,3315],"extracted_at":"2026-10-12T08:30:00+02:00","revenue_model":"Projektgeschäft mit wiederkehrender Betreuung","pricing_strategy":"Wertbasiert, Festpreise","calculation_basis":{"hourly_rate":9500,"target_margin_pct":40,"notes":"Stundensatz netto","positions":[{"ref":null,"name":"Einrichtung","cycle":"once","price":190000,"price_min":null,"price_max":null,"cost":60000,"effort_hours_internal":8,"effort_hours_external":null,"margin_pct":null,"margin_warning":null,"derived":false,"source":null}]},"story_defaults":{"mentor":{"g_mentor_character":"Erfahren und ruhig","h_friendship":"Auf Augenhöhe","i_competence":["15 Jahre Branchenerfahrung"]},"plan":{"j_mentor_plan":"In drei Schritten zum Ergebnis","k_process_plan":["Gespräch","Umsetzung","Übergabe"],"l_security_plan":["Festpreis","Zufriedenheitsgarantie",""]},"motive":["Zeitgewinn"],"bild":{"briefing":""},"sprache":{"person_bezeichnung":"Mitarbeiter"}}}},"message":null,"timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/business_model/update_business_model":{"post":{"tags":["Geschäftsmodell"],"operationId":"business_model__update_business_model","summary":"Geschäftsmodell ändern","description":"Ändert Felder des aktuellen Geschäftsmodells. Alle Änderungen stehen im Objekt `data`; geändert wird nur, was du nennst, und die Änderung wird erst gespeichert, wenn **alle** Felder gültig sind. Erlaubte Felder: `name`, `description`, `business_type` (`rental`, `service`, `product_sales`, `subscription`, `project`, `mixed`), `pricing_model`, `offer_components`, `default_billing_cycle` (`once`, `monthly`, `yearly`), `default_contract_months`, `ai_hints`, die neun Canvas-Felder, die sechs Produktstufen `level_1_book` bis `level_6_licensing`, `revenue_model`, `pricing_strategy`, `calculation_basis` und `story_defaults`. Besonderheiten: `ai_hints` wird mit Datumsmarke **angehängt**, nie ersetzt; `offer_components`, die Positionen in `calculation_basis` und die Teilobjekte von `story_defaults` werden mit dem Bestand zusammengeführt (Positionen und Bausteine über ihren Namen, ohne Beachtung der Groß-/Kleinschreibung). Die Antwort nennt `id`, `status`, die Liste `changed` und das aktuelle `offer_profile`.\n\n**Gut zu wissen**\n\n- Gibt es für dein Unternehmen noch kein Geschäftsmodell, antwortet die Funktion mit einem Fehler; ein Modell muss zuerst vorhanden sein.\n- Ein archiviertes Modell ist nicht änderbar.\n- Unbekannte Feldnamen oder eine leere `data` ergeben einen Fehler („Ungültige Eingabe“); es wird dann nichts gespeichert.\n- Geldbeträge in `calculation_basis` gibst du in Cent an (`hourly_rate`, `price`, `cost`) oder mit den Euro-Entsprechungen `hourly_rate_euro`, `price_euro`, `cost_euro`; die Marge je Position wird berechnet, sobald Preis und Kosten oder Aufwand vorliegen.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/business_model/update_business_model \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"data\": {\n        \"default_billing_cycle\": \"monthly\",\n        \"ai_hints\": \"Bei Projekten über 5.000 € immer eine Anzahlung von 30 % vorsehen.\",\n        \"offer_components\": [\n            {\n                \"name\": \"Monatliche Betreuung\",\n                \"role\": \"optional\",\n                \"cycle\": \"monthly\",\n                \"note\": \"Ab dem zweiten Monat\"\n            }\n        ],\n        \"calculation_basis\": {\n            \"hourly_rate\": 9500,\n            \"positions\": [\n                {\n                    \"name\": \"Einrichtung\",\n                    \"cycle\": \"once\",\n                    \"price\": 190000,\n                    \"cost\": 60000\n                }\n            ]\n        }\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/business_model/update_business_model\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"data\": {\n            \"default_billing_cycle\": \"monthly\",\n            \"ai_hints\": \"Bei Projekten über 5.000 € immer eine Anzahlung von 30 % vorsehen.\",\n            \"offer_components\": [\n                {\n                    \"name\": \"Monatliche Betreuung\",\n                    \"role\": \"optional\",\n                    \"cycle\": \"monthly\",\n                    \"note\": \"Ab dem zweiten Monat\",\n                },\n            ],\n            \"calculation_basis\": {\n                \"hourly_rate\": 9500,\n                \"positions\": [\n                    {\n                        \"name\": \"Einrichtung\",\n                        \"cycle\": \"once\",\n                        \"price\": 190000,\n                        \"cost\": 60000,\n                    },\n                ],\n            },\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/business_model/update_business_model\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"data\": {\n          \"default_billing_cycle\": \"monthly\",\n          \"ai_hints\": \"Bei Projekten über 5.000 € immer eine Anzahlung von 30 % vorsehen.\",\n          \"offer_components\": [\n              {\n                  \"name\": \"Monatliche Betreuung\",\n                  \"role\": \"optional\",\n                  \"cycle\": \"monthly\",\n                  \"note\": \"Ab dem zweiten Monat\"\n              }\n          ],\n          \"calculation_basis\": {\n              \"hourly_rate\": 9500,\n              \"positions\": [\n                  {\n                      \"name\": \"Einrichtung\",\n                      \"cycle\": \"once\",\n                      \"price\": 190000,\n                      \"cost\": 60000\n                  }\n              ]\n          }\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"Objekt mit den zu ändernden Feldern. `offer_components`: Liste von `{name, role (mandatory|optional|alternative), cycle (once|monthly|yearly), note}`. `calculation_basis`: `{hourly_rate (Cent), target_margin_pct, notes, positions: [{ref (offer:<id>|product:<id>|article:<id>|null), name, cycle, price (Cent), cost (Cent), effort_hours_internal, effort_hours_external}]}`. `story_defaults`: `{mentor{g_mentor_character,h_friendship,i_competence[]}, plan{j_mentor_plan,k_process_plan[3],l_security_plan[3]}, motive[], bild{briefing}, sprache{person_bezeichnung}}`. `default_contract_months`: Ziffern und Kommas (z. B. `12` oder `12,24`). Texte werden getrimmt, leere Texte setzen das Feld auf leer.","example":{"default_billing_cycle":"monthly","ai_hints":"Bei Projekten über 5.000 € immer eine Anzahlung von 30 % vorsehen.","offer_components":[{"name":"Monatliche Betreuung","role":"optional","cycle":"monthly","note":"Ab dem zweiten Monat"}],"calculation_basis":{"hourly_rate":9500,"positions":[{"name":"Einrichtung","cycle":"once","price":190000,"cost":60000}]}}}},"required":["data"]},"example":{"data":{"default_billing_cycle":"monthly","ai_hints":"Bei Projekten über 5.000 € immer eine Anzahlung von 30 % vorsehen.","offer_components":[{"name":"Monatliche Betreuung","role":"optional","cycle":"monthly","note":"Ab dem zweiten Monat"}],"calculation_basis":{"hourly_rate":9500,"positions":[{"name":"Einrichtung","cycle":"once","price":190000,"cost":60000}]}}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"business_model","werkzeug":"update_business_model","stufe":"schreiben","data":{"success":true,"data":{"id":701,"status":"active","changed":["default_billing_cycle","ai_hints","offer_components","calculation_basis"],"offer_profile":{"business_type":"service","pricing_model":"Festpreis je Projekt plus monatliche Betreuung","offer_components":[{"name":"Einrichtung","role":"mandatory","cycle":"once","note":"Einmalige Projektpauschale"},{"name":"Monatliche Betreuung","role":"optional","cycle":"monthly","note":null}],"default_billing_cycle":"monthly","default_contract_months":"12","ai_hints":"[2026-10-12] Angebote immer mit einer Einrichtungspauschale beginnen.\n\n[2026-11-09] Bei Projekten über 5.000 € immer eine Anzahlung von 30 % vorsehen.","source_interview_ids":[3310,3315],"extracted_at":"2026-10-12T08:30:00+02:00","revenue_model":"Projektgeschäft mit wiederkehrender Betreuung","pricing_strategy":"Wertbasiert, Festpreise","calculation_basis":{"hourly_rate":9500,"target_margin_pct":40,"notes":"Stundensatz netto","positions":[{"ref":null,"name":"Einrichtung","cycle":"once","price":190000,"price_min":null,"price_max":null,"cost":60000,"effort_hours_internal":8,"effort_hours_external":null,"margin_pct":null,"margin_warning":null,"derived":false,"source":null}]},"story_defaults":{"mentor":{"g_mentor_character":"Erfahren und ruhig","h_friendship":"Auf Augenhöhe","i_competence":["15 Jahre Branchenerfahrung"]},"plan":{"j_mentor_plan":"In drei Schritten zum Ergebnis","k_process_plan":["Gespräch","Umsetzung","Übergabe"],"l_security_plan":["Festpreis","Zufriedenheitsgarantie",""]},"motive":["Zeitgewinn"],"bild":{"briefing":""},"sprache":{"person_bezeichnung":"Mitarbeiter"}}}},"message":"Geändert: default_billing_cycle, ai_hints, offer_components, calculation_basis. Sag dem Nutzer, welches Feld du wie geändert hast.","timestamp":"2026-11-09 10:15:42","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/products/search_products":{"post":{"tags":["Produkte"],"operationId":"products__search_products","summary":"Produkte suchen und filtern","description":"Sucht Produkte deines Unternehmens semantisch im Wissensgraph und/oder über harte Zahlenfilter. Mindestens `query` oder `filters` muss angegeben sein, sonst kommt ein Fehler. Mit `filters` werden nur Produkte geliefert, die **alle** Filter erfüllen; Produkte, die das Attribut nicht besitzen, fallen heraus. Ohne `query` werden die Treffer nach dem Attribut des ersten Filters absteigend sortiert, mit `query` nach semantischer Ähnlichkeit innerhalb der gefilterten Menge. `data` enthält `query`, `applied_filters`, `results`, `count` (gelieferte Zeilen), `total_matched` (alle Treffer) und `truncated` (`true`, wenn es mehr Treffer als `count` gibt). Jedes Ergebnis hat `id`, `title`, `vendor`, `sku`, `price`, `currency`, `status`, `similarity` (nur bei Textsuche), `attrs` (nur gesetzte Werte) und `technicalData` (Gruppen mit `label`, `value`, `unit`, auf rund 1.500 Zeichen gekürzt, Akku-Gruppen zuerst). Ohne Treffer enthält `data` zusätzlich `message`.\n\n**Gut zu wissen**\n\n- Einheiten der `attrs`: `akkulaufzeit_min` und `ladezeit_min` in Minuten, `gewicht_kg`, `flaechenleistung_m2h`, `arbeitsbreite_cm`, `steigung_pct`, `laerm_db`, `akkukapazitaet_wh`, `preis_eur`.\n- Höchstens 4 Filter werden berücksichtigt; ungültige Einträge werden übersprungen. Ist kein einziger Filter gültig, kommt ein Fehler statt einer ungefilterten Suche.\n- Bei `total_matched` > `count` zeigt `truncated: true` an, dass `limit` die Liste begrenzt.\n- Findet die Textsuche nichts, obwohl du Produkte hast, muss der Wissensgraph in der Produktübersicht aktualisiert werden (`hint: knowledge_graph_empty`).\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/products/search_products \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\": \"Reinigungsroboter für Lagerhallen\",\n    \"limit\": 5,\n    \"filters\": [\n        {\n            \"attr\": \"akkulaufzeit_min\",\n            \"op\": \"gte\",\n            \"value\": 180\n        },\n        {\n            \"attr\": \"preis_eur\",\n            \"op\": \"lte\",\n            \"value\": 15000\n        }\n    ]\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/products/search_products\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"query\": \"Reinigungsroboter für Lagerhallen\",\n        \"limit\": 5,\n        \"filters\": [\n            {\n                \"attr\": \"akkulaufzeit_min\",\n                \"op\": \"gte\",\n                \"value\": 180,\n            },\n            {\n                \"attr\": \"preis_eur\",\n                \"op\": \"lte\",\n                \"value\": 15000,\n            },\n        ],\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/products/search_products\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"query\": \"Reinigungsroboter für Lagerhallen\",\n      \"limit\": 5,\n      \"filters\": [\n          {\n              \"attr\": \"akkulaufzeit_min\",\n              \"op\": \"gte\",\n              \"value\": 180\n          },\n          {\n              \"attr\": \"preis_eur\",\n              \"op\": \"lte\",\n              \"value\": 15000\n          }\n      ]\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Suchtext, z. B. „Roboter mit langer Akkulaufzeit“. Optional, wenn `filters` gesetzt ist; dann bestimmt der Text nur die Reihenfolge der gefilterten Produkte.","example":"Reinigungsroboter für Lagerhallen"},"limit":{"type":"integer","description":"Maximale Anzahl gelieferter Treffer. Ganzzahl von 1 bis 20, Standard 12; Werte außerhalb werden auf diesen Bereich begrenzt.","example":5},"filters":{"type":"array","maxItems":4,"description":"Liste von bis zu 4 Zahlenfiltern, alle müssen erfüllt sein. Jeder Filter ist ein Objekt mit `attr` (`akkulaufzeit_min`, `akkukapazitaet_wh`, `ladezeit_min`, `gewicht_kg`, `flaechenleistung_m2h`, `arbeitsbreite_cm`, `steigung_pct`, `laerm_db`, `preis_eur`), `op` (`gte` ≥, `lte` ≤, `eq` =) und `value` (Zahl in der Einheit des Attributs).","items":{"type":"object","properties":{"attr":{"type":"string","enum":["akkulaufzeit_min","akkukapazitaet_wh","ladezeit_min","gewicht_kg","flaechenleistung_m2h","arbeitsbreite_cm","steigung_pct","laerm_db","preis_eur"]},"op":{"type":"string","enum":["gte","lte","eq"]},"value":{"type":"number"}},"required":["attr","op","value"]},"example":[{"attr":"akkulaufzeit_min","op":"gte","value":180},{"attr":"preis_eur","op":"lte","value":15000}]}}},"example":{"query":"Reinigungsroboter für Lagerhallen","limit":5,"filters":[{"attr":"akkulaufzeit_min","op":"gte","value":180},{"attr":"preis_eur","op":"lte","value":15000}]}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"products","werkzeug":"search_products","stufe":"lesen","data":{"success":true,"data":{"query":"Reinigungsroboter für Lagerhallen","applied_filters":[{"attr":"akkulaufzeit_min","op":"gte","value":180},{"attr":"preis_eur","op":"lte","value":15000}],"results":[{"id":9101,"title":"Reinigungsroboter RX-200 (Beispiel)","vendor":"Beispielwerk","sku":"RX-200","price":12490,"currency":"EUR","status":"published","similarity":0.874,"attrs":{"akkulaufzeit_min":240,"gewicht_kg":85.5,"flaechenleistung_m2h":1800,"preis_eur":12490},"technicalData":[{"group":"Akku","items":[{"label":"Laufzeit","value":"4","unit":"h"},{"label":"Ladezeit","value":"3","unit":"h"}]},{"group":"Maße","items":[{"label":"Gewicht","value":"85,5","unit":"kg"}]}]}],"count":1,"total_matched":3,"truncated":true}}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/sparring/get_content_ideas":{"post":{"tags":["Content-Ideen"],"operationId":"sparring__get_content_ideas","summary":"Content-Ideen auflisten","description":"Liefert Content-Ideen deiner Firma, absteigend nach Dringlichkeit sortiert. Ohne `status` werden archivierte Ideen ausgeblendet; mit `status` wird exakt auf diesen Wert gefiltert (auch `archived` ist möglich). Zuerst wird auf `limit` Ideen begrenzt (Standard 10, mindestens 1), erst danach werden `content_type` und `min_score` angewendet. Dadurch kann die Antwort weniger als `limit` Einträge enthalten, obwohl weitere passende Ideen existieren. Die Antwort enthält `items` und `total` (Zahl der gelieferten Einträge). Jede Idee hat `id`, `title`, `description`, `target_content_type`, `target_content_type_label`, `keyword`, `status`, `urgency`, `business_impact`, `composite_score`, `market_fit_score`, `suggested_angle`, `target_audience`, `limbic_type`, `keyword_data`, `is_duplicate`, `dedup_recommendation`, `production_status`, `production_id`, `needs_follow_up` und `created_at`.\n\n**Gut zu wissen**\n\n- `content_type` und `min_score` filtern erst nach der Begrenzung auf `limit`; erhöhe `limit`, wenn du mit Filtern zu wenige Treffer bekommst.\n- `urgency` und `business_impact` kommen als Text (z. B. \"0.7\"), `composite_score` als Zahl.\n- Scores, Winkel und Keyword-Daten sind nur gefüllt, wenn die Idee entsprechend angereichert wurde; sonst stehen dort 0 bzw. leere Werte.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/sparring/get_content_ideas \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"active\",\n    \"content_type\": \"podcast_episode\",\n    \"limit\": 20\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/sparring/get_content_ideas\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"active\",\n        \"content_type\": \"podcast_episode\",\n        \"limit\": 20,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/sparring/get_content_ideas\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"active\",\n      \"content_type\": \"podcast_episode\",\n      \"limit\": 20\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Optionaler Statusfilter, exakt verglichen: `active`, `in_progress` oder `archived`. Ohne Angabe werden alle Ideen außer archivierten geliefert.","example":"active"},"content_type":{"type":"string","description":"Optionaler Filter auf den Ziel-Typ: `podcast_episode`, `book_chapter`, `blog_post`, `social_media` oder `landingpage`. Wird nach der Begrenzung durch `limit` angewendet.","example":"podcast_episode"},"min_score":{"type":"number","description":"Optionale Mindestgrenze für den Composite-Score als Zahl zwischen 0.0 und 1.0. Ideen mit niedrigerem oder fehlendem Score (zählt als 0) werden aussortiert; die Prüfung erfolgt nach der Begrenzung durch `limit`."},"limit":{"type":"integer","description":"Maximale Zahl der zunächst geladenen Ideen als ganze Zahl, mindestens 1. Standard ist 10. Beachte, dass die übrigen Filter danach greifen und die Antwort kleiner ausfallen kann.","example":20}}},"example":{"status":"active","content_type":"podcast_episode","limit":20}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"sparring","werkzeug":"get_content_ideas","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":61120,"title":"Wie Handwerksbetriebe Nachfolger finden","description":"Aus dem Sparring vom 3. November: Nachfolge als Dauerthema.","target_content_type":"podcast_episode","target_content_type_label":"Podcast-Episode","keyword":"Betriebsnachfolge","status":"active","urgency":"0.8","business_impact":"0.7","composite_score":0.76,"market_fit_score":0.7,"suggested_angle":"Erfahrungsbericht statt Ratgeber","target_audience":"Inhaber kleiner Betriebe","limbic_type":"","keyword_data":null,"is_duplicate":false,"dedup_recommendation":"","production_status":"pending","production_id":null,"needs_follow_up":true,"created_at":"2026-11-03T15:20:11+01:00"}],"total":1},"message":null,"timestamp":"2026-11-09 11:15:09","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/sparring/get_idea_backlog":{"post":{"tags":["Content-Ideen"],"operationId":"sparring__get_idea_backlog","summary":"Beste Ideen (Backlog)","description":"Zeigt die Content-Ideen mit der höchsten Bewertung. Dafür werden die 50 dringlichsten nicht archivierten Ideen deiner Firma geladen, als Duplikat markierte Ideen entfernt und der Rest absteigend nach Composite-Score sortiert; anschließend werden die ersten `limit` Einträge (Standard 5, mindestens 1) zurückgegeben. Die Antwort enthält `backlog` (Einträge im selben Format wie bei `get_content_ideas`) und `total` (Zahl der gelieferten Einträge). Ideen, die nicht unter den 50 dringlichsten sind, kommen nie vor, auch wenn ihr Composite-Score höher wäre. Die Funktion eignet sich für die Frage „Was sind meine besten Content-Ideen?“. Zum Sichten mit Filtern nutzt du `get_content_ideas`.\n\n**Gut zu wissen**\n\n- Ideen mit Status `in_progress` sind enthalten; nur `archived` und Duplikate werden ausgeblendet.\n- Das Fenster von 50 Ideen ergibt sich aus der Dringlichkeit, nicht aus dem Score.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/sparring/get_idea_backlog \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"limit\": 3\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/sparring/get_idea_backlog\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"limit\": 3,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/sparring/get_idea_backlog\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"limit\": 3\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","description":"Anzahl der zurückgegebenen Ideen als ganze Zahl, mindestens 1. Standard ist 5. Mehr als 50 Ideen kann der Backlog nie enthalten.","example":3}}},"example":{"limit":3}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"sparring","werkzeug":"get_idea_backlog","stufe":"lesen","data":{"success":true,"data":{"backlog":[{"id":61120,"title":"Wie Handwerksbetriebe Nachfolger finden","description":"Aus dem Sparring vom 3. November: Nachfolge als Dauerthema.","target_content_type":"podcast_episode","target_content_type_label":"Podcast-Episode","keyword":"Betriebsnachfolge","status":"active","urgency":"0.8","business_impact":"0.7","composite_score":0.76,"market_fit_score":0.7,"suggested_angle":"Erfahrungsbericht statt Ratgeber","target_audience":"Inhaber kleiner Betriebe","limbic_type":"","keyword_data":null,"is_duplicate":false,"dedup_recommendation":"","production_status":"pending","production_id":null,"needs_follow_up":true,"created_at":"2026-11-03T15:20:11+01:00"}],"total":1},"message":null,"timestamp":"2026-11-09 11:17:30","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/sparring/create_content_idea":{"post":{"tags":["Content-Ideen"],"operationId":"sparring__create_content_idea","summary":"Content-Idee anlegen","description":"Legt manuell eine neue Content-Idee für deine Firma an. Pflicht sind `title` und `content_type`. Die Idee startet mit Status `active`, Produktionsstand `pending` und dem Hinweis „braucht Nachverfolgung“ (`needs_follow_up: true`). `urgency` und `impact` werden auf den Bereich 0.0 bis 1.0 begrenzt; ohne Angabe gilt jeweils 0.5. Das Haupt-Keyword wird als `keyword` gespeichert. Falls die Anreicherung verfügbar ist, werden anschließend Keyword-Daten und eine Dubletten-Prüfung ergänzt – die Antwort zeigt dann die angereicherten Werte. Zurück kommt die vollständige Idee (Format wie bei `get_content_ideas`) mit der Meldung „Content-Idee erstellt“ und der neuen `id`.\n\n**Gut zu wissen**\n\n- `content_type` wird nicht gegen eine Liste geprüft; verwende die dokumentierten Werte, damit Filter und Beschriftung (`target_content_type_label`) greifen.\n- Texte werden HTML-maskiert gespeichert; `&` erscheint als `&amp;`.\n- Ein zweiter Aufruf mit gleichem Titel legt eine zweite Idee an.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/sparring/create_content_idea \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Wie Handwerksbetriebe Nachfolger finden\",\n    \"content_type\": \"podcast_episode\",\n    \"keyword\": \"Betriebsnachfolge\",\n    \"urgency\": 0.8\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/sparring/create_content_idea\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"title\": \"Wie Handwerksbetriebe Nachfolger finden\",\n        \"content_type\": \"podcast_episode\",\n        \"keyword\": \"Betriebsnachfolge\",\n        \"urgency\": 0.8,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/sparring/create_content_idea\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"title\": \"Wie Handwerksbetriebe Nachfolger finden\",\n      \"content_type\": \"podcast_episode\",\n      \"keyword\": \"Betriebsnachfolge\",\n      \"urgency\": 0.8\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Titel der Content-Idee als Text. Pflichtangabe.","example":"Wie Handwerksbetriebe Nachfolger finden"},"content_type":{"type":"string","description":"Ziel-Typ der Idee. Dokumentierte Werte: `podcast_episode`, `book_chapter`, `blog_post`, `social_media`, `landingpage`. Pflichtangabe; andere Werte werden zwar gespeichert, aber ohne sprechende Beschriftung ausgegeben.","example":"podcast_episode"},"description":{"type":"string","description":"Optionale Beschreibung oder Begründung der Idee. Ohne Angabe bleibt das Feld leer."},"keyword":{"type":"string","description":"Optionales Haupt-Keyword, auf das die Idee zielt. Es wird als `keyword` der Idee gespeichert und für die Anreicherung herangezogen.","example":"Betriebsnachfolge"},"target_audience":{"type":"string","description":"Optionale Zielgruppe, für die der Inhalt gedacht ist, z. B. „Inhaber kleiner Betriebe“. Wird im Feld `target_audience` ausgegeben."},"urgency":{"type":"number","description":"Optionale Dringlichkeit als Zahl von 0.0 (nicht dringend) bis 1.0 (sehr dringend). Werte außerhalb werden auf diesen Bereich begrenzt. Standard 0.5; bestimmt die Sortierung in `get_content_ideas`.","example":0.8},"impact":{"type":"number","description":"Optionaler erwarteter Geschäftsnutzen als Zahl von 0.0 bis 1.0. Werte außerhalb werden begrenzt. Standard 0.5. Wird in `business_impact` ausgegeben."}},"required":["title","content_type"]},"example":{"title":"Wie Handwerksbetriebe Nachfolger finden","content_type":"podcast_episode","keyword":"Betriebsnachfolge","urgency":0.8}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"sparring","werkzeug":"create_content_idea","stufe":"schreiben","data":{"success":true,"data":{"id":61120,"title":"Wie Handwerksbetriebe Nachfolger finden","description":"","target_content_type":"podcast_episode","target_content_type_label":"Podcast-Episode","keyword":"Betriebsnachfolge","status":"active","urgency":"0.8","business_impact":"0.5","composite_score":0,"market_fit_score":0,"suggested_angle":"","target_audience":"","limbic_type":"","keyword_data":null,"is_duplicate":false,"dedup_recommendation":"","production_status":"pending","production_id":null,"needs_follow_up":true,"created_at":"2026-11-09T11:20:44+01:00"},"message":"Content-Idee erstellt","timestamp":"2026-11-09 11:20:44","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/sparring/approve_idea":{"post":{"tags":["Content-Ideen"],"operationId":"sparring__approve_idea","summary":"Content-Idee genehmigen","description":"Genehmigt eine Content-Idee für die Produktion. Die Funktion setzt den Produktionsstand auf `approved` (mit Zeitstempel der Genehmigung), den Status der Idee auf `in_progress` und entfernt die Markierung „braucht Nachverfolgung“. Zurück kommt die aktualisierte Idee (Format wie bei `get_content_ideas`) mit der Meldung „Content-Idee genehmigt“. Die Funktion startet selbst keine Produktion und erzeugt keine Inhalte. Die Idee muss zu deiner Firma gehören und tatsächlich eine Content-Idee sein; sonst kommt `success: false` mit „Content-Idee nicht gefunden“, „Objekt ist keine Content-Idee“ oder „Kein Zugriff auf diese Content-Idee“.\n\n**Gut zu wissen**\n\n- Es wird nicht geprüft, ob die Idee bereits genehmigt oder archiviert ist – ein erneuter Aufruf setzt sie wieder auf `in_progress`.\n- `production_status` in der Antwort ist nach der Genehmigung `approved`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/sparring/approve_idea \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"idea_id\": 61120\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/sparring/approve_idea\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"idea_id\": 61120,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/sparring/approve_idea\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"idea_id\": 61120\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"idea_id":{"type":"integer","description":"Numerische ID der Content-Idee, wie sie `get_content_ideas`, `get_idea_backlog` oder `create_content_idea` als `id` liefern. Pflichtangabe.","example":61120}},"required":["idea_id"]},"example":{"idea_id":61120}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"sparring","werkzeug":"approve_idea","stufe":"schreiben","data":{"success":true,"data":{"id":61120,"title":"Wie Handwerksbetriebe Nachfolger finden","description":"","target_content_type":"podcast_episode","target_content_type_label":"Podcast-Episode","keyword":"Betriebsnachfolge","status":"in_progress","urgency":"0.8","business_impact":"0.5","composite_score":0,"market_fit_score":0,"suggested_angle":"","target_audience":"","limbic_type":"","keyword_data":null,"is_duplicate":false,"dedup_recommendation":"","production_status":"approved","production_id":null,"needs_follow_up":false,"created_at":"2026-11-09T11:20:44+01:00"},"message":"Content-Idee genehmigt","timestamp":"2026-11-09 11:25:03","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/sparring/archive_idea":{"post":{"tags":["Content-Ideen"],"operationId":"sparring__archive_idea","summary":"Content-Idee archivieren","description":"Legt eine Content-Idee ab. Der Status wird auf `archived` gesetzt und die Markierung „braucht Nachverfolgung“ entfernt. Mit `reason` speicherst du eine Begründung, die zusammen mit der Idee abgelegt wird. Archivierte Ideen erscheinen in `get_content_ideas` nur noch, wenn du `status: \"archived\"` angibst, und fehlen im `get_idea_backlog`. Die Antwort enthält keine Daten (`data` ist null), nur `success: true` und die Meldung „Content-Idee archiviert“. Die Idee muss zu deiner Firma gehören; sonst kommt `success: false` mit einer entsprechenden Fehlermeldung. Es gibt keine Funktion zum Wiederherstellen.\n\n**Gut zu wissen**\n\n- Die Begründung (`reason`) wird gespeichert, aber von keiner Lese-Funktion dieses Bereichs ausgegeben.\n- Archivieren ist per Plattform-API nicht umkehrbar.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/sparring/archive_idea \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"idea_id\": 61120,\n    \"reason\": \"Thema ist bereits in Episode 2 behandelt worden.\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/sparring/archive_idea\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"idea_id\": 61120,\n        \"reason\": \"Thema ist bereits in Episode 2 behandelt worden.\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/sparring/archive_idea\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"idea_id\": 61120,\n      \"reason\": \"Thema ist bereits in Episode 2 behandelt worden.\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"idea_id":{"type":"integer","description":"Numerische ID der Content-Idee, die archiviert werden soll. Pflichtangabe; die Idee muss zu deiner Firma gehören.","example":61120},"reason":{"type":"string","description":"Optionale Begründung als Text, z. B. „Thema inzwischen bereits veröffentlicht“. Wird bei der Idee abgelegt und HTML-maskiert gespeichert. Leere Angaben werden ignoriert.","example":"Thema ist bereits in Episode 2 behandelt worden."}},"required":["idea_id"]},"example":{"idea_id":61120,"reason":"Thema ist bereits in Episode 2 behandelt worden."}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"sparring","werkzeug":"archive_idea","stufe":"schreiben","data":{"success":true,"data":null,"message":"Content-Idee archiviert","timestamp":"2026-11-09 11:28:51","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/canvas_oeffnen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__canvas_oeffnen","summary":"Canvas anlegen oder weiterbauen","description":"Legt einen neuen Canvas an, also den Entwurf einer Anwendung aus festen Bausteinen, und liefert dessen Kurzstand zurück. Ohne Chat-Fenster, wie es bei der API immer der Fall ist, gilt: Hast du in den letzten 6 Stunden schon einen eigenen, noch unveröffentlichten Entwurf ohne Chat-Bindung bearbeitet, bekommst du genau diesen zurück statt eines neuen; `titel` wird dann ignoriert. Die Antwort enthält `app_id`, `name`, `revision` (0 = leer, jede Änderung erhöht um 1), `bausteine` (Kurzliste der obersten Ebene mit `id`, `typ`, `titel`) und einen Satz in `hinweis`. Anschließend füllst du den Canvas mit `canvas_setzen`.\n\n**Gut zu wissen**\n\n- Im Code wird zusätzlich der Schalter `neu: true` im Körper gelesen, der auch dann einen frischen Canvas erzwingt, wenn ein junger Entwurf existiert; er steht nicht im veröffentlichten Schema.\n- Eine Anwendung ist erst nach dem Veröffentlichen durch einen Menschen in der Oberfläche für andere nutzbar.\n- Der Name wird auf 160 Zeichen gekürzt; ein leerer Titel wird zu „Neue Anwendung“, die Beschreibung wird auf 600 Zeichen gekürzt.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/canvas_oeffnen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"titel\": \"Inbetriebnahme Reinigungsroboter\",\n    \"beschreibung\": \"Checkliste für die Übergabe beim Kunden\",\n    \"kontext_typ\": \"geraet\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/canvas_oeffnen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"titel\": \"Inbetriebnahme Reinigungsroboter\",\n        \"beschreibung\": \"Checkliste für die Übergabe beim Kunden\",\n        \"kontext_typ\": \"geraet\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/canvas_oeffnen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"titel\": \"Inbetriebnahme Reinigungsroboter\",\n      \"beschreibung\": \"Checkliste für die Übergabe beim Kunden\",\n      \"kontext_typ\": \"geraet\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"titel":{"type":"string","description":"Name der Anwendung, z. B. „Inbetriebnahme Reinigungsroboter“. Wird auf 160 Zeichen gekürzt; ein leerer Text ergibt „Neue Anwendung“. Wird ignoriert, wenn die Funktion einen jungen vorhandenen Entwurf zurückgibt.","example":"Inbetriebnahme Reinigungsroboter"},"beschreibung":{"type":"string","description":"Optionale Beschreibung in ein bis zwei Sätzen, was die Anwendung leistet. Wird auf 600 Zeichen gekürzt und später in der Anwendungsliste angezeigt.","example":"Checkliste für die Übergabe beim Kunden"},"kontext_typ":{"type":"string","enum":["geraet","lead"],"description":"Optional: Für welchen Datensatz die Anwendung gestartet wird. Erlaubt sind `geraet` (ein Gerät) und `lead` (ein Lead). Dann stehen beim Ausführen die Daten dieses Objekts über Platzhalter wie `{{kontext.…}}` bereit. Ohne Angabe hat die Anwendung keinen Kontext.","example":"geraet"}},"required":["titel"]},"example":{"titel":"Inbetriebnahme Reinigungsroboter","beschreibung":"Checkliste für die Übergabe beim Kunden","kontext_typ":"geraet"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"canvas_oeffnen","stufe":"schreiben","data":{"success":true,"app_id":4711,"name":"Inbetriebnahme Reinigungsroboter","revision":0,"bausteine":[],"hinweis":"Canvas offen — jetzt mit canvas_setzen füllen."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/canvas_lesen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__canvas_lesen","summary":"Canvas lesen","description":"Liest den aktuellen Entwurf einer Anwendung vollständig, also alle Bausteine mit allen Feldern. Die Antwort enthält `app_id`, `name`, `revision` (Stand des Entwurfs), `version` (Nummer der zuletzt veröffentlichten Version, `null`, wenn noch nie veröffentlicht), `unveroeffentlicht` (`true`, wenn der Entwurf neuer ist als die veröffentlichte Version) und `doc` mit der Liste `bausteine`. Als Verwalter siehst du immer den Entwurf; wer die Anwendung nur benutzen darf, bekommt die veröffentlichte Fassung, sofern sie für ihn freigegeben ist. Nutze die Funktion vor gezielten Änderungen, um IDs und Reihenfolge der Bausteine zu kennen.\n\n**Gut zu wissen**\n\n- Obwohl die Funktion nur liest, steht sie wie alle Baukasten-Funktionen auf der Rechte-Stufe `schreiben`.\n- Verschachtelte Bausteine liegen in `bausteine` (Abschnitt), `links`/`rechts` (Spalten) und `schritte[].bausteine` (Wizard).\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/canvas_lesen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"app_id\": 4711\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/canvas_lesen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"app_id\": 4711,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/canvas_lesen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"app_id\": 4711\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"app_id":{"type":"integer","description":"ID der Anwendung (des Canvas) als ganze Zahl. Du bekommst sie aus `canvas_oeffnen` (Feld `app_id`) oder aus `anwendungen_liste` (Feld `id`). Bearbeiten darf die Anwendung nur ihr Ersteller oder ein Administrator der Firma; für eine fremde oder unbekannte ID kommt `success: false` mit einem Satz.","example":4711}},"required":["app_id"]},"example":{"app_id":4711}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"canvas_lesen","stufe":"lesen","data":{"success":true,"app_id":4711,"name":"Inbetriebnahme Reinigungsroboter","revision":2,"version":null,"unveroeffentlicht":true,"doc":{"bausteine":[{"id":"intro","typ":"text","titel":"Übergabe","inhalt":"Gehe die Punkte der Reihe nach durch."},{"id":"pruefung","typ":"checkliste","titel":"Prüfung vor Ort","punkte":[{"id":"strom","text":"Netzanschluss geprüft","pflicht":true}]}]}}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/canvas_setzen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__canvas_setzen","summary":"Alle Bausteine setzen","description":"Ersetzt sämtliche Bausteine des Canvas durch die übergebene Liste. Gedacht für den ersten Wurf; danach änderst du besser gezielt einzelne Bausteine. Das Dokument wird vollständig geprüft: unbekannte Felder fallen weg, Texte werden gekürzt, und bei Fehlern wird nichts gespeichert. Stattdessen kommt `success: false` mit einem Satz je Fehler in `error` (z. B. doppelte oder ungültige ID, fehlender Pflichtinhalt, nicht erlaubtes Werkzeug). Bei Erfolg liefert die Funktion `app_id`, `name`, die neue `revision`, eine Kurzliste `bausteine` (`id`, `typ`, `titel` der obersten Ebene) und `hinweis`. Höchstens 80 Bausteine insgesamt, höchstens zwei Verschachtelungsebenen.\n\n**Gut zu wissen**\n\n- Erlaubte Bausteintypen: text, hinweis, ablauf, tabelle, bild, checkliste, formular, schritte, liste, kennzahl, eintraege, aktion, abschnitt, spalten. Jeder Baustein braucht eine eindeutige `id` (a-z, 0-9, _, höchstens 40 Zeichen); `kontext`, `schritte`, `eingabe` und `werte` sind reserviert.\n- Datenquellen (`liste`, `kennzahl`) brauchen ein lesendes Werkzeug, Knöpfe (`aktion`) ein schreibendes mit Bestätigungssatz; Namen findest du mit `baukasten_werkzeuge`. Werkzeuge, die mit `delete_`, `remove_`, `ui_`, `canvas_`, `baukasten_` oder `anwendung` beginnen, sind gesperrt.\n- Bilder müssen eine https-Adresse oder ein Pfad aus der Mediathek der Anwendung sein (`/baukasten/company-<ID>/…`).\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/canvas_setzen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"app_id\": 4711,\n    \"bausteine\": [\n        {\n            \"id\": \"intro\",\n            \"typ\": \"text\",\n            \"titel\": \"Übergabe\",\n            \"inhalt\": \"Gehe die Punkte der Reihe nach durch.\"\n        },\n        {\n            \"id\": \"pruefung\",\n            \"typ\": \"checkliste\",\n            \"titel\": \"Prüfung vor Ort\",\n            \"punkte\": [\n                {\n                    \"id\": \"strom\",\n                    \"text\": \"Netzanschluss geprüft\",\n                    \"pflicht\": true\n                },\n                {\n                    \"id\": \"typenschild\",\n                    \"text\": \"Foto vom Typenschild\",\n                    \"foto\": true\n                }\n            ]\n        }\n    ]\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/canvas_setzen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"app_id\": 4711,\n        \"bausteine\": [\n            {\n                \"id\": \"intro\",\n                \"typ\": \"text\",\n                \"titel\": \"Übergabe\",\n                \"inhalt\": \"Gehe die Punkte der Reihe nach durch.\",\n            },\n            {\n                \"id\": \"pruefung\",\n                \"typ\": \"checkliste\",\n                \"titel\": \"Prüfung vor Ort\",\n                \"punkte\": [\n                    {\n                        \"id\": \"strom\",\n                        \"text\": \"Netzanschluss geprüft\",\n                        \"pflicht\": True,\n                    },\n                    {\n                        \"id\": \"typenschild\",\n                        \"text\": \"Foto vom Typenschild\",\n                        \"foto\": True,\n                    },\n                ],\n            },\n        ],\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/canvas_setzen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"app_id\": 4711,\n      \"bausteine\": [\n          {\n              \"id\": \"intro\",\n              \"typ\": \"text\",\n              \"titel\": \"Übergabe\",\n              \"inhalt\": \"Gehe die Punkte der Reihe nach durch.\"\n          },\n          {\n              \"id\": \"pruefung\",\n              \"typ\": \"checkliste\",\n              \"titel\": \"Prüfung vor Ort\",\n              \"punkte\": [\n                  {\n                      \"id\": \"strom\",\n                      \"text\": \"Netzanschluss geprüft\",\n                      \"pflicht\": true\n                  },\n                  {\n                      \"id\": \"typenschild\",\n                      \"text\": \"Foto vom Typenschild\",\n                      \"foto\": true\n                  }\n              ]\n          }\n      ]\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"app_id":{"type":"integer","description":"ID der Anwendung (des Canvas) als ganze Zahl. Du bekommst sie aus `canvas_oeffnen` (Feld `app_id`) oder aus `anwendungen_liste` (Feld `id`). Bearbeiten darf die Anwendung nur ihr Ersteller oder ein Administrator der Firma; für eine fremde oder unbekannte ID kommt `success: false` mit einem Satz.","example":4711},"bausteine":{"type":"array","items":{"type":"object","description":"Ein Baustein: {id, typ, …Felder des Typs}"},"description":"Vollständige Liste der Bausteine als Array von Objekten, jeweils `{id, typ, …Felder des Typs}`. Beispiele: `text {titel, inhalt (Markdown)}`, `hinweis {ton: info|tipp|warnung|pflicht, inhalt}`, `checkliste {punkte[{id, text, pflicht, foto, kommentar}]}`, `formular {felder[{id, label, art, optionen, pflicht}]}` mit `art` text, langtext, zahl, datum, auswahl, janein oder foto, `aktion {titel, werkzeug, parameter, bestaetigung}`. Optional hat jeder Baustein `sichtbar_wenn {wert, op, vergleich}`; erlaubte `op`: gleich, ungleich, leer, nicht_leer, angehakt, groesser, kleiner, enthaelt. Platzhalter: `{{kontext.…}}`, `{{<checkliste>.<punkt>}}`, `{{<formular>.<feld>}}`.","example":[{"id":"intro","typ":"text","titel":"Übergabe","inhalt":"Gehe die Punkte der Reihe nach durch."},{"id":"pruefung","typ":"checkliste","titel":"Prüfung vor Ort","punkte":[{"id":"strom","text":"Netzanschluss geprüft","pflicht":true},{"id":"typenschild","text":"Foto vom Typenschild","foto":true}]}]}},"required":["app_id","bausteine"]},"example":{"app_id":4711,"bausteine":[{"id":"intro","typ":"text","titel":"Übergabe","inhalt":"Gehe die Punkte der Reihe nach durch."},{"id":"pruefung","typ":"checkliste","titel":"Prüfung vor Ort","punkte":[{"id":"strom","text":"Netzanschluss geprüft","pflicht":true},{"id":"typenschild","text":"Foto vom Typenschild","foto":true}]}]}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"canvas_setzen","stufe":"schreiben","data":{"success":true,"app_id":4711,"name":"Inbetriebnahme Reinigungsroboter","revision":1,"bausteine":[{"id":"intro","typ":"text","titel":"Übergabe"},{"id":"pruefung","typ":"checkliste","titel":"Prüfung vor Ort"}],"hinweis":"Gespeichert — der Canvas zeigt es schon."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/canvas_baustein_einfuegen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__canvas_baustein_einfuegen","summary":"Baustein einfügen","description":"Fügt einen einzelnen Baustein in den Canvas ein, ohne die übrigen anzufassen. Die Position bestimmst du mit `nach`: eine Baustein-ID setzt ihn dahinter (auch innerhalb von Abschnitten, Spalten und Wizard-Schritten), `anfang` ganz nach vorn, ohne `nach` ans Ende der obersten Ebene. Danach wird der gesamte Canvas erneut geprüft; schlägt das fehl (doppelte ID, unbekannter Typ, fehlender Inhalt, mehr als 80 Bausteine), wird nichts gespeichert und `error` nennt den Grund. Die Antwort hat dieselbe Form wie bei `canvas_setzen`: `app_id`, `name`, neue `revision`, Kurzliste `bausteine`, `hinweis`.\n\n**Gut zu wissen**\n\n- Existiert die Baustein-ID in `nach` nicht, kommt der Satz „Den Baustein „…“ gibt es im Canvas nicht.“ und nichts wird geändert.\n- Die ID des neuen Bausteins muss im ganzen Canvas einmalig sein, auch gegenüber verschachtelten Bausteinen.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/canvas_baustein_einfuegen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"app_id\": 4711,\n    \"baustein\": {\n        \"id\": \"warnhinweis\",\n        \"typ\": \"hinweis\",\n        \"ton\": \"warnung\",\n        \"titel\": \"Vor dem Start\",\n        \"inhalt\": \"Roboter nur auf ebenem Boden in Betrieb nehmen.\"\n    },\n    \"nach\": \"intro\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/canvas_baustein_einfuegen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"app_id\": 4711,\n        \"baustein\": {\n            \"id\": \"warnhinweis\",\n            \"typ\": \"hinweis\",\n            \"ton\": \"warnung\",\n            \"titel\": \"Vor dem Start\",\n            \"inhalt\": \"Roboter nur auf ebenem Boden in Betrieb nehmen.\",\n        },\n        \"nach\": \"intro\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/canvas_baustein_einfuegen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"app_id\": 4711,\n      \"baustein\": {\n          \"id\": \"warnhinweis\",\n          \"typ\": \"hinweis\",\n          \"ton\": \"warnung\",\n          \"titel\": \"Vor dem Start\",\n          \"inhalt\": \"Roboter nur auf ebenem Boden in Betrieb nehmen.\"\n      },\n      \"nach\": \"intro\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"app_id":{"type":"integer","description":"ID der Anwendung (des Canvas) als ganze Zahl. Du bekommst sie aus `canvas_oeffnen` (Feld `app_id`) oder aus `anwendungen_liste` (Feld `id`). Bearbeiten darf die Anwendung nur ihr Ersteller oder ein Administrator der Firma; für eine fremde oder unbekannte ID kommt `success: false` mit einem Satz.","example":4711},"baustein":{"type":"object","description":"Der neue Baustein als Objekt `{id, typ, …Felder des Typs}`. Erlaubte Typen: text, hinweis, ablauf, tabelle, bild, checkliste, formular, schritte, liste, kennzahl, eintraege, aktion, abschnitt, spalten. Die `id` besteht aus a-z, 0-9 und _ (höchstens 40 Zeichen); Pflichtfelder hängen vom Typ ab, z. B. braucht `text` einen `inhalt` und `checkliste` mindestens einen Punkt.","example":{"id":"warnhinweis","typ":"hinweis","ton":"warnung","titel":"Vor dem Start","inhalt":"Roboter nur auf ebenem Boden in Betrieb nehmen."}},"nach":{"type":"string","description":"Optional: ID des Bausteins, hinter den der neue gesetzt wird (auch ein verschachtelter). Der Sonderwert `anfang` setzt ihn an die erste Stelle. Fehlt die Angabe, landet der Baustein am Ende der obersten Ebene.","example":"intro"}},"required":["app_id","baustein"]},"example":{"app_id":4711,"baustein":{"id":"warnhinweis","typ":"hinweis","ton":"warnung","titel":"Vor dem Start","inhalt":"Roboter nur auf ebenem Boden in Betrieb nehmen."},"nach":"intro"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"canvas_baustein_einfuegen","stufe":"schreiben","data":{"success":true,"app_id":4711,"name":"Inbetriebnahme Reinigungsroboter","revision":2,"bausteine":[{"id":"intro","typ":"text","titel":"Übergabe"},{"id":"warnhinweis","typ":"hinweis","titel":"Vor dem Start"},{"id":"pruefung","typ":"checkliste","titel":"Prüfung vor Ort"}],"hinweis":"Gespeichert — der Canvas zeigt es schon."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/canvas_baustein_aendern":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__canvas_baustein_aendern","summary":"Baustein ändern","description":"Ändert einzelne Felder eines vorhandenen Bausteins; alle anderen Felder bleiben unverändert. Der Baustein wird über seine `id` gefunden, auch wenn er in einem Abschnitt, einer Spalte oder einem Wizard-Schritt liegt. Die übergebenen Felder ersetzen die alten Werte vollständig (nicht verschachtelt gemischt): Ein neues `punkte`-Array ersetzt also die ganze Liste der Punkte. `id` und `typ` lassen sich nicht ändern; dafür entfernst du den Baustein und fügst einen neuen ein. Anschließend wird der Canvas geprüft, bei Fehlern wird nichts gespeichert. Die Antwort liefert `app_id`, `name`, neue `revision`, Kurzliste `bausteine` und `hinweis`.\n\n**Gut zu wissen**\n\n- Unbekannte ID: „Den Baustein „…“ gibt es im Canvas nicht.“ – es wird nichts geändert.\n- Felder, die der Bausteintyp nicht kennt, werden bei der Prüfung stillschweigend verworfen.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/canvas_baustein_aendern \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"app_id\": 4711,\n    \"id\": \"intro\",\n    \"felder\": {\n        \"inhalt\": \"Gehe die Punkte der Reihe nach durch und fotografiere das Typenschild.\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/canvas_baustein_aendern\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"app_id\": 4711,\n        \"id\": \"intro\",\n        \"felder\": {\n            \"inhalt\": \"Gehe die Punkte der Reihe nach durch und fotografiere das Typenschild.\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/canvas_baustein_aendern\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"app_id\": 4711,\n      \"id\": \"intro\",\n      \"felder\": {\n          \"inhalt\": \"Gehe die Punkte der Reihe nach durch und fotografiere das Typenschild.\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"app_id":{"type":"integer","description":"ID der Anwendung (des Canvas) als ganze Zahl. Du bekommst sie aus `canvas_oeffnen` (Feld `app_id`) oder aus `anwendungen_liste` (Feld `id`). Bearbeiten darf die Anwendung nur ihr Ersteller oder ein Administrator der Firma; für eine fremde oder unbekannte ID kommt `success: false` mit einem Satz.","example":4711},"id":{"type":"string","description":"ID des Bausteins, der geändert werden soll, z. B. `intro`. Die IDs erfährst du aus `canvas_lesen` oder aus der Kurzliste `bausteine` der letzten Antwort.","example":"intro"},"felder":{"type":"object","description":"Objekt mit nur den Feldern, die sich ändern, z. B. `{\"inhalt\": \"Neuer Text\"}` oder `{\"titel\": \"Neuer Titel\"}`. Die Werte ersetzen die bisherigen komplett. `id` und `typ` in diesem Objekt werden ignoriert.","example":{"inhalt":"Gehe die Punkte der Reihe nach durch und fotografiere das Typenschild."}}},"required":["app_id","id","felder"]},"example":{"app_id":4711,"id":"intro","felder":{"inhalt":"Gehe die Punkte der Reihe nach durch und fotografiere das Typenschild."}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"canvas_baustein_aendern","stufe":"schreiben","data":{"success":true,"app_id":4711,"name":"Inbetriebnahme Reinigungsroboter","revision":3,"bausteine":[{"id":"intro","typ":"text","titel":"Übergabe"},{"id":"pruefung","typ":"checkliste","titel":"Prüfung vor Ort"}],"hinweis":"Gespeichert — der Canvas zeigt es schon."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/canvas_baustein_entfernen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__canvas_baustein_entfernen","summary":"Baustein entfernen","description":"Entfernt einen Baustein samt seiner Unterbausteine aus dem Canvas. Der Baustein wird über die `id` auch in Abschnitten, Spalten und Wizard-Schritten gefunden. Die Änderung wird als neue Revision gespeichert und lässt sich mit `canvas_rueckgaengig` zurücknehmen. Die Antwort enthält `app_id`, `name`, die neue `revision`, die Kurzliste `bausteine` der obersten Ebene und `hinweis`. Existiert die ID nicht, kommt `success: false` mit dem Satz „Den Baustein „…“ gibt es im Canvas nicht.“ und es wird nichts gespeichert. Entfernst du den letzten Baustein, bleibt ein leerer Canvas übrig, der sich nicht veröffentlichen lässt.\n\n**Gut zu wissen**\n\n- Ein Abschnitt oder Wizard-Schritt nimmt alle darin liegenden Bausteine mit.\n- Platzhalter in anderen Bausteinen, die auf den entfernten verweisen, werden nicht angepasst.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/canvas_baustein_entfernen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"app_id\": 4711,\n    \"id\": \"warnhinweis\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/canvas_baustein_entfernen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"app_id\": 4711,\n        \"id\": \"warnhinweis\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/canvas_baustein_entfernen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"app_id\": 4711,\n      \"id\": \"warnhinweis\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"app_id":{"type":"integer","description":"ID der Anwendung (des Canvas) als ganze Zahl. Du bekommst sie aus `canvas_oeffnen` (Feld `app_id`) oder aus `anwendungen_liste` (Feld `id`). Bearbeiten darf die Anwendung nur ihr Ersteller oder ein Administrator der Firma; für eine fremde oder unbekannte ID kommt `success: false` mit einem Satz.","example":4711},"id":{"type":"string","description":"ID des Bausteins, der entfernt werden soll, z. B. `warnhinweis`. Sie steht in der Kurzliste `bausteine` jeder Antwort und im Ergebnis von `canvas_lesen`.","example":"warnhinweis"}},"required":["app_id","id"]},"example":{"app_id":4711,"id":"warnhinweis"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"canvas_baustein_entfernen","stufe":"schreiben","data":{"success":true,"app_id":4711,"name":"Inbetriebnahme Reinigungsroboter","revision":4,"bausteine":[{"id":"intro","typ":"text","titel":"Übergabe"},{"id":"pruefung","typ":"checkliste","titel":"Prüfung vor Ort"}],"hinweis":"Gespeichert — der Canvas zeigt es schon."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/canvas_baustein_verschieben":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__canvas_baustein_verschieben","summary":"Baustein verschieben","description":"Verschiebt einen vorhandenen Baustein an eine andere Stelle des Canvas. Mit `nach` gibst du die ID des Bausteins an, hinter den er gesetzt wird; `anfang` setzt ihn ganz nach vorn, ohne `nach` wandert er ans Ende der obersten Ebene. Intern wird der Baustein zuerst herausgelöst und dann wieder eingefügt, daher kann er auch aus einem Abschnitt auf die oberste Ebene (oder umgekehrt) wechseln. Die Antwort enthält `app_id`, `name`, die neue `revision`, die Kurzliste `bausteine` und `hinweis`. Gibt es die ID oder das Ziel nicht, kommt `success: false` mit einem Satz, und der Canvas bleibt unverändert. Verweist `nach` auf den Baustein selbst, passiert nichts.\n\n**Gut zu wissen**\n\n- Als Ziel taugt kein Baustein, der im verschobenen Baustein selbst liegt: Er wird beim Herauslösen mit entfernt und deshalb nicht gefunden.\n- Die Reihenfolge der Bausteine bestimmt die Anzeige der Anwendung von oben nach unten.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/canvas_baustein_verschieben \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"app_id\": 4711,\n    \"id\": \"pruefung\",\n    \"nach\": \"anfang\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/canvas_baustein_verschieben\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"app_id\": 4711,\n        \"id\": \"pruefung\",\n        \"nach\": \"anfang\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/canvas_baustein_verschieben\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"app_id\": 4711,\n      \"id\": \"pruefung\",\n      \"nach\": \"anfang\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"app_id":{"type":"integer","description":"ID der Anwendung (des Canvas) als ganze Zahl. Du bekommst sie aus `canvas_oeffnen` (Feld `app_id`) oder aus `anwendungen_liste` (Feld `id`). Bearbeiten darf die Anwendung nur ihr Ersteller oder ein Administrator der Firma; für eine fremde oder unbekannte ID kommt `success: false` mit einem Satz.","example":4711},"id":{"type":"string","description":"ID des Bausteins, der verschoben werden soll, z. B. `pruefung`.","example":"pruefung"},"nach":{"type":"string","description":"Optional: ID des Bausteins, hinter den der verschobene gesetzt wird. `anfang` bedeutet ganz nach vorn. Ohne Angabe wird der Baustein ans Ende der obersten Ebene gestellt.","example":"anfang"}},"required":["app_id","id"]},"example":{"app_id":4711,"id":"pruefung","nach":"anfang"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"canvas_baustein_verschieben","stufe":"schreiben","data":{"success":true,"app_id":4711,"name":"Inbetriebnahme Reinigungsroboter","revision":5,"bausteine":[{"id":"pruefung","typ":"checkliste","titel":"Prüfung vor Ort"},{"id":"intro","typ":"text","titel":"Übergabe"}],"hinweis":"Gespeichert — der Canvas zeigt es schon."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/canvas_rueckgaengig":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__canvas_rueckgaengig","summary":"Letzte Änderung rückgängig machen","description":"Stellt den Canvas auf den Stand vor der letzten Änderung zurück. Jede Änderung ist eine Revision; das Zurücknehmen legt selbst eine neue Revision an, die den älteren Stand wieder herstellt. Mehrfaches Aufrufen geht Schritt für Schritt weiter zurück, und zwar in der Reihenfolge der ursprünglichen Änderungen, nicht zwischen Rückgängig und Wiederholen hin und her. Ein Wiederherstellen (Redo) gibt es nicht. Die Antwort enthält `app_id`, `name`, die neue `revision`, die Kurzliste `bausteine` des wiederhergestellten Stands und `hinweis`. Ist man beim leeren Anfangsstand angekommen, kommt `success: false` mit „Es gibt nichts mehr zum Rückgängigmachen.“\n\n**Gut zu wissen**\n\n- Veröffentlichte Versionen bleiben unberührt; zurückgesetzt wird nur der Entwurf.\n- Der erste Aufruf nach `canvas_setzen` führt zum leeren Anfangs-Canvas.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/canvas_rueckgaengig \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"app_id\": 4711\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/canvas_rueckgaengig\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"app_id\": 4711,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/canvas_rueckgaengig\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"app_id\": 4711\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"app_id":{"type":"integer","description":"ID der Anwendung (des Canvas) als ganze Zahl. Du bekommst sie aus `canvas_oeffnen` (Feld `app_id`) oder aus `anwendungen_liste` (Feld `id`). Bearbeiten darf die Anwendung nur ihr Ersteller oder ein Administrator der Firma; für eine fremde oder unbekannte ID kommt `success: false` mit einem Satz.","example":4711}},"required":["app_id"]},"example":{"app_id":4711}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"canvas_rueckgaengig","stufe":"schreiben","data":{"success":true,"app_id":4711,"name":"Inbetriebnahme Reinigungsroboter","revision":6,"bausteine":[{"id":"intro","typ":"text","titel":"Übergabe"},{"id":"pruefung","typ":"checkliste","titel":"Prüfung vor Ort"}],"hinweis":"Rückgängig gemacht."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/baukasten_werkzeuge":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__baukasten_werkzeuge","summary":"Erlaubte Werkzeuge suchen","description":"Durchsucht den Werkzeugkatalog der Plattform nach Funktionen, die ein Canvas oder ein Agent verwenden darf. Mit `art: lesen` bekommst du Werkzeuge für Datenquellen (Liste, Kennzahl), mit `art: schreiben` solche für Aktionen und Agenten-Schritte, die etwas verändern. `suche` filtert nach einem Stichwort in Name und Beschreibung (Groß-/Kleinschreibung egal). Die Antwort enthält `werkzeuge`, höchstens 25 Einträge mit `name`, `beschreibung` (auf 400 Zeichen gekürzt), `bereich` (der Katalog-Bereich) und `parameter` (JSON-Schema). Nur diese Namen sind im Baukasten erlaubt; gesperrt sind u. a. Löschen, Entfernen, Ansichtssteuerung und der Baukasten selbst. Was genau als lesend zählt, entscheidet der Name des Werkzeugs.\n\n**Gut zu wissen**\n\n- Der Katalog ist größer als die Bereiche dieser öffentlichen API: Ein Agent kann Werkzeuge nutzen, die du hier nicht direkt aufrufen kannst. Sie laufen immer mit Firma und Person deines Schlüssels.\n- Datenquellen mit Namen wie `summarize_…`, `analyze_…` oder `export_…` werden beim Speichern abgelehnt, weil sie bei jedem Anzeigen KI-Leistung abrufen.\n- Bei mehr als 25 Treffern grenze die Suche mit einem genaueren Stichwort ein.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/baukasten_werkzeuge \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"art\": \"lesen\",\n    \"suche\": \"lead\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/baukasten_werkzeuge\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"art\": \"lesen\",\n        \"suche\": \"lead\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/baukasten_werkzeuge\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"art\": \"lesen\",\n      \"suche\": \"lead\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"art":{"type":"string","enum":["lesen","schreiben"],"description":"Welche Sorte Werkzeug du suchst: `lesen` für Datenquellen (Liste, Kennzahl, lesende Agenten-Schritte) oder `schreiben` für Aktionen, die Daten verändern. Pflichtangabe im Schema; ein anderer Wert wird wie „keine Angabe“ behandelt und liefert Werkzeuge beider Arten.","example":"lesen"},"suche":{"type":"string","description":"Optionales Stichwort, z. B. `ticket` oder `wartung`. Gesucht wird in Name und Beschreibung der Werkzeuge. Ohne Angabe kommen die ersten Werkzeuge der gewählten Art.","example":"lead"}},"required":["art"]},"example":{"art":"lesen","suche":"lead"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"baukasten_werkzeuge","stufe":"lesen","data":{"success":true,"werkzeuge":[{"name":"get_lead","beschreibung":"Hole Details zu einem Lead","bereich":"crm","parameter":{"type":"object","properties":{"lead_id":{"type":"integer","description":"ID des Leads"}},"required":["lead_id"]}}]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/anwendungen_liste":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__anwendungen_liste","summary":"Anwendungen auflisten","description":"Listet die Anwendungen deiner Firma, getrennt in veröffentlichte Anwendungen und Entwürfe. Unter `anwendungen` stehen die veröffentlichten, die dein Schlüssel sehen darf (Ersteller, Firmen-Administratoren sowie Personen, für die sie freigegeben sind). Unter `entwuerfe` stehen unveröffentlichte Anwendungen, die du verwalten darfst. Jeder Eintrag hat `id`, `name`, `beschreibung`, `symbol`, `status` (`entwurf` oder `veroeffentlicht`), `kontext_typ` (`geraet`, `lead` oder `null`), `darf_verwalten`, `updated_at` und `thread_id`. Archivierte Anwendungen erscheinen nicht. Sortiert wird nach Name. Mit der `id` greifst du in den anderen Canvas-Funktionen auf eine Anwendung zu.\n\n**Gut zu wissen**\n\n- Eine Anwendung mit Status `veroeffentlicht` kann zusätzlich einen neueren, unveröffentlichten Entwurfsstand haben; `canvas_lesen` zeigt das im Feld `unveroeffentlicht`.\n- Es gibt keine Seitenaufteilung; die Liste enthält alle sichtbaren Anwendungen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/anwendungen_liste \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/anwendungen_liste\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/anwendungen_liste\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"anwendungen_liste","stufe":"lesen","data":{"success":true,"anwendungen":[{"id":4602,"name":"Wartungsrunde Halle 2","beschreibung":"Monatliche Kontrolle der Anlagen","symbol":"wrench","status":"veroeffentlicht","kontext_typ":"geraet","darf_verwalten":true,"updated_at":"2026-11-03 09:15:22","thread_id":null}],"entwuerfe":[{"id":4711,"name":"Inbetriebnahme Reinigungsroboter","beschreibung":"Checkliste für die Übergabe beim Kunden","symbol":"layout-grid","status":"entwurf","kontext_typ":"geraet","darf_verwalten":true,"updated_at":"2026-11-09 08:40:10","thread_id":null}]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/anwendung_oeffnen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__anwendung_oeffnen","summary":"Anwendung im Chat öffnen","description":"Holt eine bestehende Anwendung als Canvas neben ein Chat-Fenster, damit sie dort weiterbearbeitet werden kann. Die Funktion setzt voraus, dass der Aufruf aus dem Chat der Toni-Zentrale (`#/toni`) kommt; sie bindet die Anwendung an genau diesen Chat-Thread. Über die Plattform-API gibt es keinen Chat-Thread, deshalb antwortet sie hier immer mit `success: false` und dem Satz „Das geht nur im Chat-Fenster der Zentrale (#/toni).“ Um eine Anwendung per API zu bearbeiten, brauchst du sie nicht zu öffnen: Alle `canvas_…`-Funktionen arbeiten direkt mit der `app_id` aus `anwendungen_liste`, und `canvas_lesen` liefert den Stand.\n\n**Gut zu wissen**\n\n- Über die API ist diese Funktion wirkungslos; nutze stattdessen `anwendungen_liste` und `canvas_lesen`.\n- Verwandt ist `agent_oeffnen`, das für Agenten dasselbe tut und über die API aus demselben Grund fehlschlägt.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/anwendung_oeffnen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"app_id\": 4602\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/anwendung_oeffnen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"app_id\": 4602,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/anwendung_oeffnen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"app_id\": 4602\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"app_id":{"type":"integer","description":"ID der Anwendung als ganze Zahl (aus `anwendungen_liste`). Im Chat der Zentrale wird die Anwendung damit an den aktuellen Chat gebunden; über die API wird der Wert nicht ausgewertet, weil vorher der Chat-Fehler kommt.","example":4602}},"required":["app_id"]},"example":{"app_id":4602}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"anwendung_oeffnen","stufe":"schreiben","data":{"success":false,"error":"Das geht nur im Chat-Fenster der Zentrale (#/toni)."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_entwurf":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_entwurf","summary":"Agenten-Entwurf anlegen","description":"Legt einen neuen Agenten als Entwurf an. Ein Agent arbeitet eine Folge von Schritten ohne Menschen ab und fragt bei schreibenden Werkzeugen nach. Der neue Entwurf ist leer: keine Schritte, Auslöser nur von Hand, Budget 60 Schritte / 40 KI-Aufrufe / 30 Minuten, nach jedem Lauf eine Benachrichtigung per Glocke an den Inhaber. `anweisungen` (das Wissen des Agenten in Sätzen) wird gleich mitgespeichert. Über die API gibt es keinen Chat; deshalb entsteht bei jedem Aufruf ein neuer Agent, es wird nie ein vorhandener zurückgegeben. Die Antwort enthält `agent_id`, `name`, `revision` (0 bzw. 1 mit Anweisungen), `status` (`entwurf`), die leere Liste `schritte`, `briefing`, `leitplanken` und einen `hinweis`.\n\n**Gut zu wissen**\n\n- Rufe die Funktion nicht wiederholt auf, nur um den Agenten „wiederzufinden“: Jeder Aufruf legt einen weiteren Agenten an. Vorhandene findest du mit `agenten_liste`.\n- Ein Agent läuft erst, nachdem ein Mensch seine Version in der Oberfläche (`#/agenten/<id>`) veröffentlicht hat.\n- Der Name wird auf 160 Zeichen gekürzt; ein leerer Name wird zu „Neuer Agent“.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_entwurf \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Anfragen sichten\",\n    \"beschreibung\": \"Prüft neue Anfragen und markiert dringende\",\n    \"anweisungen\": \"Dringend sind Anfragen mit Fristen unter 48 Stunden oder einem Wunschtermin in dieser Woche.\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_entwurf\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"name\": \"Anfragen sichten\",\n        \"beschreibung\": \"Prüft neue Anfragen und markiert dringende\",\n        \"anweisungen\": \"Dringend sind Anfragen mit Fristen unter 48 Stunden oder einem Wunschtermin in dieser Woche.\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_entwurf\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"name\": \"Anfragen sichten\",\n      \"beschreibung\": \"Prüft neue Anfragen und markiert dringende\",\n      \"anweisungen\": \"Dringend sind Anfragen mit Fristen unter 48 Stunden oder einem Wunschtermin in dieser Woche.\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name des Agenten, z. B. „Anfragen sichten“. Wird auf 160 Zeichen gekürzt; ein leerer Name ergibt „Neuer Agent“. Der Name wird in Listen und beim Start über `agent_starten` verwendet.","example":"Anfragen sichten"},"beschreibung":{"type":"string","description":"Optionale Beschreibung, wozu der Agent da ist, in ein bis zwei Sätzen. Wird auf 600 Zeichen gekürzt und in der Agentenliste gezeigt.","example":"Prüft neue Anfragen und markiert dringende"},"anweisungen":{"type":"string","description":"Optional: Was der Agent wissen muss, in ganzen Sätzen (Fachbegriffe, Regeln des Hauses, Tonfall). Wird auf 6000 Zeichen gekürzt und jedem KI-Schritt als Hintergrundwissen mitgegeben.","example":"Dringend sind Anfragen mit Fristen unter 48 Stunden oder einem Wunschtermin in dieser Woche."}},"required":["name"]},"example":{"name":"Anfragen sichten","beschreibung":"Prüft neue Anfragen und markiert dringende","anweisungen":"Dringend sind Anfragen mit Fristen unter 48 Stunden oder einem Wunschtermin in dieser Woche."}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_entwurf","stufe":"schreiben","data":{"success":true,"agent_id":812,"name":"Anfragen sichten","revision":1,"status":"entwurf","schritte":[],"briefing":{"ziel":"","fertig_wenn":""},"leitplanken":[],"hinweis":"Agent offen — mit agent_setzen die Schritte setzen. Veröffentlichen macht der Chef."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_oeffnen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_oeffnen","summary":"Agent im Chat öffnen","description":"Holt einen bestehenden Agenten als Ablaufgrafik in ein Chat-Fenster, damit er dort weiterbearbeitet werden kann. Die Funktion funktioniert nur im Chat der Toni-Zentrale (`#/toni`), weil sie den Agenten an den aktuellen Chat-Thread bindet. Über die Plattform-API gibt es keinen Chat-Thread; sie antwortet daher immer mit `success: false` und dem Satz „Das geht nur im Chat-Fenster der Zentrale (#/toni).“ Um einen Agenten per API zu bearbeiten, musst du ihn nicht öffnen: Alle `agent_…`-Funktionen arbeiten direkt mit der `agent_id` aus `agent_entwurf` oder `agenten_liste`, und `agent_lesen` zeigt den vollständigen Entwurf.\n\n**Gut zu wissen**\n\n- Über die API ist diese Funktion wirkungslos; nutze `agenten_liste` und `agent_lesen`.\n- Verwandt ist `anwendung_oeffnen`, das für Anwendungen dasselbe tut.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_oeffnen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"agent_id\": 812\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_oeffnen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"agent_id\": 812,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_oeffnen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"agent_id\": 812\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"type":"integer","description":"ID des Agenten als ganze Zahl. Du bekommst sie aus `agent_entwurf` (Feld `agent_id`) oder `agenten_liste` (Feld `id`). Ändern darf den Agenten nur sein Ersteller oder ein Administrator der Firma; eine fremde, archivierte oder unbekannte ID liefert `success: false` mit „Diesen Agenten gibt es nicht.“","example":812}},"required":["agent_id"]},"example":{"agent_id":812}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_oeffnen","stufe":"schreiben","data":{"success":false,"error":"Das geht nur im Chat-Fenster der Zentrale (#/toni)."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_briefing":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_briefing","summary":"Briefing, Leitplanken und KI-Stufe setzen","description":"Setzt die „weichen“ Vorgaben eines Agenten-Entwurfs: das Briefing (`ziel`: wozu es den Agenten gibt, `fertig_wenn`: woran man erkennt, dass er fertig ist), sein Wissen (`anweisungen`), die Leitplanken und die KI-Stufe. Nur die übergebenen Felder werden verändert, alles andere (Schritte, Auslöser, Budget, Werkzeuge) bleibt. Leitplanken ersetzen die bisherigen vollständig. Briefing, Anweisungen und Leitplanken werden jedem KI-, Datei- und freien Schritt als Absatz „Briefing des Agenten“ mitgegeben; Leitplanken gehen dort jeder anderen Anweisung vor. Die Antwort ist der Kurzstand des Agenten (`agent_id`, `name`, neue `revision`, `status`, `schritte`, `briefing`, `leitplanken`, `hinweis`).\n\n**Gut zu wissen**\n\n- Es sind höchstens 20 Leitplanken erlaubt (je 300 Zeichen); leere und doppelte Einträge fallen weg. Bei mehr als 20 wird nichts gespeichert.\n- Die KI-Stufe `schnell` gilt, wenn nichts gesetzt ist. Einzelne Schritte können mit `modell` eine eigene Stufe wählen. Höhere Stufen denken länger und kosten mehr Rechenleistung.\n- Eine Änderung wirkt erst im laufenden Betrieb, wenn ein Mensch die neue Version veröffentlicht.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_briefing \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"agent_id\": 812,\n    \"briefing\": {\n        \"ziel\": \"Neue Anfragen sichten und dringende Fälle markieren.\",\n        \"fertig_wenn\": \"Jede neue Anfrage ist eingeschätzt und dringende Anfragen tragen den Status „contacted“.\"\n    },\n    \"leitplanken\": [\n        \"Nie Anfragen löschen.\",\n        \"Immer zuerst die Anfrage lesen, dann entscheiden.\"\n    ],\n    \"modell\": \"ausgewogen\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_briefing\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"agent_id\": 812,\n        \"briefing\": {\n            \"ziel\": \"Neue Anfragen sichten und dringende Fälle markieren.\",\n            \"fertig_wenn\": \"Jede neue Anfrage ist eingeschätzt und dringende Anfragen tragen den Status „contacted“.\",\n        },\n        \"leitplanken\": [\n            \"Nie Anfragen löschen.\",\n            \"Immer zuerst die Anfrage lesen, dann entscheiden.\",\n        ],\n        \"modell\": \"ausgewogen\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_briefing\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"agent_id\": 812,\n      \"briefing\": {\n          \"ziel\": \"Neue Anfragen sichten und dringende Fälle markieren.\",\n          \"fertig_wenn\": \"Jede neue Anfrage ist eingeschätzt und dringende Anfragen tragen den Status „contacted“.\"\n      },\n      \"leitplanken\": [\n          \"Nie Anfragen löschen.\",\n          \"Immer zuerst die Anfrage lesen, dann entscheiden.\"\n      ],\n      \"modell\": \"ausgewogen\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"type":"integer","description":"ID des Agenten als ganze Zahl. Du bekommst sie aus `agent_entwurf` (Feld `agent_id`) oder `agenten_liste` (Feld `id`). Ändern darf den Agenten nur sein Ersteller oder ein Administrator der Firma; eine fremde, archivierte oder unbekannte ID liefert `success: false` mit „Diesen Agenten gibt es nicht.“","example":812},"briefing":{"type":"object","description":"Objekt mit `ziel` (wozu es den Agenten gibt) und `fertig_wenn` (woran man erkennt, dass die Arbeit erledigt ist), jeweils bis 1000 Zeichen. Beide Felder werden zusammen gesetzt; ein fehlendes wird leer.","example":{"ziel":"Neue Anfragen sichten und dringende Fälle markieren.","fertig_wenn":"Jede neue Anfrage ist eingeschätzt und dringende Anfragen tragen den Status „contacted“."}},"anweisungen":{"type":"string","description":"Wissen des Agenten in ganzen Sätzen, bis 6000 Zeichen. Ersetzt den bisherigen Text. Wird jedem KI-Schritt als Hintergrund mitgegeben."},"leitplanken":{"type":"array","items":{"type":"string"},"description":"Liste kurzer Regeln wie „Nie …“, „Immer …“, „Nur …“; höchstens 20 Einträge zu je 300 Zeichen. Die Liste ersetzt die bisherigen Leitplanken vollständig. Sie gelten in KI-Schritten vor jeder anderen Anweisung; verletzt eine Aufgabe eine Leitplanke, soll der Agent sie nicht ausführen.","example":["Nie Anfragen löschen.","Immer zuerst die Anfrage lesen, dann entscheiden."]},"modell":{"type":"string","enum":["schnell","ausgewogen","gruendlich","tiefgang"],"description":"KI-Stufe des Agenten als Standard für alle KI-Schritte: `schnell` (Standard), `ausgewogen`, `gruendlich` oder `tiefgang`. Eine unbekannte Stufe wird mit dem Satz „Das KI-Modell … gibt es nicht.“ abgelehnt.","example":"ausgewogen"}},"required":["agent_id"]},"example":{"agent_id":812,"briefing":{"ziel":"Neue Anfragen sichten und dringende Fälle markieren.","fertig_wenn":"Jede neue Anfrage ist eingeschätzt und dringende Anfragen tragen den Status „contacted“."},"leitplanken":["Nie Anfragen löschen.","Immer zuerst die Anfrage lesen, dann entscheiden."],"modell":"ausgewogen"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_briefing","stufe":"schreiben","data":{"success":true,"agent_id":812,"name":"Anfragen sichten","revision":2,"status":"entwurf","schritte":[],"briefing":{"ziel":"Neue Anfragen sichten und dringende Fälle markieren.","fertig_wenn":"Jede neue Anfrage ist eingeschätzt und dringende Anfragen tragen den Status „contacted“."},"leitplanken":["Nie Anfragen löschen.","Immer zuerst die Anfrage lesen, dann entscheiden."],"hinweis":"Gespeichert."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_setzen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_setzen","summary":"Schritte, Auslöser und Werkzeuge setzen","description":"Schreibt den Ablauf eines Agenten-Entwurfs in einem Zug (erster Wurf): die Schritte, die Liste der erlaubten Werkzeuge, Auslöser, Budget, Anweisungen, Briefing, Leitplanken, KI-Stufe und Benachrichtigung. Die Schritte werden vollständig ersetzt; alle anderen Felder bleiben, wenn du sie weglässt. Das Dokument wird streng geprüft: Schritt-Schlüssel (`key`) müssen eindeutig sein, Sprungziele müssen existieren, jedes im Ablauf benutzte Werkzeug muss in `werkzeuge` stehen, ein KI-Schritt braucht Ergebnisfelder. Bei Fehlern wird nichts gespeichert und `error` nennt jeden Grund als Satz. Antwort: Kurzstand mit `agent_id`, `name`, `revision`, `status`, `schritte` (`key`, `typ`, `titel`), `briefing`, `leitplanken` und `hinweis`.\n\n**Gut zu wissen**\n\n- Zeitpläne und Ereignisse als Auslöser wirken erst, wenn ein Mensch die Version veröffentlicht hat; bis dahin läuft der Entwurf nur per Simulation.\n- Die Funktion setzt den Agenten-Entwurf neu zusammen und übernimmt dabei nur die hier genannten Felder: Felder je Kunde (`felder`), die Abteilung und die Zuordnung zu einem KI-Mitarbeiter gehen verloren, falls der Entwurf sie enthielt. Sie lassen sich über diese API nicht setzen.\n- Schreibende Werkzeuge fragen im Lauf nach, außer `ohne_rueckfrage` ist gesetzt; nach außen wirkende (Namen mit `send_`, `publish_`, `submit_`, `activate_`, `deploy_` sowie `create_invoice_draft`) brauchen zusätzlich `aussen_bestaetigt`.\n- Höchstens 40 Schritte je Agent.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_setzen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"agent_id\": 812,\n    \"schritte\": [\n        {\n            \"key\": \"lead_laden\",\n            \"typ\": \"werkzeug\",\n            \"titel\": \"Anfrage laden\",\n            \"werkzeug\": \"get_lead\",\n            \"parameter\": {\n                \"lead_id\": \"{{eingabe.lead_id}}\"\n            }\n        },\n        {\n            \"key\": \"einschaetzen\",\n            \"typ\": \"ki\",\n            \"titel\": \"Dringlichkeit einschätzen\",\n            \"anweisung\": \"Beurteile anhand der Anfrage, ob sie innerhalb eines Tages beantwortet werden sollte.\",\n            \"eingaben\": {\n                \"anfrage\": \"{{schritte.lead_laden}}\"\n            },\n            \"ergebnis\": [\n                {\n                    \"name\": \"dringend\",\n                    \"art\": \"janein\",\n                    \"beschreibung\": \"Ja, wenn die Anfrage innerhalb eines Tages beantwortet werden sollte.\"\n                }\n            ]\n        },\n        {\n            \"key\": \"pruefen\",\n            \"typ\": \"entscheidung\",\n            \"titel\": \"Dringend?\",\n            \"bedingung\": {\n                \"wert\": \"{{schritte.einschaetzen.dringend}}\",\n                \"op\": \"angehakt\"\n            },\n            \"dann\": \"status_setzen\",\n            \"sonst\": \"ende\"\n        },\n        {\n            \"key\": \"status_setzen\",\n            \"typ\": \"werkzeug\",\n            \"titel\": \"Status setzen\",\n            \"werkzeug\": \"change_lead_status\",\n            \"parameter\": {\n                \"lead_id\": \"{{eingabe.lead_id}}\",\n                \"status\": \"contacted\"\n            }\n        }\n    ],\n    \"werkzeuge\": [\n        {\n            \"name\": \"get_lead\",\n            \"ohne_rueckfrage\": true\n        },\n        {\n            \"name\": \"change_lead_status\",\n            \"ohne_rueckfrage\": false\n        }\n    ],\n    \"ausloeser\": {\n        \"hand\": true,\n        \"ereignisse\": [\n            \"lead.neu\"\n        ]\n    },\n    \"budget\": {\n        \"schritte\": 30,\n        \"ki\": 10,\n        \"minuten\": 10\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_setzen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"agent_id\": 812,\n        \"schritte\": [\n            {\n                \"key\": \"lead_laden\",\n                \"typ\": \"werkzeug\",\n                \"titel\": \"Anfrage laden\",\n                \"werkzeug\": \"get_lead\",\n                \"parameter\": {\n                    \"lead_id\": \"{{eingabe.lead_id}}\",\n                },\n            },\n            {\n                \"key\": \"einschaetzen\",\n                \"typ\": \"ki\",\n                \"titel\": \"Dringlichkeit einschätzen\",\n                \"anweisung\": \"Beurteile anhand der Anfrage, ob sie innerhalb eines Tages beantwortet werden sollte.\",\n                \"eingaben\": {\n                    \"anfrage\": \"{{schritte.lead_laden}}\",\n                },\n                \"ergebnis\": [\n                    {\n                        \"name\": \"dringend\",\n                        \"art\": \"janein\",\n                        \"beschreibung\": \"Ja, wenn die Anfrage innerhalb eines Tages beantwortet werden sollte.\",\n                    },\n                ],\n            },\n            {\n                \"key\": \"pruefen\",\n                \"typ\": \"entscheidung\",\n                \"titel\": \"Dringend?\",\n                \"bedingung\": {\n                    \"wert\": \"{{schritte.einschaetzen.dringend}}\",\n                    \"op\": \"angehakt\",\n                },\n                \"dann\": \"status_setzen\",\n                \"sonst\": \"ende\",\n            },\n            {\n                \"key\": \"status_setzen\",\n                \"typ\": \"werkzeug\",\n                \"titel\": \"Status setzen\",\n                \"werkzeug\": \"change_lead_status\",\n                \"parameter\": {\n                    \"lead_id\": \"{{eingabe.lead_id}}\",\n                    \"status\": \"contacted\",\n                },\n            },\n        ],\n        \"werkzeuge\": [\n            {\n                \"name\": \"get_lead\",\n                \"ohne_rueckfrage\": True,\n            },\n            {\n                \"name\": \"change_lead_status\",\n                \"ohne_rueckfrage\": False,\n            },\n        ],\n        \"ausloeser\": {\n            \"hand\": True,\n            \"ereignisse\": [\n                \"lead.neu\",\n            ],\n        },\n        \"budget\": {\n            \"schritte\": 30,\n            \"ki\": 10,\n            \"minuten\": 10,\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_setzen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"agent_id\": 812,\n      \"schritte\": [\n          {\n              \"key\": \"lead_laden\",\n              \"typ\": \"werkzeug\",\n              \"titel\": \"Anfrage laden\",\n              \"werkzeug\": \"get_lead\",\n              \"parameter\": {\n                  \"lead_id\": \"{{eingabe.lead_id}}\"\n              }\n          },\n          {\n              \"key\": \"einschaetzen\",\n              \"typ\": \"ki\",\n              \"titel\": \"Dringlichkeit einschätzen\",\n              \"anweisung\": \"Beurteile anhand der Anfrage, ob sie innerhalb eines Tages beantwortet werden sollte.\",\n              \"eingaben\": {\n                  \"anfrage\": \"{{schritte.lead_laden}}\"\n              },\n              \"ergebnis\": [\n                  {\n                      \"name\": \"dringend\",\n                      \"art\": \"janein\",\n                      \"beschreibung\": \"Ja, wenn die Anfrage innerhalb eines Tages beantwortet werden sollte.\"\n                  }\n              ]\n          },\n          {\n              \"key\": \"pruefen\",\n              \"typ\": \"entscheidung\",\n              \"titel\": \"Dringend?\",\n              \"bedingung\": {\n                  \"wert\": \"{{schritte.einschaetzen.dringend}}\",\n                  \"op\": \"angehakt\"\n              },\n              \"dann\": \"status_setzen\",\n              \"sonst\": \"ende\"\n          },\n          {\n              \"key\": \"status_setzen\",\n              \"typ\": \"werkzeug\",\n              \"titel\": \"Status setzen\",\n              \"werkzeug\": \"change_lead_status\",\n              \"parameter\": {\n                  \"lead_id\": \"{{eingabe.lead_id}}\",\n                  \"status\": \"contacted\"\n              }\n          }\n      ],\n      \"werkzeuge\": [\n          {\n              \"name\": \"get_lead\",\n              \"ohne_rueckfrage\": true\n          },\n          {\n              \"name\": \"change_lead_status\",\n              \"ohne_rueckfrage\": false\n          }\n      ],\n      \"ausloeser\": {\n          \"hand\": true,\n          \"ereignisse\": [\n              \"lead.neu\"\n          ]\n      },\n      \"budget\": {\n          \"schritte\": 30,\n          \"ki\": 10,\n          \"minuten\": 10\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"type":"integer","description":"ID des Agenten als ganze Zahl. Du bekommst sie aus `agent_entwurf` (Feld `agent_id`) oder `agenten_liste` (Feld `id`). Ändern darf den Agenten nur sein Ersteller oder ein Administrator der Firma; eine fremde, archivierte oder unbekannte ID liefert `success: false` mit „Diesen Agenten gibt es nicht.“","example":812},"schritte":{"type":"array","items":{"type":"object","description":"Schritt {key, typ: werkzeug|ki|datei|browser|tabelle|frei|frage|entscheidung, titel, …Felder des Typs}"},"description":"Vollständige Liste der Schritte, jeweils `{key, typ, titel, …}`. `key`: a-z, 0-9, _ (höchstens 40 Zeichen), eindeutig; `ende`, `eingabe`, `kontext` und `schritte` sind reserviert. `typ` und seine Felder: `werkzeug {werkzeug, parameter}` (fest); `ki {anweisung, eingaben, ergebnis[{name, art: text|zahl|liste|objekt|janein, beschreibung}], modell}` (ergebnis ist Pflicht); `datei {quelle, auszug{anweisung, ergebnis[]}}` liest Dateien des Laufs; `browser {start_url (https), ziel, domains[], zugang, max_aktionen (bis 50), dateien_holen}`; `frei {ziel, werkzeuge[], max_runden (1–12)}` (die KI wählt selbst); `frage {frage, art: freigabe|eingabe, felder}`; `entscheidung {bedingung{wert, op, vergleich}, dann, sonst}` mit Schritt-Keys oder `ende`; `tabelle` (Tabelle umformen, ohne KI); außerdem `pdf_werte` und `berechnen`. Optional `weiter` (Key des nächsten Schritts oder `ende`); ohne `weiter` folgt der nächste in der Liste. Werte wie `{{eingabe.lead_id}}` und `{{schritte.<key>.<feld>}}` werden zur Laufzeit eingesetzt.","example":[{"key":"lead_laden","typ":"werkzeug","titel":"Anfrage laden","werkzeug":"get_lead","parameter":{"lead_id":"{{eingabe.lead_id}}"}},{"key":"einschaetzen","typ":"ki","titel":"Dringlichkeit einschätzen","anweisung":"Beurteile anhand der Anfrage, ob sie innerhalb eines Tages beantwortet werden sollte.","eingaben":{"anfrage":"{{schritte.lead_laden}}"},"ergebnis":[{"name":"dringend","art":"janein","beschreibung":"Ja, wenn die Anfrage innerhalb eines Tages beantwortet werden sollte."}]},{"key":"pruefen","typ":"entscheidung","titel":"Dringend?","bedingung":{"wert":"{{schritte.einschaetzen.dringend}}","op":"angehakt"},"dann":"status_setzen","sonst":"ende"},{"key":"status_setzen","typ":"werkzeug","titel":"Status setzen","werkzeug":"change_lead_status","parameter":{"lead_id":"{{eingabe.lead_id}}","status":"contacted"}}]},"werkzeuge":{"type":"array","items":{"type":"object"},"description":"Liste der Werkzeuge, die der Agent benutzen darf, als Objekte `{name, ohne_rueckfrage, aussen_bestaetigt}` (beide Schalter Standard `false`). Jedes in einem `werkzeug`- oder `frei`-Schritt benutzte Werkzeug muss hier stehen. Gesperrt sind Namen mit `delete_`, `remove_`, `ui_`, `canvas_`, `baukasten_`, `anwendung` sowie `find_tool`, `call_tool` und `delegate_background`. Namen findest du mit `baukasten_werkzeuge`. Lässt du das Feld weg, bleibt die bisherige Liste.","example":[{"name":"get_lead","ohne_rueckfrage":true},{"name":"change_lead_status","ohne_rueckfrage":false}]},"ausloeser":{"type":"object","description":"Wann Läufe entstehen. `hand` ist immer wahr. `ereignisse`: Liste von Ereignissen der Plattform, z. B. `lead.neu`, `rueckruf.gewuenscht`, `mail.neu`, `ticket.neu`, `angebot.angenommen`, `rechnung.bezahlt`, `rechnung.ueberfaellig`, `datei.neu`; unbekannte Namen fallen weg, die Daten stehen im Lauf als `{{eingabe.<feld>}}` bereit (bei `lead.neu` z. B. `lead_id`). `zeitplaene`: höchstens 10 Pläne der Form `{takt: einmalig, datum: \"JJJJ-MM-TT\", uhrzeit: \"HH:MM\"}`, `{takt: taeglich, uhrzeit}`, `{takt: woechentlich, wochentage: [1-7], uhrzeit}`, `{takt: monatlich, tag: 1-31 oder \"letzter\", uhrzeit}`, `{takt: jaehrlich, monat, tag, uhrzeit}` oder `{takt: intervall, alle: N, einheit: minuten|stunden|tage|wochen, ab: \"JJJJ-MM-TT HH:MM\"}` (Intervall mindestens 15 Minuten, Zeitzone Berlin). Fehlt das Feld, bleiben die bisherigen Auslöser.","example":{"hand":true,"ereignisse":["lead.neu"]}},"budget":{"type":"object","description":"Obergrenzen je Lauf als Objekt `{schritte, ki, minuten}`: `schritte` 1–200 (Standard 60), `ki` 0–100 KI-Aufrufe (Standard 40), `minuten` 1–120 (Standard 30). Werte außerhalb werden begrenzt. Ist ein Limit erreicht, wird der Lauf mit dem Grund „Budget erreicht“ angehalten bzw. der KI-Schritt scheitert. Fehlt das Feld, bleibt das bisherige Budget.","example":{"schritte":30,"ki":10,"minuten":10}},"anweisungen":{"type":"string","description":"Wissen des Agenten in ganzen Sätzen, bis 6000 Zeichen. Fehlt das Feld, bleibt der bisherige Text."},"briefing":{"type":"object","description":"Objekt `{ziel, fertig_wenn}` mit je bis zu 1000 Zeichen: wozu es den Agenten gibt und woran man erkennt, dass er fertig ist. Fehlt das Feld, bleibt das bisherige Briefing."},"leitplanken":{"type":"array","items":{"type":"string"},"description":"Liste kurzer Regeln („Nie …“, „Immer …“, „Nur …“), höchstens 20 zu je 300 Zeichen. Ersetzt die bisherigen. Fehlt das Feld, bleiben die bisherigen."},"modell":{"type":"string","enum":["schnell","ausgewogen","gruendlich","tiefgang"],"description":"KI-Stufe des Agenten: `schnell` (Standard), `ausgewogen`, `gruendlich` oder `tiefgang`. Einzelne KI-, Datei-, Browser- und freie Schritte können mit ihrem Feld `modell` eine eigene Stufe wählen."},"benachrichtigung":{"type":"object","description":"Wer nach einem Lauf Bescheid bekommt: Objekt `{glocke: bool, email: bool, auch_fehler: bool}`. Benachrichtigt wird der Inhaber der Firma (Glocke oben bzw. E-Mail); mit `auch_fehler: true` auch bei Fehler oder Anhalten, Simulationen melden nichts. Fehlt das Feld, bleibt die bisherige Einstellung (neue Agenten: Glocke an)."}},"required":["agent_id","schritte"]},"example":{"agent_id":812,"schritte":[{"key":"lead_laden","typ":"werkzeug","titel":"Anfrage laden","werkzeug":"get_lead","parameter":{"lead_id":"{{eingabe.lead_id}}"}},{"key":"einschaetzen","typ":"ki","titel":"Dringlichkeit einschätzen","anweisung":"Beurteile anhand der Anfrage, ob sie innerhalb eines Tages beantwortet werden sollte.","eingaben":{"anfrage":"{{schritte.lead_laden}}"},"ergebnis":[{"name":"dringend","art":"janein","beschreibung":"Ja, wenn die Anfrage innerhalb eines Tages beantwortet werden sollte."}]},{"key":"pruefen","typ":"entscheidung","titel":"Dringend?","bedingung":{"wert":"{{schritte.einschaetzen.dringend}}","op":"angehakt"},"dann":"status_setzen","sonst":"ende"},{"key":"status_setzen","typ":"werkzeug","titel":"Status setzen","werkzeug":"change_lead_status","parameter":{"lead_id":"{{eingabe.lead_id}}","status":"contacted"}}],"werkzeuge":[{"name":"get_lead","ohne_rueckfrage":true},{"name":"change_lead_status","ohne_rueckfrage":false}],"ausloeser":{"hand":true,"ereignisse":["lead.neu"]},"budget":{"schritte":30,"ki":10,"minuten":10}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_setzen","stufe":"schreiben","data":{"success":true,"agent_id":812,"name":"Anfragen sichten","revision":3,"status":"entwurf","schritte":[{"key":"lead_laden","typ":"werkzeug","titel":"Anfrage laden"},{"key":"einschaetzen","typ":"ki","titel":"Dringlichkeit einschätzen"},{"key":"pruefen","typ":"entscheidung","titel":"Dringend?"},{"key":"status_setzen","typ":"werkzeug","titel":"Status setzen"}],"briefing":{"ziel":"Neue Anfragen sichten und dringende Fälle markieren.","fertig_wenn":"Jede neue Anfrage ist eingeschätzt und dringende Anfragen tragen den Status „contacted“."},"leitplanken":["Nie Anfragen löschen.","Immer zuerst die Anfrage lesen, dann entscheiden."],"hinweis":"Gespeichert — die Ablaufgrafik zeigt es."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_schritt_einfuegen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_schritt_einfuegen","summary":"Schritt einfügen","description":"Fügt einem Agenten-Entwurf einen einzelnen Schritt hinzu, ohne die übrigen zu verändern. Mit `nach` bestimmst du die Stelle: ein Schritt-Key setzt ihn dahinter, `anfang` ganz nach vorn, ohne Angabe ans Ende der Liste. Der Schritt folgt standardmäßig auf seinen Vorgänger; Sprünge legst du mit `weiter` (bzw. `dann`/`sonst` bei Entscheidungen) fest. Danach wird der ganze Agent erneut geprüft; bei Fehlern (doppelter Key, fehlende Pflichtfelder, Sprungziel unbekannt, Werkzeug nicht in der Liste) wird nichts gespeichert und `error` nennt den Grund. Die Antwort ist der Kurzstand des Agenten mit neuer `revision` und der aktuellen Liste `schritte`.\n\n**Gut zu wissen**\n\n- Benutzt der Schritt ein Werkzeug, muss es bereits in der Werkzeugliste des Agenten stehen. Diese Funktion kann die Liste nicht ändern; setze sie vorher mit `agent_setzen`.\n- Einfügen mitten in den Ablauf ändert die Reihenfolge, nicht die expliziten Sprungziele anderer Schritte.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_schritt_einfuegen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"agent_id\": 812,\n    \"schritt\": {\n        \"key\": \"freigabe\",\n        \"typ\": \"frage\",\n        \"titel\": \"Status setzen?\",\n        \"frage\": \"Soll die Anfrage auf „contacted“ gesetzt werden?\",\n        \"art\": \"freigabe\"\n    },\n    \"nach\": \"pruefen\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_schritt_einfuegen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"agent_id\": 812,\n        \"schritt\": {\n            \"key\": \"freigabe\",\n            \"typ\": \"frage\",\n            \"titel\": \"Status setzen?\",\n            \"frage\": \"Soll die Anfrage auf „contacted“ gesetzt werden?\",\n            \"art\": \"freigabe\",\n        },\n        \"nach\": \"pruefen\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_schritt_einfuegen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"agent_id\": 812,\n      \"schritt\": {\n          \"key\": \"freigabe\",\n          \"typ\": \"frage\",\n          \"titel\": \"Status setzen?\",\n          \"frage\": \"Soll die Anfrage auf „contacted“ gesetzt werden?\",\n          \"art\": \"freigabe\"\n      },\n      \"nach\": \"pruefen\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"type":"integer","description":"ID des Agenten als ganze Zahl. Du bekommst sie aus `agent_entwurf` (Feld `agent_id`) oder `agenten_liste` (Feld `id`). Ändern darf den Agenten nur sein Ersteller oder ein Administrator der Firma; eine fremde, archivierte oder unbekannte ID liefert `success: false` mit „Diesen Agenten gibt es nicht.“","example":812},"schritt":{"type":"object","description":"Der neue Schritt als Objekt `{key, typ, titel, …Felder des Typs}`. `typ` ist einer von werkzeug, ki, datei, browser, frei, frage, entscheidung, tabelle, pdf_werte, berechnen. Der `key` besteht aus a-z, 0-9, _ (höchstens 40 Zeichen) und darf im Agenten nur einmal vorkommen. Die Felder je Typ stehen bei `agent_setzen` im Parameter `schritte`.","example":{"key":"freigabe","typ":"frage","titel":"Status setzen?","frage":"Soll die Anfrage auf „contacted“ gesetzt werden?","art":"freigabe"}},"nach":{"type":"string","description":"Optional: Key des Schritts, hinter den der neue gesetzt wird. `anfang` bedeutet ganz nach vorn, ohne Angabe wird der Schritt ans Ende gehängt. Ein unbekannter Key ergibt den Satz „Den Schritt „…“ gibt es nicht.“","example":"pruefen"}},"required":["agent_id","schritt"]},"example":{"agent_id":812,"schritt":{"key":"freigabe","typ":"frage","titel":"Status setzen?","frage":"Soll die Anfrage auf „contacted“ gesetzt werden?","art":"freigabe"},"nach":"pruefen"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_schritt_einfuegen","stufe":"schreiben","data":{"success":true,"agent_id":812,"name":"Anfragen sichten","revision":4,"status":"entwurf","schritte":[{"key":"lead_laden","typ":"werkzeug","titel":"Anfrage laden"},{"key":"einschaetzen","typ":"ki","titel":"Dringlichkeit einschätzen"},{"key":"pruefen","typ":"entscheidung","titel":"Dringend?"},{"key":"freigabe","typ":"frage","titel":"Status setzen?"},{"key":"status_setzen","typ":"werkzeug","titel":"Status setzen"}],"briefing":{"ziel":"Neue Anfragen sichten und dringende Fälle markieren.","fertig_wenn":"Jede neue Anfrage ist eingeschätzt und dringende Anfragen tragen den Status „contacted“."},"leitplanken":["Nie Anfragen löschen.","Immer zuerst die Anfrage lesen, dann entscheiden."],"hinweis":"Gespeichert."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_schritt_aendern":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_schritt_aendern","summary":"Schritt ändern","description":"Ändert Felder eines vorhandenen Schritts im Agenten-Entwurf. Die übergebenen Felder ersetzen die bisherigen Werte (nicht verschachtelt gemischt): ein neues `ergebnis`-Array ersetzt also die ganze Ergebnisliste, ein neues `parameter`-Objekt alle Parameter. Alle nicht genannten Felder bleiben. `key` und `typ` lassen sich nicht ändern; für einen anderen Typ entfernst du den Schritt und fügst einen neuen ein. Anschließend wird der gesamte Agent geprüft; bei Fehlern wird nichts gespeichert. Die Antwort ist der Kurzstand mit neuer `revision` und der Liste `schritte`. Ein unbekannter Key ergibt „Den Schritt „…“ gibt es nicht.“\n\n**Gut zu wissen**\n\n- Wechselst du das Werkzeug eines Schritts, muss das neue Werkzeug in der Werkzeugliste des Agenten stehen (`agent_setzen`).\n- Mit `weiter` änderst du das Sprungziel eines Schritts; bei einer `entscheidung` heißen die Felder `dann` und `sonst`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_schritt_aendern \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"agent_id\": 812,\n    \"key\": \"einschaetzen\",\n    \"felder\": {\n        \"anweisung\": \"Beurteile anhand der Anfrage, ob sie innerhalb von 48 Stunden beantwortet werden sollte.\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_schritt_aendern\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"agent_id\": 812,\n        \"key\": \"einschaetzen\",\n        \"felder\": {\n            \"anweisung\": \"Beurteile anhand der Anfrage, ob sie innerhalb von 48 Stunden beantwortet werden sollte.\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_schritt_aendern\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"agent_id\": 812,\n      \"key\": \"einschaetzen\",\n      \"felder\": {\n          \"anweisung\": \"Beurteile anhand der Anfrage, ob sie innerhalb von 48 Stunden beantwortet werden sollte.\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"type":"integer","description":"ID des Agenten als ganze Zahl. Du bekommst sie aus `agent_entwurf` (Feld `agent_id`) oder `agenten_liste` (Feld `id`). Ändern darf den Agenten nur sein Ersteller oder ein Administrator der Firma; eine fremde, archivierte oder unbekannte ID liefert `success: false` mit „Diesen Agenten gibt es nicht.“","example":812},"key":{"type":"string","description":"Key des Schritts, der geändert werden soll, z. B. `einschaetzen`. Die Keys stehen in der Liste `schritte` jeder Antwort und in `agent_lesen`.","example":"einschaetzen"},"felder":{"type":"object","description":"Objekt mit den zu ändernden Feldern, z. B. `{\"anweisung\": \"…\"}`, `{\"parameter\": {…}}` oder `{\"titel\": \"…\"}`. Unbekannte Felder des Schritttyps werden bei der Prüfung verworfen; `key` und `typ` in diesem Objekt werden ignoriert.","example":{"anweisung":"Beurteile anhand der Anfrage, ob sie innerhalb von 48 Stunden beantwortet werden sollte."}}},"required":["agent_id","key","felder"]},"example":{"agent_id":812,"key":"einschaetzen","felder":{"anweisung":"Beurteile anhand der Anfrage, ob sie innerhalb von 48 Stunden beantwortet werden sollte."}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_schritt_aendern","stufe":"schreiben","data":{"success":true,"agent_id":812,"name":"Anfragen sichten","revision":5,"status":"entwurf","schritte":[{"key":"lead_laden","typ":"werkzeug","titel":"Anfrage laden"},{"key":"einschaetzen","typ":"ki","titel":"Dringlichkeit einschätzen"},{"key":"pruefen","typ":"entscheidung","titel":"Dringend?"},{"key":"status_setzen","typ":"werkzeug","titel":"Status setzen"}],"briefing":{"ziel":"Neue Anfragen sichten und dringende Fälle markieren.","fertig_wenn":"Jede neue Anfrage ist eingeschätzt und dringende Anfragen tragen den Status „contacted“."},"leitplanken":["Nie Anfragen löschen.","Immer zuerst die Anfrage lesen, dann entscheiden."],"hinweis":"Gespeichert."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_schritt_entfernen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_schritt_entfernen","summary":"Schritt entfernen","description":"Entfernt einen Schritt aus dem Agenten-Entwurf. Die übrigen Schritte rücken nach; wer keinen eigenen Sprung (`weiter`) hat, folgt dann auf den neuen Vorgänger. Verweist noch ein anderer Schritt per `weiter`, `dann` oder `sonst` auf den entfernten Key, schlägt die Prüfung mit „Das Sprungziel „…“ gibt es nicht.“ fehl, und nichts wird gespeichert: biege zuerst das Sprungziel mit `agent_schritt_aendern` um. Die Antwort ist der Kurzstand des Agenten mit neuer `revision`, `status` und der verbleibenden Liste `schritte`. Ein unbekannter Key ergibt „Den Schritt „…“ gibt es nicht.“ Der Entwurf darf danach leer sein; veröffentlichen lässt er sich dann nicht.\n\n**Gut zu wissen**\n\n- Werkzeuge, die nur der entfernte Schritt benutzte, bleiben in der Werkzeugliste stehen.\n- Jede Änderung ist eine eigene Revision des Entwurfs; den aktuellen Stand zeigt `agent_lesen`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_schritt_entfernen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"agent_id\": 812,\n    \"key\": \"freigabe\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_schritt_entfernen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"agent_id\": 812,\n        \"key\": \"freigabe\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_schritt_entfernen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"agent_id\": 812,\n      \"key\": \"freigabe\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"type":"integer","description":"ID des Agenten als ganze Zahl. Du bekommst sie aus `agent_entwurf` (Feld `agent_id`) oder `agenten_liste` (Feld `id`). Ändern darf den Agenten nur sein Ersteller oder ein Administrator der Firma; eine fremde, archivierte oder unbekannte ID liefert `success: false` mit „Diesen Agenten gibt es nicht.“","example":812},"key":{"type":"string","description":"Key des Schritts, der entfernt werden soll, z. B. `freigabe`. Er steht in der Liste `schritte` jeder Antwort und in `agent_lesen`.","example":"freigabe"}},"required":["agent_id","key"]},"example":{"agent_id":812,"key":"freigabe"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_schritt_entfernen","stufe":"schreiben","data":{"success":true,"agent_id":812,"name":"Anfragen sichten","revision":6,"status":"entwurf","schritte":[{"key":"lead_laden","typ":"werkzeug","titel":"Anfrage laden"},{"key":"einschaetzen","typ":"ki","titel":"Dringlichkeit einschätzen"},{"key":"pruefen","typ":"entscheidung","titel":"Dringend?"},{"key":"status_setzen","typ":"werkzeug","titel":"Status setzen"}],"briefing":{"ziel":"Neue Anfragen sichten und dringende Fälle markieren.","fertig_wenn":"Jede neue Anfrage ist eingeschätzt und dringende Anfragen tragen den Status „contacted“."},"leitplanken":["Nie Anfragen löschen.","Immer zuerst die Anfrage lesen, dann entscheiden."],"hinweis":"Gespeichert."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_lesen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_lesen","summary":"Agenten lesen","description":"Liest einen Agenten vollständig. Als Verwalter bekommst du den Entwurf, andernfalls die veröffentlichte Version, sofern sie für dich freigegeben ist. Die Antwort enthält `agent_id`, `name`, `status` (`entwurf`, `aktiv` oder `pausiert`), `version` (Nummer der veröffentlichten Version oder `null`), `revision` (Stand des Entwurfs) und `doc` mit allem Inhalt: `anweisungen`, `briefing`, `leitplanken`, `schritte`, `werkzeuge`, `ausloeser`, `budget`, `modell` (als Stufe), `benachrichtigung` und weitere optionale Felder. Nutze die Funktion, um nach Änderungen den Stand zu kontrollieren oder um Schritt-Keys für `agent_schritt_aendern` zu finden.\n\n**Gut zu wissen**\n\n- Archivierte oder fremde Agenten melden „Diesen Agenten gibt es nicht.“\n- `status` `aktiv` bedeutet: Es gibt eine veröffentlichte Version, die laufen darf; `pausiert` stoppt Zeitpläne, Ereignisse und Starts.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_lesen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"agent_id\": 812\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_lesen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"agent_id\": 812,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_lesen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"agent_id\": 812\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"type":"integer","description":"ID des Agenten als ganze Zahl. Du bekommst sie aus `agent_entwurf` (Feld `agent_id`) oder `agenten_liste` (Feld `id`). Ändern darf den Agenten nur sein Ersteller oder ein Administrator der Firma; eine fremde, archivierte oder unbekannte ID liefert `success: false` mit „Diesen Agenten gibt es nicht.“","example":812}},"required":["agent_id"]},"example":{"agent_id":812}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_lesen","stufe":"lesen","data":{"success":true,"agent_id":812,"name":"Anfragen sichten","status":"entwurf","version":null,"revision":3,"doc":{"anweisungen":"Dringend sind Anfragen mit Fristen unter 48 Stunden.","briefing":{"ziel":"Neue Anfragen sichten und dringende Fälle markieren.","fertig_wenn":"Jede neue Anfrage ist eingeschätzt und dringende Anfragen tragen den Status „contacted“."},"leitplanken":["Nie Anfragen löschen.","Immer zuerst die Anfrage lesen, dann entscheiden."],"schritte":[{"key":"lead_laden","typ":"werkzeug","titel":"Anfrage laden","werkzeug":"get_lead","parameter":{"lead_id":"{{eingabe.lead_id}}"}},{"key":"einschaetzen","typ":"ki","titel":"Dringlichkeit einschätzen","anweisung":"Beurteile anhand der Anfrage, ob sie innerhalb eines Tages beantwortet werden sollte.","eingaben":{"anfrage":"{{schritte.lead_laden}}"},"ergebnis":[{"name":"dringend","art":"janein","beschreibung":"Ja, wenn die Anfrage innerhalb eines Tages beantwortet werden sollte."}]},{"key":"pruefen","typ":"entscheidung","titel":"Dringend?","bedingung":{"wert":"{{schritte.einschaetzen.dringend}}","op":"angehakt"},"dann":"status_setzen","sonst":"ende"},{"key":"status_setzen","typ":"werkzeug","titel":"Status setzen","werkzeug":"change_lead_status","parameter":{"lead_id":"{{eingabe.lead_id}}","status":"contacted"}}],"ausloeser":{"hand":true,"ereignisse":["lead.neu"]},"budget":{"schritte":30,"ki":10,"minuten":10},"werkzeuge":[{"name":"get_lead","ohne_rueckfrage":true,"aussen_bestaetigt":false},{"name":"change_lead_status","ohne_rueckfrage":false,"aussen_bestaetigt":false}],"felder":[],"benachrichtigung":{"glocke":true,"email":false,"auch_fehler":true}}}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agenten_liste":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agenten_liste","summary":"Agenten auflisten","description":"Listet die Agenten deiner Firma, die dein Schlüssel sehen darf: alle, die du verwalten darfst (auch Entwürfe), und fremde, sofern sie veröffentlicht und für dich freigegeben sind. Jeder Eintrag hat `id`, `name`, `status` (`entwurf`, `aktiv` oder `pausiert`), `letzter_lauf` (`id`, `status`, `created_at`, `beendet_at` des jüngsten echten Laufs, Simulationen zählen nicht; `null`, wenn es noch keinen gab) und `naechster_termin` (nächster Zeitplan-Start als `JJJJ-MM-TT HH:MM`, nur bei aktiven Agenten mit Zeitplan, sonst `null`). Archivierte Agenten fehlen. Es gibt keine Seitenaufteilung. Mit `id` bzw. `name` rufst du `agent_starten` auf.\n\n**Gut zu wissen**\n\n- Der Name in `agent_starten` darf auch nur ein Teil sein, muss aber genau einen Agenten treffen; die Liste hilft, Namen eindeutig zu halten.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agenten_liste \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agenten_liste\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agenten_liste\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agenten_liste","stufe":"lesen","data":{"success":true,"agenten":[{"id":812,"name":"Anfragen sichten","status":"aktiv","letzter_lauf":{"id":9120,"status":"fertig","created_at":"2026-11-08 14:02:11","beendet_at":"2026-11-08 14:02:49"},"naechster_termin":"2026-11-10 07:30"}]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_starten":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_starten","summary":"Agenten starten oder simulieren","description":"Startet einen Lauf. Ohne `simulation` läuft die veröffentlichte Version eines aktiven Agenten; ist er noch nicht veröffentlicht oder pausiert, kommt ein entsprechender Satz in `error`. Mit `simulation: true` läuft stattdessen eine Kopie des gespeicherten Entwurfs als Probe (auch unveröffentlicht oder pausiert, nur für Verwalter): Lesende Werkzeuge und KI-Schritte laufen echt, Schreibendes wird nur im Protokoll angezeigt, Fragen gelten als „Ja“. Den Agenten benennst du mit `agent_id` oder mit einem Namen bzw. Namensteil (muss genau einen treffen). Die Antwort enthält `lauf_id`, `simulation` und `satz`. Der Lauf wird eingereiht und asynchron abgearbeitet; den Fortschritt liefert `agent_lauf_status`.\n\n**Gut zu wissen**\n\n- Verbraucht KI-Rechenleistung. Das gilt auch für Simulationen.\n- Ein echter Lauf führt die Schritte tatsächlich aus: Je nach Agent verändert er Daten, schickt Nachrichten oder startet Anrufe. Teste Änderungen deshalb zuerst mit `simulation: true`.\n- Schreibende Werkzeuge fragen im echten Lauf nach (siehe `agent_rueckfragen`), sofern der Agent sie nicht ausdrücklich ohne Rückfrage zulässt.\n- Eine Simulation prüft den Entwurf wie beim Veröffentlichen; bei Fehlern im Entwurf oder ohne Schritte startet sie nicht.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_starten \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"agent_id\": 812,\n    \"eingabe\": {\n        \"lead_id\": 2088\n    },\n    \"simulation\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_starten\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"agent_id\": 812,\n        \"eingabe\": {\n            \"lead_id\": 2088,\n        },\n        \"simulation\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_starten\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"agent_id\": 812,\n      \"eingabe\": {\n          \"lead_id\": 2088\n      },\n      \"simulation\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"type":"integer","description":"ID des Agenten als ganze Zahl (aus `agent_entwurf` oder `agenten_liste`). Alternativ kannst du `name` angeben. Fehlen beide oder passt keiner, kommt „Diesen Agenten gibt es nicht.“ bzw. „Einen Agenten mit diesem Namen gibt es nicht.“","example":812},"name":{"type":"string","description":"Name oder Namensteil des Agenten, Groß-/Kleinschreibung egal. Es muss genau ein sichtbarer Agent passen; bei mehreren Treffern nennt `error` die Namen und fragt, welcher gemeint ist. Wird nur ausgewertet, wenn `agent_id` fehlt."},"eingabe":{"type":"object","description":"Objekt mit den Eingabewerten des Laufs. Im Ablauf sind sie als `{{eingabe.<name>}}` abrufbar, z. B. `{\"lead_id\": 2088}` als `{{eingabe.lead_id}}`. Passwörter und ähnliche Felder werden im Protokoll geschwärzt. Ohne Angabe ist die Eingabe leer.","example":{"lead_id":2088}},"simulation":{"type":"boolean","description":"Boolean, Standard `false`. Bei `true` läuft der gespeicherte Entwurf als Probe: nichts wird verändert, nichts geht nach außen, Rückfragen werden automatisch bejaht. Nur Verwalter des Agenten dürfen das („Simulieren dürfen nur die Verwalter des Agenten.“).","example":true}}},"example":{"agent_id":812,"eingabe":{"lead_id":2088},"simulation":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_starten","stufe":"schreiben","data":{"success":true,"lauf_id":9120,"simulation":true,"satz":"Simulation gestartet — es wird nichts verändert. Lauf 9120. Den Stand siehst du unter #/agenten/812/lauf/9120."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_lauf_status":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_lauf_status","summary":"Stand eines Laufs","description":"Liefert den Stand eines Laufs in einem Satz und mit allen bisherigen Schritten. `status` ist einer von `wartet_auf_start` (in der Warteschlange), `laeuft`, `wartet` (auf eine Antwort eines Menschen oder ein äußeres Ereignis), `angehalten`, `fertig`, `fehler` oder `abgebrochen`. `satz` fasst das lesbar zusammen und nennt bei Wartezeiten die offene Rückfrage; bei `angehalten` und `fehler` steht der Grund dabei. `schritte` listet jede Ausführung mit `key`, `status` (`laeuft`, `fertig`, `fehler`, `wartet`, `uebersprungen`) und `fehler`; ein wiederholter Schritt erscheint mehrfach. `ergebnis` ist das Ergebnis des letzten Schritts, sobald der Lauf `fertig` ist, sonst leer.\n\n**Gut zu wissen**\n\n- Frage den Status in Abständen von einigen Sekunden ab; ein Lauf wird asynchron von einem Hintergrundprozess bearbeitet.\n- Sehen darf einen Lauf, wer ihn gestartet hat, und wer den Agenten sehen darf; sonst kommt „Diesen Lauf gibt es nicht.“\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_lauf_status \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"lauf_id\": 9120\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_lauf_status\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"lauf_id\": 9120,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_lauf_status\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"lauf_id\": 9120\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lauf_id":{"type":"integer","description":"ID des Laufs als ganze Zahl. Du bekommst sie aus der Antwort von `agent_starten` (Feld `lauf_id`) oder aus `agent_rueckfragen` (Feld `lauf_id`). Eingreifen darf die Person, die den Lauf gestartet hat, sowie die Verwalter des Agenten.","example":9120}},"required":["lauf_id"]},"example":{"lauf_id":9120}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_lauf_status","stufe":"lesen","data":{"success":true,"status":"fertig","satz":"Lauf 9120 von „Anfragen sichten“: fertig.","schritte":[{"key":"lead_laden","status":"fertig","fehler":null},{"key":"einschaetzen","status":"fertig","fehler":null},{"key":"pruefen","status":"fertig","fehler":null}],"ergebnis":{"erfuellt":false}}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_rueckfragen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_rueckfragen","summary":"Offene Rückfragen der Agenten","description":"Listet alle offenen Rückfragen von Agenten-Läufen, die auf eine Antwort warten. `rueckfragen` enthält je Frage `id`, `agent` (Name), `lauf_id`, `schritt` (Key des wartenden Schritts), `art`, `satz` (die Frage im Klartext) und `parameter`. `art` ist `werkzeug`, wenn der Agent ein schreibendes Werkzeug ausführen möchte und deshalb fragt (dann zeigt `parameter` die geplanten Werte), oder `frage`, wenn ein `frage`-Schritt eine Freigabe oder Eingabe verlangt. Sichtbar sind Fragen der Läufe, die du gestartet hast, und der Agenten, die du verwalten darfst. Beantworten kannst du sie mit die Oberfläche.\n\n**Gut zu wissen**\n\n- Nur Läufe im Status `wartet` tauchen auf; sobald die Frage beantwortet ist, verschwindet sie aus der Liste.\n- Prüfe bei `art: werkzeug` die `parameter` genau, bevor du zustimmst: Sie sind die Werte, mit denen das Werkzeug tatsächlich läuft.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_rueckfragen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_rueckfragen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_rueckfragen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_rueckfragen","stufe":"lesen","data":{"success":true,"rueckfragen":[{"id":311,"agent":"Anfragen sichten","lauf_id":9120,"schritt":"status_setzen","art":"werkzeug","satz":"Darf ich „change_lead_status“ ausführen? (Aktualisiere Status- oder Pipeline-Informationen eines Leads)","parameter":{"lead_id":2088,"status":"contacted"}}]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_anhalten":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_anhalten","summary":"Lauf anhalten","description":"Hält einen Lauf an. Läuft er gerade, wird nur ein Anhalten-Wunsch gesetzt: der aktuelle Schritt wird zu Ende geführt, dann stoppt der Lauf mit dem Status `angehalten` („Von Hand angehalten.“). Wartet der Lauf in der Warteschlange oder auf eine Antwort (`wartet_auf_start`, `wartet`), wird er sofort `angehalten`. Bei einem beendeten Lauf (`fertig`, `fehler`, `abgebrochen`) passiert nichts. Die Antwort enthält den Status direkt nach dem Aufruf. Bei einem laufenden Lauf ist das noch `laeuft`; ob er angehalten hat, siehst du mit `agent_lauf_status`. Anhalten dürfen die Person, die den Lauf gestartet hat, und die Verwalter des Agenten.\n\n**Gut zu wissen**\n\n- Mit `agent_fortsetzen` machst du an derselben Stelle weiter.\n- Bereits ausgeführte Schritte und deren Wirkung werden nicht zurückgenommen.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_anhalten \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"lauf_id\": 9120\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_anhalten\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"lauf_id\": 9120,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_anhalten\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"lauf_id\": 9120\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lauf_id":{"type":"integer","description":"ID des Laufs als ganze Zahl. Du bekommst sie aus der Antwort von `agent_starten` (Feld `lauf_id`) oder aus `agent_rueckfragen` (Feld `lauf_id`). Eingreifen darf die Person, die den Lauf gestartet hat, sowie die Verwalter des Agenten.","example":9120}},"required":["lauf_id"]},"example":{"lauf_id":9120}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_anhalten","stufe":"schreiben","data":{"success":true,"status":"laeuft"}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/baukasten/agent_fortsetzen":{"post":{"tags":["Agenten und Anwendungen"],"operationId":"baukasten__agent_fortsetzen","summary":"Lauf fortsetzen","description":"Setzt einen angehaltenen oder gescheiterten Lauf fort. Der Lauf wird erneut eingereiht und beginnt wieder an dem Schritt, an dem er stand; bei einem gescheiterten Lauf wird also dieser Schritt wiederholt. Bei jedem anderen Status (z. B. `laeuft`, `wartet`, `fertig`) kommt `success: false` mit dem Satz „Fortsetzen geht nur bei einem angehaltenen oder gescheiterten Lauf.“ Ein Lauf, der auf eine Antwort wartet, wird nicht fortgesetzt, sondern beantwortet (die Antwort in der Oberfläche). Die Antwort enthält den neuen Status (meist `wartet_auf_start`). Fortsetzen dürfen die Person, die den Lauf gestartet hat, und die Verwalter des Agenten.\n\n**Gut zu wissen**\n\n- Der Lauf kann weitere KI-Aufrufe und Werkzeuge verbrauchen.\n- Die Zähler für Schritte und KI-Aufrufe bleiben erhalten: Ein wegen „Budget erreicht“ angehaltener Lauf stößt nach dem Fortsetzen sofort wieder an sein Budget.\n- Ein gescheiterter Schritt wird mit denselben Eingaben wiederholt; behebe vorher die Ursache, die in `agent_lauf_status` steht.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/baukasten/agent_fortsetzen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"lauf_id\": 9120\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/baukasten/agent_fortsetzen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"lauf_id\": 9120,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/baukasten/agent_fortsetzen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"lauf_id\": 9120\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lauf_id":{"type":"integer","description":"ID des Laufs als ganze Zahl. Du bekommst sie aus der Antwort von `agent_starten` (Feld `lauf_id`) oder aus `agent_rueckfragen` (Feld `lauf_id`). Eingreifen darf die Person, die den Lauf gestartet hat, sowie die Verwalter des Agenten.","example":9120}},"required":["lauf_id"]},"example":{"lauf_id":9120}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"baukasten","werkzeug":"agent_fortsetzen","stufe":"schreiben","data":{"success":true,"status":"wartet_auf_start"}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/agent_teams/list_teams":{"post":{"tags":["Agent-Teams"],"operationId":"agent_teams__list_teams","summary":"Agent-Teams auflisten","description":"Listet alle Agent-Teams, die zu deinem Unternehmen gehören und veröffentlicht sind, alphabetisch nach Name. Es gibt keine Seitenaufteilung und keine Filter. Die Antwort steckt in `data` und enthält `count` (Anzahl) und `teams`. Jedes Team hat `id`, `name`, `description`, `companyId`, `memberId`, `teamType` (`optimization`, `implementation`, `process`, `strategy` oder `custom`), `status` (`draft`, `active`, `paused`, `archived`), `sourceInterviewId`, `sourceInsightCategory`, `teamGoal`, `teamContext`, `mcpServersConfig`, `teamMembers` (Liste der Mitglieder), `moderatorConfig`, `createdFrom`, `priority`, `memberCount`, `creationDate` und `modificationDate` (Unix-Zeitstempel). Mit der `id` rufst du die übrigen Funktionen auf.\n\n**Gut zu wissen**\n\n- Zusätzlich zu `data` enthält die Antwort die Felder `message`, `timestamp` und `meta`; das eigentliche Ergebnis steht in `data`.\n- Archivierte Teams erscheinen weiterhin, erkennbar am `status`; es wird nicht danach gefiltert.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/agent_teams/list_teams \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/agent_teams/list_teams\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/agent_teams/list_teams\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"agent_teams","werkzeug":"list_teams","stufe":"lesen","data":{"success":true,"data":{"count":1,"teams":[{"id":3105,"name":"Angebots-Taskforce","description":null,"companyId":9001,"memberId":null,"teamType":"process","status":"draft","sourceInterviewId":null,"sourceInsightCategory":null,"teamGoal":"Angebote in unter 24 Stunden versenden","teamContext":null,"mcpServersConfig":null,"teamMembers":[{"chatAgentId":1201,"teamRole":"Texter","roleDescription":"Formuliert den Angebotstext"},{"chatAgentId":1202,"teamRole":"Prüfer","roleDescription":"Kontrolliert Preise und Fristen"}],"moderatorConfig":null,"createdFrom":"toni_suggestion","priority":null,"memberCount":2,"creationDate":1762680000,"modificationDate":1762680000}]},"message":null,"timestamp":"2026-11-09 10:15:30","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/agent_teams/create_team":{"post":{"tags":["Agent-Teams"],"operationId":"agent_teams__create_team","summary":"Agent-Team anlegen","description":"Legt ein neues Agent-Team an und gibt es zurück. Gespeichert werden Name, Typ, Ziel und die Mitgliederliste; das Team gehört zu deinem Unternehmen, erhält den Status `draft` und die Herkunft `toni_suggestion`. Texte werden vor dem Speichern bereinigt, Sonderzeichen wie `&`, `<` oder Anführungszeichen landen als HTML-Entitäten im Namen. Die Mitglieder werden unverändert als Liste übernommen: Üblich sind Objekte mit `chatAgentId`, optional `avatarId`, `teamRole` und `roleDescription`. Zusätzlich wird das Team in den Wissensgraphen gespiegelt (Verknüpfungen zu Firma, Agenten und Avataren); scheitert das, ist das Team trotzdem angelegt. Die Antwort steckt in `data` und hat die Felder wie bei `list_teams`.\n\n**Gut zu wissen**\n\n- Beschreibung, Status, Kontext und Moderator-Einstellungen lassen sich mit dieser Funktion nicht setzen; das geschieht in der Oberfläche.\n- Der Code prüft nicht, ob die angegebenen `chatAgentId`/`avatarId` existieren oder zu deinem Unternehmen gehören; ungültige Verweise werden erst beim Auflösen beim Auflösen in der Oberfläche übersprungen.\n- Ein Team mit `status` `draft` erscheint trotzdem in `list_teams`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/agent_teams/create_team \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Angebots-Taskforce\",\n    \"team_type\": \"process\",\n    \"goal\": \"Angebote in unter 24 Stunden versenden\",\n    \"members\": [\n        {\n            \"chatAgentId\": 1201,\n            \"teamRole\": \"Texter\",\n            \"roleDescription\": \"Formuliert den Angebotstext\"\n        },\n        {\n            \"chatAgentId\": 1202,\n            \"teamRole\": \"Prüfer\",\n            \"roleDescription\": \"Kontrolliert Preise und Fristen\"\n        }\n    ]\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/agent_teams/create_team\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"name\": \"Angebots-Taskforce\",\n        \"team_type\": \"process\",\n        \"goal\": \"Angebote in unter 24 Stunden versenden\",\n        \"members\": [\n            {\n                \"chatAgentId\": 1201,\n                \"teamRole\": \"Texter\",\n                \"roleDescription\": \"Formuliert den Angebotstext\",\n            },\n            {\n                \"chatAgentId\": 1202,\n                \"teamRole\": \"Prüfer\",\n                \"roleDescription\": \"Kontrolliert Preise und Fristen\",\n            },\n        ],\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/agent_teams/create_team\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"name\": \"Angebots-Taskforce\",\n      \"team_type\": \"process\",\n      \"goal\": \"Angebote in unter 24 Stunden versenden\",\n      \"members\": [\n          {\n              \"chatAgentId\": 1201,\n              \"teamRole\": \"Texter\",\n              \"roleDescription\": \"Formuliert den Angebotstext\"\n          },\n          {\n              \"chatAgentId\": 1202,\n              \"teamRole\": \"Prüfer\",\n              \"roleDescription\": \"Kontrolliert Preise und Fristen\"\n          }\n      ]\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name des Teams, z. B. „Angebots-Taskforce“. Pflichtfeld. Der Text wird getrimmt und HTML-bereinigt gespeichert.","example":"Angebots-Taskforce"},"team_type":{"type":"string","description":"Typ des Teams. Die Klasse kennt `optimization`, `implementation`, `process`, `strategy` und `custom`. Ohne Angabe wird `custom` verwendet. Nutze nur diese Werte.","example":"process"},"goal":{"type":"string","description":"Ziel des Teams in einem oder zwei Sätzen, z. B. „Angebote in unter 24 Stunden versenden“. Wird als `teamGoal` gespeichert und im Team-Chat als Ausgangspunkt benutzt. Ohne Angabe bleibt das Ziel leer.","example":"Angebote in unter 24 Stunden versenden"},"members":{"type":"array","description":"Mitglieder als Liste von Objekten, z. B. `{\"chatAgentId\": 1201, \"teamRole\": \"Texter\", \"roleDescription\": \"Formuliert den Angebotstext\"}`. `chatAgentId` verweist auf einen Chat-Agenten, `avatarId` auf einen Avatar, `teamRole` und `roleDescription` beschreiben die Rolle im Team. Die Objekte werden ohne Prüfung gespeichert. Ohne Angabe ist das Team leer.","items":{"type":"object"},"example":[{"chatAgentId":1201,"teamRole":"Texter","roleDescription":"Formuliert den Angebotstext"},{"chatAgentId":1202,"teamRole":"Prüfer","roleDescription":"Kontrolliert Preise und Fristen"}]}},"required":["name"]},"example":{"name":"Angebots-Taskforce","team_type":"process","goal":"Angebote in unter 24 Stunden versenden","members":[{"chatAgentId":1201,"teamRole":"Texter","roleDescription":"Formuliert den Angebotstext"},{"chatAgentId":1202,"teamRole":"Prüfer","roleDescription":"Kontrolliert Preise und Fristen"}]}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"agent_teams","werkzeug":"create_team","stufe":"schreiben","data":{"success":true,"data":{"id":3105,"name":"Angebots-Taskforce","description":null,"companyId":9001,"memberId":null,"teamType":"process","status":"draft","sourceInterviewId":null,"sourceInsightCategory":null,"teamGoal":"Angebote in unter 24 Stunden versenden","teamContext":null,"mcpServersConfig":null,"teamMembers":[{"chatAgentId":1201,"teamRole":"Texter","roleDescription":"Formuliert den Angebotstext"},{"chatAgentId":1202,"teamRole":"Prüfer","roleDescription":"Kontrolliert Preise und Fristen"}],"moderatorConfig":null,"createdFrom":"toni_suggestion","priority":null,"memberCount":2,"creationDate":1762680000,"modificationDate":1762680000},"message":null,"timestamp":"2026-11-09 10:15:30","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/reflections/get_reflections":{"post":{"tags":["Reflexionen und Benachrichtigungen"],"operationId":"reflections__get_reflections","summary":"Reflexionen auflisten","description":"Listet Reflexionen, neueste zuerst. In `data` stehen `items`, `limit`, `offset` und `total` (Gesamtzahl der Treffer ohne Seitenbegrenzung). Jedes Element enthält `id`, `title`, `status`, `emotion`, `urgency`, `is_private`, `needs_follow_up`, `company_id`, `member_id`, `created_at` und `updated_at`. Ohne `include_private` erscheinen nur nicht private Reflexionen. Über die Plattform-API wird zusätzlich automatisch auf die Reflexionen der Person des Schlüssels eingeschränkt, weil der Server deren `member_id` setzt; Reflexionen anderer Personen deines Unternehmens siehst du hier nicht. Für Details einer Reflexion rufst du anschließend `get_reflection` auf.\n\n**Gut zu wissen**\n\n- Die Dringlichkeit wird als Wert zwischen 0 und 1 gespeichert (z. B. `0.7`). Ein ganzzahliger `urgency_min` wie 7 passt deshalb auf keine Reflexion; 1 passt nur auf den Höchstwert.\n- Die Suche in `search` wird vor der Abfrage HTML-escaped; Sonderzeichen wie `&` oder Anführungszeichen finden daher nichts.\n- Eine Obergrenze für `limit` gibt es nicht; wähle sinnvolle Seitengrößen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/reflections/get_reflections \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"pending\",\n    \"limit\": 2,\n    \"offset\": 0\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/reflections/get_reflections\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"status\": \"pending\",\n        \"limit\": 2,\n        \"offset\": 0,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/reflections/get_reflections\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"status\": \"pending\",\n      \"limit\": 2,\n      \"offset\": 0\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"Optional: exakter Status als Text, z. B. `pending`, `ready`, `in_progress` oder `completed`. Es wird exakt verglichen; ohne Angabe kommen alle Status.","example":"pending"},"emotion":{"type":"string","description":"Optional: exakte Emotion als Text, z. B. `frustration` oder `neutral`. Gefiltert wird auf Gleichheit."},"urgency_min":{"type":"integer","description":"Optional: Mindest-Dringlichkeit als ganze Zahl. Beachte, dass die Dringlichkeit intern zwischen 0 und 1 liegt; praktisch wirkt nur 1 (nur Höchstwerte), 0 deaktiviert den Filter."},"search":{"type":"string","description":"Optional: Suchtext, der als Teilstring in Titel oder Beschreibung gesucht wird. Der Text wird HTML-escaped und getrimmt."},"include_private":{"type":"boolean","description":"Optional (Wahrheitswert, Standard `false`). Mit `true` kommen auch als privat markierte Reflexionen zurück (soweit sie dir gehören); ohne Angabe nur nicht private."},"limit":{"type":"integer","description":"Optional: Höchstzahl der Reflexionen in der Antwort (ganze Zahl, Standard 25, mindestens 1). Eine Obergrenze erzwingt der Code nicht.","example":2},"offset":{"type":"integer","description":"Optional: Anzahl der Reflexionen, die am Anfang übersprungen werden (ganze Zahl, Standard 0). Mit `limit` blätterst du seitenweise.","example":0}}},"example":{"status":"pending","limit":2,"offset":0}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"reflections","werkzeug":"get_reflections","stufe":"lesen","data":{"success":true,"data":{"items":[{"id":512,"title":"Angebotsfreigabe dauert zu lange","status":"pending","emotion":"frustration","urgency":"0.7","is_private":false,"needs_follow_up":true,"company_id":20481,"member_id":7310,"created_at":"2026-03-10T09:12:44+01:00","updated_at":"2026-03-12T14:03:10+01:00"},{"id":498,"title":"Rückrufe bleiben liegen","status":"pending","emotion":"neutral","urgency":"0.5","is_private":false,"needs_follow_up":false,"company_id":20481,"member_id":7310,"created_at":"2026-03-10T09:12:44+01:00","updated_at":"2026-03-12T14:03:10+01:00"}],"limit":2,"offset":0,"total":14},"message":null,"timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/reflections/get_notifications":{"post":{"tags":["Reflexionen und Benachrichtigungen"],"operationId":"reflections__get_notifications","summary":"Benachrichtigungen auflisten","description":"Listet die Benachrichtigungen der Glocke der Person, zu der der Schlüssel gehört, neueste zuerst. In `data` stehen `notifications` und `count` (Anzahl in dieser Antwort). Jede Meldung enthält `id` (Datenbank-ID, die du für andere Funktionen brauchst), `notification_id` (Textkennung), `type`, `title`, `message`, `link`, `icon`, `read` (Wahrheitswert), `created_at` und `data`. Die Quellen sind unter anderem CRM, Wartung, Produktionen und Aufgabenvorschläge. Mit `nur_ungelesen` = `true` kommen nur ungelesene, sonst alle. Das Ergebnis ist auf `limit` Einträge begrenzt (Standard 20, höchstens 100).\n\n**Gut zu wissen**\n\n- Mit `get_notification_count` erfährst du die Zahl ungelesener Meldungen, ohne sie zu laden.\n- Es gibt kein Blättern; ältere Meldungen als das `limit` erreichst du nicht.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/reflections/get_notifications \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"nur_ungelesen\": false,\n    \"limit\": 2\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/reflections/get_notifications\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"nur_ungelesen\": False,\n        \"limit\": 2,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/reflections/get_notifications\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"nur_ungelesen\": false,\n      \"limit\": 2\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"nur_ungelesen":{"type":"boolean","description":"Optional (Wahrheitswert, Standard `false`). Mit `true` werden nur ungelesene Benachrichtigungen geliefert, ebenfalls begrenzt durch `limit`.","example":false},"limit":{"type":"integer","description":"Optional: Höchstzahl der Einträge (ganze Zahl, Standard 20). Werte unter 1 werden zu 1, Werte über 100 zu 100.","example":2}}},"example":{"nur_ungelesen":false,"limit":2}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"reflections","werkzeug":"get_notifications","stufe":"lesen","data":{"success":true,"data":{"notifications":[{"id":9021,"notification_id":"notif_20260318_083012_a1b2c3","type":"crm_lead","title":"Neue Anfrage von Familie Beispiel","message":"Familie Beispiel hat über das Formular ein Angebot angefragt.","link":"/crm","icon":"mail","read":false,"created_at":"2026-03-18 08:30:12","data":[]},{"id":9018,"notification_id":"notif_20260317_160455_d4e5f6","type":"task_suggestion","title":"Aufgabenvorschlag: Angebot nachfassen","message":"Toni schlägt vor, das Angebot an Mustermann GmbH nachzufassen.","link":"/aufgaben","icon":"bell","read":true,"created_at":"2026-03-17 16:04:55","data":[]}],"count":2},"message":null,"timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/reflections/get_notification_count":{"post":{"tags":["Reflexionen und Benachrichtigungen"],"operationId":"reflections__get_notification_count","summary":"Ungelesene Benachrichtigungen zählen","description":"Liefert die Anzahl der ungelesenen Benachrichtigungen der Person, zu der der Schlüssel gehört. In `data` steht ein einziges Feld `count` mit einer ganzen Zahl. Die Funktion hat keine Parameter, verändert nichts und eignet sich für eine kleine Abfrage in kurzen Abständen, etwa um ein Kennzeichen für Neues anzuzeigen. Die Meldungen selbst lädst du bei Bedarf mit `get_notifications`, abgehakt werden sie mit `mark_notification_read`. Ist kein Mitgliedskontext gesetzt, endet die Funktion mit einem Fehler statt mit einer leeren Zahl.\n\n**Gut zu wissen**\n\n- Gezählt werden nur Meldungen mit dem Status ungelesen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/reflections/get_notification_count \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/reflections/get_notification_count\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/reflections/get_notification_count\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"reflections","werkzeug":"get_notification_count","stufe":"lesen","data":{"success":true,"data":{"count":3},"message":null,"timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/reflections/mark_notification_read":{"post":{"tags":["Reflexionen und Benachrichtigungen"],"operationId":"reflections__mark_notification_read","summary":"Benachrichtigung als gelesen markieren","description":"Markiert eine einzelne Benachrichtigung der Person als gelesen oder, mit `alle` = `true`, sämtliche ungelesenen. Bei einer einzelnen Meldung enthält die Antwort `notification_id` und die Meldung `Als gelesen markiert.`. Mit `alle` kommt in `data` die Zahl der geänderten Meldungen unter `marked`. Fremde Meldungen können nicht verändert werden: die Funktion filtert auf die Person des Schlüssels; trifft keine Zeile zu, kommt `success: false` mit `Benachrichtigung nicht gefunden.`. Gibst du weder `notification_id` noch `alle` an, lautet die Fehlermeldung `notification_id oder alle=true ist erforderlich.`.\n\n**Gut zu wissen**\n\n- `alle` wird nur bei dem Wert `true` ausgewertet.\n- Eine bereits gelesene Meldung erneut zu markieren führt zur Meldung `Benachrichtigung nicht gefunden.`, weil keine Zeile geändert wird.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/reflections/mark_notification_read \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"notification_id\": 9021\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/reflections/mark_notification_read\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"notification_id\": 9021,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/reflections/mark_notification_read\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"notification_id\": 9021\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"notification_id":{"type":"integer","description":"Optional: Datenbank-ID der Benachrichtigung (ganze Zahl), wie sie in `get_notifications` unter `notifications[].id` steht. Ohne diese Angabe musst du `alle` setzen.","example":9021},"alle":{"type":"boolean","description":"Optional (Wahrheitswert): mit `true` werden alle ungelesenen Benachrichtigungen der Person als gelesen markiert, `notification_id` wird dann nicht beachtet."}}},"example":{"notification_id":9021}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"reflections","werkzeug":"mark_notification_read","stufe":"schreiben","data":{"success":true,"data":{"notification_id":9021},"message":"Als gelesen markiert.","timestamp":"2026-03-18 11:20:11","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/reflections/create_notification":{"post":{"tags":["Reflexionen und Benachrichtigungen"],"operationId":"reflections__create_notification","summary":"Benachrichtigung anlegen","description":"Legt eine neue, ungelesene Benachrichtigung für die Person des Schlüssels an. Sie erscheint in der Glocke. Pflicht sind `titel` und `nachricht`; sind sie nach dem Trimmen leer, kommt `success: false` mit `titel und nachricht sind erforderlich.`. Ohne Angabe gelten Typ `external` und Symbol `bell`; `link` bleibt dann leer. Alle Textfelder werden HTML-escaped gespeichert, Zeichen wie `&` oder Anführungszeichen erscheinen daher in kodierter Form. Die Antwort enthält die neue `notification_id` (Datenbank-ID) und die Meldung `Benachrichtigung angelegt.`. Die Funktion kann nur Meldungen für die Person des Schlüssels anlegen, nicht für andere.\n\n**Gut zu wissen**\n\n- Der Link wird nicht auf Gültigkeit geprüft; verwende einen internen Pfad wie `/crm`.\n- Nutze die Funktion sparsam, damit die Glocke nicht mit Hinweisen überflutet wird.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/reflections/create_notification \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"titel\": \"Angebot nachfassen\",\n    \"nachricht\": \"Das Angebot an Mustermann GmbH ist seit fünf Tagen offen.\",\n    \"icon\": \"calendar\",\n    \"link\": \"/crm\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/reflections/create_notification\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"titel\": \"Angebot nachfassen\",\n        \"nachricht\": \"Das Angebot an Mustermann GmbH ist seit fünf Tagen offen.\",\n        \"icon\": \"calendar\",\n        \"link\": \"/crm\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/reflections/create_notification\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"titel\": \"Angebot nachfassen\",\n      \"nachricht\": \"Das Angebot an Mustermann GmbH ist seit fünf Tagen offen.\",\n      \"icon\": \"calendar\",\n      \"link\": \"/crm\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"titel":{"type":"string","description":"Kurze Überschrift der Benachrichtigung (Text, Pflichtangabe). Sie wird getrimmt und HTML-escaped.","example":"Angebot nachfassen"},"nachricht":{"type":"string","description":"Fließtext der Benachrichtigung (Pflichtangabe). Er wird getrimmt und HTML-escaped gespeichert.","example":"Das Angebot an Mustermann GmbH ist seit fünf Tagen offen."},"typ":{"type":"string","description":"Optional: technischer Typ als Text, z. B. `crm_lead`. Standard ist `external`. Der Typ dient der Einordnung in der Oberfläche."},"icon":{"type":"string","description":"Optional: Symbolname als Text, z. B. `bell`, `phone`, `mail` oder `calendar`. Standard ist `bell`.","example":"calendar"},"link":{"type":"string","description":"Optional: Ziel beim Anklicken als interner Pfad, z. B. `/crm`. Ohne Angabe ist die Meldung nicht anklickbar.","example":"/crm"}},"required":["titel","nachricht"]},"example":{"titel":"Angebot nachfassen","nachricht":"Das Angebot an Mustermann GmbH ist seit fünf Tagen offen.","icon":"calendar","link":"/crm"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"reflections","werkzeug":"create_notification","stufe":"schreiben","data":{"success":true,"data":{"notification_id":9022},"message":"Benachrichtigung angelegt.","timestamp":"2026-03-18 11:22:40","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/reflections/delete_notification":{"post":{"tags":["Reflexionen und Benachrichtigungen"],"operationId":"reflections__delete_notification","summary":"Benachrichtigung löschen","description":"Löscht eine einzelne Benachrichtigung der Person, zu der der Schlüssel gehört, endgültig. Die Löschung filtert auf die Person; fremde Meldungen bleiben unberührt und führen zu `success: false` mit `Benachrichtigung nicht gefunden.`. Bei Erfolg enthält `data` die gelöschte `notification_id` und `message` lautet `Benachrichtigung gelöscht.`. Die Funktion löscht jeweils nur eine Meldung; ein Löschen aller Meldungen gibt es hier nicht. Die Stufe ist `loeschen`, deshalb musst du `\"bestaetigt\": true` im Körper mitsenden. Wenn du eine Meldung nur aus dem Blick nehmen willst, nutze `mark_notification_read`.\n\n**Gut zu wissen**\n\n- Das Löschen ist endgültig.\n- Die ID ist die Datenbank-ID aus `get_notifications` (Feld `id`), nicht die Textkennung `notification_id`.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/reflections/delete_notification \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"notification_id\": 9018,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/reflections/delete_notification\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"notification_id\": 9018,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/reflections/delete_notification\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"notification_id\": 9018,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"notification_id":{"type":"integer","description":"Datenbank-ID der zu löschenden Benachrichtigung (ganze Zahl, Pflichtangabe), wie sie in `get_notifications` unter `notifications[].id` steht.","example":9018},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["notification_id","bestaetigt"]},"example":{"notification_id":9018,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"reflections","werkzeug":"delete_notification","stufe":"loeschen","data":{"success":true,"data":{"notification_id":9018},"message":"Benachrichtigung gelöscht.","timestamp":"2026-03-18 11:25:05","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/reflections/get_reflection":{"post":{"tags":["Reflexionen und Benachrichtigungen"],"operationId":"reflections__get_reflection","summary":"Reflexion im Detail abrufen","description":"Liefert eine einzelne Reflexion mit den erweiterten Feldern. Zusätzlich zu den Feldern der Liste (`id`, `title`, `status`, `emotion`, `urgency`, `is_private`, `needs_follow_up`, `company_id`, `member_id`, `created_at`, `updated_at`) kommen `description`, `theme`, `potential_action`, `root_cause`, `privacy_level`, sowie `core_insight`, `raw_input` und `processing_status`. Die drei zuletzt genannten Felder werden nur ausgelesen, wenn die Klasse sie besitzt; in der aktuellen Klassendefinition sind sie immer `null`. Die Kernerkenntnis steht als `description` in der Reflexion, das Originaltranskript in den KI-Zusatzdaten. Ist die ID unbekannt oder gehört die Reflexion zu einem anderen Unternehmen, kommt `success: false` mit einer Fehlermeldung.\n\n**Gut zu wissen**\n\n- Reflexionen ohne Unternehmenszuordnung passieren die Mandantenprüfung; verlasse dich nicht darauf, dass fremde IDs abgewiesen werden.\n- Mit `get_reflections` findest du die IDs.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/reflections/get_reflection \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"reflection_id\": 512\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/reflections/get_reflection\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"reflection_id\": 512,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/reflections/get_reflection\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"reflection_id\": 512\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reflection_id":{"type":"integer","description":"ID der Reflexion (ganze Zahl, Pflichtangabe), wie sie in `get_reflections` unter `items[].id` steht. Eine unbekannte ID führt zu `Reflexion nicht gefunden`.","example":512}},"required":["reflection_id"]},"example":{"reflection_id":512}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"reflections","werkzeug":"get_reflection","stufe":"lesen","data":{"success":true,"data":{"id":512,"title":"Angebotsfreigabe dauert zu lange","status":"pending","emotion":"frustration","urgency":"0.7","is_private":false,"needs_follow_up":true,"company_id":20481,"member_id":7310,"created_at":"2026-03-10T09:12:44+01:00","updated_at":"2026-03-12T14:03:10+01:00","description":"Die Angebotsfreigabe dauert zu lange, weil zwei Personen zustimmen müssen.","core_insight":null,"raw_input":null,"theme":"Prozesse","potential_action":"Freigabe auf eine Person reduzieren.","root_cause":"Unklare Zuständigkeit.","privacy_level":null,"processing_status":null},"message":null,"timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/reflections/create_reflection":{"post":{"tags":["Reflexionen und Benachrichtigungen"],"operationId":"reflections__create_reflection","summary":"Reflexion anlegen","description":"Legt eine neue Reflexion für die Person des Schlüssels an. Pflicht sind `title` und `core_insight`. Die Kernerkenntnis wird als Beschreibung gespeichert, das Originaltranskript (`raw_input`) und `source_id` in den Zusatzdaten. Ohne Angaben gelten: Emotion `neutral`, Quelle `assistant`, Dringlichkeit 5 (intern 0,5), Status `pending`; die Reflexion wird veröffentlicht im Ordner `/reflections` abgelegt. Optionale Felder (`description`, `theme`, `status`, `is_private`, `needs_follow_up`, `potential_action`, `root_cause`) werden nach dem Anlegen übernommen. Die Antwort enthält die erweiterte Reflexion und die Meldung `Reflexion erstellt`. Die Person muss zum Unternehmen des Schlüssels gehören.\n\n**Gut zu wissen**\n\n- Wenn du `description` mitgibst, ersetzt sie die als Beschreibung gespeicherte Kernerkenntnis.\n- `urgency` wird nach dem Speichern ein zweites Mal als Rohwert gesetzt (der Code erwartet dafür einen Text zwischen 0 und 1). Eine ganze Zahl kann deshalb zu einem Fehler führen, obwohl die Reflexion bereits gespeichert ist; lass die Angabe im Zweifel weg.\n- `status` wird nicht gegen eine Liste geprüft; üblich sind `pending`, `ready` und `completed`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/reflections/create_reflection \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Angebotsfreigabe dauert zu lange\",\n    \"core_insight\": \"Zwei Freigaben pro Angebot verzögern den Versand um durchschnittlich zwei Tage.\",\n    \"emotion\": \"frustration\",\n    \"theme\": \"Prozesse\",\n    \"needs_follow_up\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/reflections/create_reflection\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"title\": \"Angebotsfreigabe dauert zu lange\",\n        \"core_insight\": \"Zwei Freigaben pro Angebot verzögern den Versand um durchschnittlich zwei Tage.\",\n        \"emotion\": \"frustration\",\n        \"theme\": \"Prozesse\",\n        \"needs_follow_up\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/reflections/create_reflection\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"title\": \"Angebotsfreigabe dauert zu lange\",\n      \"core_insight\": \"Zwei Freigaben pro Angebot verzögern den Versand um durchschnittlich zwei Tage.\",\n      \"emotion\": \"frustration\",\n      \"theme\": \"Prozesse\",\n      \"needs_follow_up\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Kurzer Titel der Reflexion (Pflichtangabe). Er dient auch als Grundlage für den Objektschlüssel.","example":"Angebotsfreigabe dauert zu lange"},"core_insight":{"type":"string","description":"Die Kernerkenntnis in ein bis drei Sätzen (Pflichtangabe). Sie wird als Beschreibung der Reflexion gespeichert, sofern du nicht zusätzlich `description` angibst.","example":"Zwei Freigaben pro Angebot verzögern den Versand um durchschnittlich zwei Tage."},"raw_input":{"type":"string","description":"Optional: Originaltranskript oder Notizen, aus denen die Erkenntnis stammt. Wird in den Zusatzdaten der Reflexion abgelegt."},"description":{"type":"string","description":"Optional: ausführlichere Beschreibung oder Kontext. Überschreibt die aus `core_insight` übernommene Beschreibung."},"emotion":{"type":"string","description":"Optional: Emotion als Text, z. B. `frustration`. Standard ist `neutral`.","example":"frustration"},"urgency":{"type":"integer","description":"Optional: Bewertung 0 bis 10 (ganze Zahl, Standard 5). Der Wert wird beim Anlegen auf eine Skala von 0 bis 1 umgerechnet. Beachte den Hinweis zur doppelten Verarbeitung."},"status":{"type":"string","description":"Optional: Status als Text. Beim Anlegen steht er auf `pending`; ein hier angegebener Wert überschreibt ihn danach (z. B. `ready` oder `completed`)."},"theme":{"type":"string","description":"Optional: Thema oder Kategorie der Reflexion als Text, z. B. `Prozesse`.","example":"Prozesse"},"is_private":{"type":"boolean","description":"Optional (Wahrheitswert): markiert die Reflexion als privat. Private Reflexionen erscheinen in `get_reflections` nur mit `include_private`."},"needs_follow_up":{"type":"boolean","description":"Optional (Wahrheitswert): kennzeichnet, dass eine Nachverfolgung nötig ist.","example":true},"source_type":{"type":"string","description":"Optional: Quelle der Erkenntnis, z. B. `voice_chat`, `note` oder `meeting`. Standard ist `assistant`."},"source_id":{"type":"integer","description":"Optional: ID des Ursprungs (ganze Zahl), z. B. eines Gesprächs. Wird in den Zusatzdaten der Reflexion gespeichert."},"potential_action":{"type":"string","description":"Optional: empfohlene Handlung als Text."},"root_cause":{"type":"string","description":"Optional: vermutete Ursache als Text."}},"required":["title","core_insight"]},"example":{"title":"Angebotsfreigabe dauert zu lange","core_insight":"Zwei Freigaben pro Angebot verzögern den Versand um durchschnittlich zwei Tage.","emotion":"frustration","theme":"Prozesse","needs_follow_up":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"reflections","werkzeug":"create_reflection","stufe":"schreiben","data":{"success":true,"data":{"id":512,"title":"Angebotsfreigabe dauert zu lange","status":"pending","emotion":"frustration","urgency":"0.7","is_private":false,"needs_follow_up":true,"company_id":20481,"member_id":7310,"created_at":"2026-03-10T09:12:44+01:00","updated_at":"2026-03-12T14:03:10+01:00","description":"Zwei Freigaben pro Angebot verzögern den Versand um durchschnittlich zwei Tage.","core_insight":null,"raw_input":null,"theme":"Prozesse","potential_action":"Freigabe auf eine Person reduzieren.","root_cause":"Unklare Zuständigkeit.","privacy_level":null,"processing_status":null},"message":"Reflexion erstellt","timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/reflections/update_reflection":{"post":{"tags":["Reflexionen und Benachrichtigungen"],"operationId":"reflections__update_reflection","summary":"Reflexion aktualisieren","description":"Ändert Felder einer bestehenden Reflexion. In `data` gibst du ein Objekt mit den zu ändernden Feldern an. Übernommen werden nur diese Schlüssel: `description`, `theme`, `status`, `emotion`, `urgency`, `potential_action`, `root_cause`, `privacy_level`, `measured_impact`, `outcomes`, `is_private`, `needs_follow_up` und `processing_status`. Alle anderen Schlüssel (auch `title`) werden ignoriert, Felder mit dem Wert `null` ebenfalls. Ist `data` leer, kommt `success: false` mit `Keine Daten zum Aktualisieren übergeben`. Die Antwort enthält die erweiterte Reflexion und die Meldung `Reflexion aktualisiert`. Die Reflexion muss zu deinem Unternehmen gehören.\n\n**Gut zu wissen**\n\n- Die Funktion kann den Titel und die Zusatzdaten nicht ändern.\n- `urgency` verlangt intern einen Wert zwischen 0 und 1 als Text; eine ganze Zahl kann einen Fehler auslösen.\n- Reflexionen ohne Unternehmenszuordnung werden von der Mandantenprüfung nicht abgewiesen.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/reflections/update_reflection \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"reflection_id\": 512,\n    \"data\": {\n        \"status\": \"completed\",\n        \"needs_follow_up\": false\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/reflections/update_reflection\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"reflection_id\": 512,\n        \"data\": {\n            \"status\": \"completed\",\n            \"needs_follow_up\": False,\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/reflections/update_reflection\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"reflection_id\": 512,\n      \"data\": {\n          \"status\": \"completed\",\n          \"needs_follow_up\": false\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reflection_id":{"type":"integer","description":"ID der Reflexion (ganze Zahl, Pflichtangabe), wie sie in `get_reflections` unter `items[].id` steht.","example":512},"data":{"type":"object","description":"Objekt mit den Feldern, die geändert werden sollen, z. B. `{\"status\": \"completed\", \"measured_impact\": \"Freigabezeit von 2 auf 1 Tag gesunken\"}`. Erlaubt sind die oben genannten Schlüssel; Wahrheitswerte für `is_private` und `needs_follow_up`, sonst Texte.","example":{"status":"completed","needs_follow_up":false}}},"required":["reflection_id","data"]},"example":{"reflection_id":512,"data":{"status":"completed","needs_follow_up":false}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"reflections","werkzeug":"update_reflection","stufe":"schreiben","data":{"success":true,"data":{"id":512,"title":"Angebotsfreigabe dauert zu lange","status":"completed","emotion":"frustration","urgency":"0.7","is_private":false,"needs_follow_up":false,"company_id":20481,"member_id":7310,"created_at":"2026-03-10T09:12:44+01:00","updated_at":"2026-03-12T14:03:10+01:00","description":"Die Angebotsfreigabe dauert zu lange, weil zwei Personen zustimmen müssen.","core_insight":null,"raw_input":null,"theme":"Prozesse","potential_action":"Freigabe auf eine Person reduzieren.","root_cause":"Unklare Zuständigkeit.","privacy_level":null,"processing_status":null},"message":"Reflexion aktualisiert","timestamp":"2026-03-18 11:10:02","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/reflections/delete_reflection":{"post":{"tags":["Reflexionen und Benachrichtigungen"],"operationId":"reflections__delete_reflection","summary":"Reflexion löschen","description":"Löscht eine Reflexion endgültig. Die Funktion lädt die Reflexion, prüft die Zugehörigkeit zum Unternehmen und entfernt das Objekt aus Pimcore; ein Rückgängigmachen gibt es nicht. Bei Erfolg ist `data` leer (`null`) und `message` lautet `Reflexion gelöscht`. Ist die ID unbekannt oder gehört die Reflexion zu einem anderen Unternehmen, kommt `success: false` mit einer Fehlermeldung. Die Stufe der Funktion ist `loeschen`; deshalb musst du im Körper `\"bestaetigt\": true` mitsenden.\n\n**Gut zu wissen**\n\n- Das Löschen ist endgültig.\n- Lies die Reflexion vorher mit `get_reflection`, wenn du sicher sein willst, dass es die richtige ist.\n\n**Recht am Schlüssel:** `loeschen`. Der Aufruf ist unumkehrbar und braucht `\"bestaetigt\": true` im Körper.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/reflections/delete_reflection \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"reflection_id\": 498,\n    \"bestaetigt\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/reflections/delete_reflection\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"reflection_id\": 498,\n        \"bestaetigt\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/reflections/delete_reflection\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"reflection_id\": 498,\n      \"bestaetigt\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"loeschen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reflection_id":{"type":"integer","description":"ID der zu löschenden Reflexion (ganze Zahl, Pflichtangabe), wie sie in `get_reflections` unter `items[].id` steht.","example":498},"bestaetigt":{"type":"boolean","description":"Muss `true` sein, damit die Aktion ausgeführt wird. Ohne Bestätigung antwortet die Schnittstelle mit `409` und führt nichts aus.","example":true}},"required":["reflection_id","bestaetigt"]},"example":{"reflection_id":498,"bestaetigt":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"reflections","werkzeug":"delete_reflection","stufe":"loeschen","data":{"success":true,"data":null,"message":"Reflexion gelöscht","timestamp":"2026-03-18 11:12:30","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „loeschen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"409":{"description":"`\"bestaetigt\": true` fehlt im Körper.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/reflections/get_reflection_statistics":{"post":{"tags":["Reflexionen und Benachrichtigungen"],"operationId":"reflections__get_reflection_statistics","summary":"Statistik der Reflexionen","description":"Liefert aggregierte Kennzahlen zu allen Reflexionen deines Unternehmens. In `data` stehen `total` (Anzahl), `by_status` und `by_emotion` (Zählung je Wert; fehlende Werte erscheinen als `unknown` bzw. `neutral`) sowie `high_urgency` (Anzahl der Reflexionen mit Dringlichkeit ab 7). Die Auswertung geht über alle Reflexionen des Unternehmens, unabhängig von Person und Privat-Markierung. Die Funktion hat keine Parameter und verändert nichts. Bei sehr vielen Reflexionen wird jede einzeln geladen, die Antwortzeit wächst deshalb entsprechend.\n\n**Gut zu wissen**\n\n- Die Dringlichkeit wird zwischen 0 und 1 gespeichert, der Schwellenwert für `high_urgency` liegt aber bei 7; der Zähler bleibt deshalb bei neu angelegten Reflexionen 0.\n- Private Reflexionen anderer Personen fließen in die Zahlen ein (nur als Zählung, nicht als Inhalt).\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/reflections/get_reflection_statistics \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/reflections/get_reflection_statistics\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/reflections/get_reflection_statistics\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"reflections","werkzeug":"get_reflection_statistics","stufe":"lesen","data":{"success":true,"data":{"total":14,"by_status":{"pending":8,"ready":4,"completed":2},"by_emotion":{"frustration":6,"neutral":8},"high_urgency":0},"message":null,"timestamp":"2026-03-18 10:42:15","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/verbindungen/list_email_vorlagen":{"post":{"tags":["E-Mail-Vorlagen und Webhooks"],"operationId":"verbindungen__list_email_vorlagen","summary":"E-Mail-Vorlagen auflisten","description":"Listet die E-Mail-Vorlagen für Mails an Leads, nach Name aufsteigend, höchstens 200. Enthalten sind deine eigenen Vorlagen und Vorlagen, die für alle Firmen gelten (`eigene` ist dann `false`). Nur die Typen Allgemein, Rückruf und Follow-up erscheinen; Vorlagen für Belege, Verträge, Podcast, Termine oder die Toni-App haben eigene Platzhalter und fehlen. Jeder Eintrag in `vorlagen` enthält `id`, `name`, `betreff`, `text` (höchstens 4000 Zeichen), `typ` (`general`, `callback`, `followup`), `typ_label`, `eigene`, `aktiv` und `werte`, die Namen der `{wert.…}`-Platzhalter aus Betreff und Text. Die Antwort steht flach neben `success`, nicht in `data`.\n\n**Gut zu wissen**\n\n- Die `id` brauchst du für `preview_email_vorlage` und `update_email_vorlage`.\n- Vorlagen aller Firmen kannst du lesen und in der Vorschau nutzen, aber nicht ändern.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/verbindungen/list_email_vorlagen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"suche\": \"Rückruf\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/verbindungen/list_email_vorlagen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"suche\": \"Rückruf\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/verbindungen/list_email_vorlagen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"suche\": \"Rückruf\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"suche":{"type":"string","description":"Optionales Stichwort. Es bleiben Vorlagen, in deren Namen oder Betreff das Wort vorkommt (Groß-/Kleinschreibung egal). Ohne Angabe kommen alle Vorlagen.","example":"Rückruf"}}},"example":{"suche":"Rückruf"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"verbindungen","werkzeug":"list_email_vorlagen","stufe":"lesen","data":{"success":true,"vorlagen":[{"id":3412,"name":"Rückruf-Bestätigung","betreff":"Ihr Rückruf am {wert.termin}","text":"{lead.gruss}, wir rufen Sie am {wert.termin} zurück.","typ":"callback","typ_label":"Rückruf","eigene":true,"aktiv":true,"werte":["termin"]}]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/verbindungen/preview_email_vorlage":{"post":{"tags":["E-Mail-Vorlagen und Webhooks"],"operationId":"verbindungen__preview_email_vorlage","summary":"E-Mail-Vorlage als Vorschau füllen","description":"Füllt eine Vorlage und zeigt das Ergebnis, ohne etwas zu senden. Zurück kommt `vorschau` mit `vorlage` (Name), `an` (E-Mail-Adresse des Leads, leer ohne `lead_id`), `betreff` und `text`. Mit `lead_id` werden `{lead.…}`, `{company.…}`, `{member.…}` und `{system.…}` mit echten Werten gefüllt; ohne Lead stehen Beispielwerte wie „Max Mustermann“ darin. Eigene Werte aus `werte` ersetzen `{wert.name}`; unbekannte `{wert.…}` werden leer. Ist die Vorlage fehlerhaft (zum Beispiel eine kaputte Bedingung), kommt ein Fehlertext mit dem Namen der Vorlage.\n\n**Gut zu wissen**\n\n- Es wird keine Mail gesendet und nichts gespeichert.\n- Der Lead muss zu deiner Firma gehören, sonst kommt „Den Lead … gibt es in dieser Firma nicht.“\n- Vorlagen anderer Typen als Allgemein, Rückruf und Follow-up werden abgewiesen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/verbindungen/preview_email_vorlage \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"vorlage_id\": 3412,\n    \"lead_id\": 48211,\n    \"werte\": {\n        \"termin\": \"Dienstag, 10. November, 14 Uhr\"\n    }\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/verbindungen/preview_email_vorlage\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"vorlage_id\": 3412,\n        \"lead_id\": 48211,\n        \"werte\": {\n            \"termin\": \"Dienstag, 10. November, 14 Uhr\",\n        },\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/verbindungen/preview_email_vorlage\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"vorlage_id\": 3412,\n      \"lead_id\": 48211,\n      \"werte\": {\n          \"termin\": \"Dienstag, 10. November, 14 Uhr\"\n      }\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"vorlage_id":{"type":"integer","description":"ID der Vorlage aus `list_email_vorlagen`. Pflicht. Die Vorlage muss dir gehören oder für alle Firmen gelten.","example":3412},"lead_id":{"type":"integer","description":"Optional: ID eines Leads deiner Firma. Dessen Daten füllen die `{lead.…}`-Platzhalter und `an`. Ohne Angabe werden Beispieldaten genutzt.","example":48211},"werte":{"type":"object","description":"Optionales Objekt mit eigenen Werten für `{wert.name}` in Betreff und Text, zum Beispiel `{\"termin\": \"Dienstag, 10. November, 14 Uhr\"}`. Einfache Werte werden als Text eingesetzt, Listen und Objekte als JSON.","example":{"termin":"Dienstag, 10. November, 14 Uhr"}}},"required":["vorlage_id"]},"example":{"vorlage_id":3412,"lead_id":48211,"werte":{"termin":"Dienstag, 10. November, 14 Uhr"}}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"verbindungen","werkzeug":"preview_email_vorlage","stufe":"lesen","data":{"success":true,"vorschau":{"vorlage":"Rückruf-Bestätigung","an":"erika.beispiel@example.com","betreff":"Ihr Rückruf am Dienstag, 10. November, 14 Uhr","text":"Frau Beispiel, wir rufen Sie am Dienstag, 10. November, 14 Uhr zurück."}}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/verbindungen/create_email_vorlage":{"post":{"tags":["E-Mail-Vorlagen und Webhooks"],"operationId":"verbindungen__create_email_vorlage","summary":"E-Mail-Vorlage anlegen","description":"Legt eine neue Vorlage für deine Firma an. Sie ist aktiv, nutzt die Firmensignatur und erscheint auch in den Einstellungen unter E-Mail-Vorlagen. Name (höchstens 120 Zeichen), Betreff und Text sind Pflicht; Betreff und Text werden auf gültige Platzhalter und Bedingungen geprüft, Fehler kommen als Text zurück. Ohne oder mit unbekanntem `typ` wird die Vorlage vom Typ Allgemein. Zurück kommt `vorlage` mit `id`, `name`, `betreff`, `text`, `typ`, `typ_label`, `eigene`, `aktiv` und `werte`, dazu `message` „Vorlage angelegt“.\n\n**Gut zu wissen**\n\n- Die Vorlage wird als reiner Text gespeichert; HTML-Inhalt entsteht erst beim Versand.\n- Der Aufruf ist nicht idempotent: ein zweiter Aufruf mit gleichem Namen legt eine zweite Vorlage an.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/verbindungen/create_email_vorlage \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Rückruf-Bestätigung\",\n    \"betreff\": \"Ihr Rückruf am {wert.termin}\",\n    \"text\": \"{lead.gruss}, wir rufen Sie am {wert.termin} zurück.\",\n    \"typ\": \"callback\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/verbindungen/create_email_vorlage\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"name\": \"Rückruf-Bestätigung\",\n        \"betreff\": \"Ihr Rückruf am {wert.termin}\",\n        \"text\": \"{lead.gruss}, wir rufen Sie am {wert.termin} zurück.\",\n        \"typ\": \"callback\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/verbindungen/create_email_vorlage\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"name\": \"Rückruf-Bestätigung\",\n      \"betreff\": \"Ihr Rückruf am {wert.termin}\",\n      \"text\": \"{lead.gruss}, wir rufen Sie am {wert.termin} zurück.\",\n      \"typ\": \"callback\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Anzeigename der Vorlage, höchstens 120 Zeichen. Pflicht. Er erscheint in den Auswahllisten des Dashboards.","example":"Rückruf-Bestätigung"},"betreff":{"type":"string","description":"Betreffzeile der Mail. Pflicht. Platzhalter wie `{lead.lastname}` und eigene Werte `{wert.name}` sind erlaubt.","example":"Ihr Rückruf am {wert.termin}"},"text":{"type":"string","description":"Text der Mail. Pflicht. Platzhalter: `{lead.gruss}`, `{lead.anrede}`, `{lead.firstname}`, `{lead.lastname}`, `{lead.company}`, `{company.company}`, `{member.name}`, `{system.date}`; eigene Werte als `{wert.name}`; Bedingungen wie `{if lead.titel}…{/if}`.","example":"{lead.gruss}, wir rufen Sie am {wert.termin} zurück."},"typ":{"type":"string","enum":["general","callback","followup"],"description":"Verwendungszweck der Vorlage: `general` (Allgemein), `callback` (Rückruf) oder `followup` (Follow-up). Standard ist `general`.","example":"callback"}},"required":["name","betreff","text"]},"example":{"name":"Rückruf-Bestätigung","betreff":"Ihr Rückruf am {wert.termin}","text":"{lead.gruss}, wir rufen Sie am {wert.termin} zurück.","typ":"callback"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"verbindungen","werkzeug":"create_email_vorlage","stufe":"schreiben","data":{"success":true,"vorlage":{"id":3412,"name":"Rückruf-Bestätigung","betreff":"Ihr Rückruf am {wert.termin}","text":"{lead.gruss}, wir rufen Sie am {wert.termin} zurück.","typ":"callback","typ_label":"Rückruf","eigene":true,"aktiv":true,"werte":["termin"]},"message":"Vorlage angelegt"}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/verbindungen/update_email_vorlage":{"post":{"tags":["E-Mail-Vorlagen und Webhooks"],"operationId":"verbindungen__update_email_vorlage","summary":"E-Mail-Vorlage ändern","description":"Ändert eine eigene Vorlage deiner Firma; nur die übergebenen Felder werden angefasst. Vorlagen, die für alle Firmen gelten, lassen sich nicht ändern: dafür kommt der Fehler „Diese Vorlage gehört allen Firmen und lässt sich hier nicht ändern — leg eine eigene an.“ Betreff und Text werden vor dem Speichern geprüft, auch wenn nur einer der beiden geändert wird. Wird der Text geändert, ersetzt er den bisherigen Klartext. Mit `aktiv: false` schaltest du die Vorlage ab. Zurück kommt die geänderte `vorlage` im selben Aufbau wie bei `list_email_vorlagen`, dazu `message` „Vorlage geändert“.\n\n**Gut zu wissen**\n\n- Der Typ lässt sich über das Schema nicht setzen, obwohl der Server einen übergebenen `typ` (general, callback, followup) übernehmen würde.\n- Eine abgeschaltete Vorlage bleibt in der Liste, mit `aktiv: false`.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/verbindungen/update_email_vorlage \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"vorlage_id\": 3412,\n    \"betreff\": \"Dein Rückruf am {wert.termin}\",\n    \"aktiv\": true\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/verbindungen/update_email_vorlage\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"vorlage_id\": 3412,\n        \"betreff\": \"Dein Rückruf am {wert.termin}\",\n        \"aktiv\": True,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/verbindungen/update_email_vorlage\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"vorlage_id\": 3412,\n      \"betreff\": \"Dein Rückruf am {wert.termin}\",\n      \"aktiv\": true\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"vorlage_id":{"type":"integer","description":"ID der eigenen Vorlage aus `list_email_vorlagen`. Pflicht. Vorlagen anderer Firmen werden als nicht vorhanden gemeldet.","example":3412},"name":{"type":"string","description":"Neuer Anzeigename, höchstens 120 Zeichen. Ein leerer Wert lässt den Namen unverändert."},"betreff":{"type":"string","description":"Neue Betreffzeile. Platzhalter und `{wert.name}` sind erlaubt. Wird sie weggelassen, bleibt der bisherige Betreff.","example":"Dein Rückruf am {wert.termin}"},"text":{"type":"string","description":"Neuer Mailtext mit denselben Platzhaltern und Bedingungen wie bei `create_email_vorlage`. Wird er weggelassen, bleibt der bisherige Text."},"aktiv":{"type":"boolean","description":"`true` schaltet die Vorlage ein, `false` aus. Ohne Angabe bleibt der Zustand unverändert.","example":true}},"required":["vorlage_id"]},"example":{"vorlage_id":3412,"betreff":"Dein Rückruf am {wert.termin}","aktiv":true}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"verbindungen","werkzeug":"update_email_vorlage","stufe":"schreiben","data":{"success":true,"vorlage":{"id":3412,"name":"Rückruf-Bestätigung","betreff":"Dein Rückruf am {wert.termin}","text":"{lead.gruss}, wir rufen Sie am {wert.termin} zurück.","typ":"callback","typ_label":"Rückruf","eigene":true,"aktiv":true,"werte":["termin"]},"message":"Vorlage geändert"}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/verbindungen/list_webhook_ziele":{"post":{"tags":["E-Mail-Vorlagen und Webhooks"],"operationId":"verbindungen__list_webhook_ziele","summary":"Webhook-Ziele auflisten","description":"Listet die Webhook-Ziele deiner Firma, nach Name sortiert. Ein Ziel ist eine Adresse, an die Abläufe Daten schicken dürfen. Jeder Eintrag in `ziele` enthält `id`, `name`, `url`, `aktiv`, `letzter_aufruf_at` und `letzter_status` (jeweils `null`, solange das Ziel nie aufgerufen wurde). Das Signatur-Geheimnis der Ziele wird nicht ausgegeben. Neue Ziele und das Löschen sind dem Menschen im Dashboard unter Agenten → Verbindungen vorbehalten; diese Funktion liest nur. Die Antwort steht flach neben `success`, nicht in `data`.\n\n**Gut zu wissen**\n\n- Die `url` kann ein geheimer Pfad sein (zum Beispiel bei Zapier oder Make); behandle die Antwort vertraulich.\n- Es gibt höchstens 30 Ziele je Firma.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/verbindungen/list_webhook_ziele \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/verbindungen/list_webhook_ziele\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/verbindungen/list_webhook_ziele\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"verbindungen","werkzeug":"list_webhook_ziele","stufe":"lesen","data":{"success":true,"ziele":[{"id":17,"name":"Zapier Leads","url":"https://hooks.example.com/catch/12345/abcde","aktiv":true,"letzter_aufruf_at":"2026-11-08 16:42:10","letzter_status":"200"}]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/verbindungen/create_datei":{"post":{"tags":["E-Mail-Vorlagen und Webhooks"],"operationId":"verbindungen__create_datei","summary":"Datei erzeugen und ablegen","description":"Erzeugt eine Datei und legt sie im Ordner „Ausgabe“ deiner Firma ab (Dateien-Bereich im Dashboard, sichtbar für die Firma, danach durchsuchbar wie ein Upload). Formate: `docx`, `xlsx`, `csv`, `json`, `md`, `txt`. Zurück kommt `datei` mit `datei_id`, `name` (inklusive Endung), `format`, `groesse` in Byte, `ordner` und `link`. Der Dateiname wird bereinigt und auf 120 Zeichen gekürzt; fehlt er, entsteht „Datei <Datum Uhrzeit>“. Die Datei darf höchstens 10 MB groß sein, Tabellen höchstens 20.000 Zeilen. Der Bereich Dateien muss für die Firma freigeschaltet sein, sonst kommt ein Fehlertext.\n\n**Gut zu wissen**\n\n- Bei `csv` und `xlsx` werden Zellen, die mit `=`, `+`, `-` oder `@` beginnen, als Text abgelegt; Formeln aus fremden Daten werden nicht ausgeführt.\n- CSV wird mit Semikolon und BOM geschrieben und öffnet sich so im deutschen Excel richtig.\n- Der `link` ist ein relativer Pfad (`/api/dateien/<id>/inhalt`), kein vollständiger Download-Link mit Host.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/verbindungen/create_datei \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"format\": \"xlsx\",\n    \"name\": \"Leads Oktober\",\n    \"inhalt\": [\n        {\n            \"Name\": \"Erika Beispiel\",\n            \"Firma\": \"Beispiel GmbH\",\n            \"Quelle\": \"Webseite\"\n        },\n        {\n            \"Name\": \"Max Muster\",\n            \"Firma\": \"Muster AG\",\n            \"Quelle\": \"Empfehlung\"\n        }\n    ]\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/verbindungen/create_datei\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"format\": \"xlsx\",\n        \"name\": \"Leads Oktober\",\n        \"inhalt\": [\n            {\n                \"Name\": \"Erika Beispiel\",\n                \"Firma\": \"Beispiel GmbH\",\n                \"Quelle\": \"Webseite\",\n            },\n            {\n                \"Name\": \"Max Muster\",\n                \"Firma\": \"Muster AG\",\n                \"Quelle\": \"Empfehlung\",\n            },\n        ],\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/verbindungen/create_datei\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"format\": \"xlsx\",\n      \"name\": \"Leads Oktober\",\n      \"inhalt\": [\n          {\n              \"Name\": \"Erika Beispiel\",\n              \"Firma\": \"Beispiel GmbH\",\n              \"Quelle\": \"Webseite\"\n          },\n          {\n              \"Name\": \"Max Muster\",\n              \"Firma\": \"Muster AG\",\n              \"Quelle\": \"Empfehlung\"\n          }\n      ]\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"format":{"type":"string","enum":["docx","xlsx","csv","json","md","txt"],"description":"Dateiformat: `docx`, `xlsx`, `csv`, `json`, `md` oder `txt`. Pflicht. Das Format bestimmt, wie `inhalt` gelesen wird.","example":"xlsx"},"name":{"type":"string","description":"Dateiname ohne Endung, zum Beispiel `Leads Oktober`. Pflicht. Sonderzeichen wie `/ \\ : * ? \" < > |` werden durch Leerzeichen ersetzt; die Endung ergänzt die Funktion.","example":"Leads Oktober"},"inhalt":{"description":"Der Inhalt. Bei `txt`, `md`, `docx` ein Text (in `docx` bedeuten `# Überschrift` eine Überschrift, `- Punkt` eine Liste, eine Leerzeile einen neuen Absatz). Bei `csv`, `xlsx` eine Liste von Zeilen: Objekte ergeben Spalten aus den Schlüsseln, Listen von Listen werden direkt übernommen. Bei `xlsx` auch `{\"blaetter\": [{\"name\": \"…\", \"zeilen\": […]}]}` mit bis zu 20 Blättern. Bei `json` beliebige Daten.","example":[{"Name":"Erika Beispiel","Firma":"Beispiel GmbH","Quelle":"Webseite"},{"Name":"Max Muster","Firma":"Muster AG","Quelle":"Empfehlung"}]}},"required":["format","name","inhalt"]},"example":{"format":"xlsx","name":"Leads Oktober","inhalt":[{"Name":"Erika Beispiel","Firma":"Beispiel GmbH","Quelle":"Webseite"},{"Name":"Max Muster","Firma":"Muster AG","Quelle":"Empfehlung"}]}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"verbindungen","werkzeug":"create_datei","stufe":"schreiben","data":{"success":true,"datei":{"datei_id":90211,"name":"Leads Oktober.xlsx","format":"xlsx","groesse":6144,"ordner":"Ausgabe","link":"/api/dateien/90211/inhalt"},"message":"Datei angelegt"}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/kundensupport/kundensupport_suche":{"post":{"tags":["Kundensupport"],"operationId":"kundensupport__kundensupport_suche","summary":"Kundensupport nach Themen durchsuchen","description":"Sucht im Kundensupport deiner Firma nach inhaltlich ähnlichen Texten: Kundennachrichten und Kommentare, eure Antworten und die Postings dazu. Die Suche vergleicht die Bedeutung deiner Formulierung mit den gespeicherten Texten und ergänzt einen kleinen Bonus für wörtliche Treffer. In `anzahl` steht die Zahl der Treffer (höchstens 10), in `treffer` eine Liste kompakter Textzeilen. Jede Zeile nennt Gespräch (oder Posting), Kanal, Datum, Art (`nachricht`, `antwort` oder `beitrag`), den Text und, falls vorhanden, eure Antwort, die zugehörige Frage oder die Adresse des Postings. Es werden nur Daten deiner Firma durchsucht.\n\n**Gut zu wissen**\n\n- Es gibt keine Mindestähnlichkeit: du bekommst immer die bis zu zehn nächstliegenden Texte, auch wenn sie nur entfernt passen.\n- Fällt der lokale Einbettungsdienst aus, kommt eine leere Trefferliste ohne Fehlermeldung.\n- Die Suche rechnet auf dem eigenen Server (lokale Vektoren), ruft keinen externen KI-Dienst auf und verbraucht kein KI-Guthaben.\n- Ein unbekannter `kanal` liefert `anzahl` 0 und den Hinweis „Diesen Kanal gibt es bei dir nicht.“, ein ungültiger Wert für `art` wird ignoriert.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/kundensupport/kundensupport_suche \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"frage\": \"Kommentare zur Lieferzeit\",\n    \"art\": \"nachricht\",\n    \"kanal\": \"instagram\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/kundensupport/kundensupport_suche\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"frage\": \"Kommentare zur Lieferzeit\",\n        \"art\": \"nachricht\",\n        \"kanal\": \"instagram\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/kundensupport/kundensupport_suche\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"frage\": \"Kommentare zur Lieferzeit\",\n      \"art\": \"nachricht\",\n      \"kanal\": \"instagram\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"frage":{"type":"string","description":"Wonach gesucht wird, in eigenen Worten, zum Beispiel `Kommentare zur Lieferzeit`. Pflicht. Ein leerer Text liefert den Fehler „Wonach soll ich suchen?“.","example":"Kommentare zur Lieferzeit"},"art":{"type":"string","enum":["beitrag","nachricht","antwort"],"description":"Optional: `beitrag` (nur Postings), `nachricht` (nur Kundennachrichten und Kommentare) oder `antwort` (nur eure Antworten). Ohne Angabe wird alles durchsucht.","example":"nachricht"},"kanal":{"type":"string","description":"Optional: nur ein Kanal, angegeben als Kanalart wie `email`, `instagram`, `linkedin`, `amazon` oder als Name eines Kanals deiner Firma. Gibt es den Kanal nicht, kommt keine Trefferliste.","example":"instagram"}},"required":["frage"]},"example":{"frage":"Kommentare zur Lieferzeit","art":"nachricht","kanal":"instagram"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"kundensupport","werkzeug":"kundensupport_suche","stufe":"lesen","data":{"success":true,"anzahl":1,"treffer":["Gespräch 4711 · instagram Shop-Profil · 05.11.2026 · nachricht: Wie lange dauert die Lieferung? → unsere Antwort: Meist 2 bis 3 Werktage. [Posting https://www.example.com/p/abc123]"]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/kundensupport/kundensupport_muster":{"post":{"tags":["Kundensupport"],"operationId":"kundensupport__kundensupport_muster","summary":"Wiederkehrende Muster finden","description":"Gruppiert ähnliche Kundennachrichten und Kommentare der letzten Tage zu einem Thema. Die Funktion nimmt die bis zu 60 Nachrichten, die deinem Thema am nächsten liegen, und fasst Texte mit hoher Ähnlichkeit zu Gruppen zusammen. Zurück kommen `tage` (der gewählte Zeitraum), `anzahl_gruppen` (alle gefundenen Gruppen) und `gruppen`, die größten zuerst, höchstens acht. Jede Gruppe hat `anzahl`, ein `beispiel`, bis zu drei Einträge in `weitere` (Datum, Gespräch, Text und eure Antwort) und `postings` mit der Zahl der Kommentare je Posting. Es werden nur Daten deiner Firma gelesen.\n\n**Gut zu wissen**\n\n- Es gibt keine Mindestähnlichkeit zum Thema: auch bei einem Thema ohne echte Treffer kommen Gruppen aus den nächstliegenden Nachrichten.\n- Die Funktion rechnet lokal und verbraucht kein KI-Guthaben. Bei sehr großem Bestand liest sie bis zu 50.000 gespeicherte Vektoren je Aufruf.\n- Es zählen nur eingehende Nachrichten und Kommentare, keine eigenen Antworten.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/kundensupport/kundensupport_muster \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"frage\": \"Fragen unter Postings zum Preis\",\n    \"tage\": 30\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/kundensupport/kundensupport_muster\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"frage\": \"Fragen unter Postings zum Preis\",\n        \"tage\": 30,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/kundensupport/kundensupport_muster\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"frage\": \"Fragen unter Postings zum Preis\",\n      \"tage\": 30\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"frage":{"type":"string","description":"Thema, zu dem Muster gesucht werden, zum Beispiel `Fragen unter Postings zum Preis`. Pflicht. Ein leerer Text liefert den Fehler „Zu welchem Thema soll ich Muster suchen?“.","example":"Fragen unter Postings zum Preis"},"tage":{"type":"integer","description":"Zeitraum in Tagen, den die Nachrichten höchstens zurückliegen dürfen. Ganze Zahl von 1 bis 730, Standard 90. Werte außerhalb werden auf diesen Bereich begrenzt.","example":30}},"required":["frage"]},"example":{"frage":"Fragen unter Postings zum Preis","tage":30}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"kundensupport","werkzeug":"kundensupport_muster","stufe":"lesen","data":{"success":true,"tage":30,"anzahl_gruppen":1,"gruppen":[{"anzahl":3,"beispiel":"Was kostet das Set im Angebot?","weitere":["03.11.2026 · Gespräch 4702 · Was kostet das Set im Angebot? → Antwort: Das Set kostet 49 Euro.","04.11.2026 · Gespräch 4708 · Gibt es das Set auch günstiger?"],"postings":["3× unter https://www.example.com/p/abc123 – Neues Set im Angebot"]}]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/buchhaltung/offene_rechnungen":{"post":{"tags":["Rechnungen lesen"],"operationId":"buchhaltung__offene_rechnungen","summary":"Offene Rechnungen auflisten","description":"Listet die offenen Rechnungen deiner Firma. Als offen gelten Rechnungen und Anzahlungsrechnungen im Status `issued` (gestellt), `overdue` (überfällig) oder `partially_paid` (teilbezahlt), deren Restbetrag größer als null ist. Der Rest ist der Bruttobetrag minus die gebuchten Zahlungen. Sortiert wird nach Fälligkeitsdatum aufsteigend, Rechnungen ohne Fälligkeit stehen am Ende. In `data` stehen `anzahl_gesamt` (alle offenen Rechnungen), `offen_gesamt` (Summe aller offenen Restbeträge, nicht nur der gelisteten) und `rechnungen` mit `nummer`, `kunde`, `belegdatum`, `faellig_am`, `tage_ueberfaellig`, `offen` und `status`. Die Antwort steht flach neben `success`.\n\n**Gut zu wissen**\n\n- `limit` kürzt nur die Liste `rechnungen`; `anzahl_gesamt` und `offen_gesamt` zählen immer alle offenen Rechnungen.\n- `kunde` ist der Name des Rechnungsempfängers (Kundendaten), `tage_ueberfaellig` ist 0, solange die Rechnung nicht fällig ist.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/buchhaltung/offene_rechnungen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"limit\": 5\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/buchhaltung/offene_rechnungen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"limit\": 5,\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/buchhaltung/offene_rechnungen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"limit\": 5\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","description":"Wie viele Rechnungen die Liste höchstens enthält. Ganze Zahl von 1 bis 50, Standard 10. Werte außerhalb werden auf diesen Bereich begrenzt.","example":5}}},"example":{"limit":5}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"buchhaltung","werkzeug":"offene_rechnungen","stufe":"lesen","data":{"success":true,"anzahl_gesamt":2,"offen_gesamt":"1.785,00 €","rechnungen":[{"nummer":"RE-2026-0142","kunde":"Beispiel GmbH","belegdatum":"2026-09-15","faellig_am":"2026-09-29","tage_ueberfaellig":41,"offen":"1.190,00 €","status":"overdue"}]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/buchhaltung/rechnung_lesen":{"post":{"tags":["Rechnungen lesen"],"operationId":"buchhaltung__rechnung_lesen","summary":"Rechnung anhand der Nummer lesen","description":"Liest eine Rechnung oder Anzahlungsrechnung deiner Firma anhand der Rechnungsnummer (genauer Vergleich). Zurück kommt `rechnung` mit `nummer`, `kunde` (Name des Empfängers), `belegdatum`, `faellig_am`, `tage_ueberfaellig`, `offen` (Restbetrag), `status`, `gesamt_brutto` und `positionen`. Jede Position hat `titel`, `menge`, `einheit` und `brutto`. Es gibt keinen Statusfilter: auch bezahlte, stornierte oder noch nicht gestellte Rechnungen werden gelesen; `offen` ist dann `0,00 €` oder der noch offene Rest. Ohne Nummer kommt der Fehler „Nenne mir bitte die Rechnungsnummer.“, bei unbekannter Nummer „Eine Rechnung mit der Nummer … habe ich bei dir nicht gefunden.“\n\n**Gut zu wissen**\n\n- Gesucht wird nur in deiner Firma; eine Nummer einer anderen Firma wird wie eine unbekannte Nummer behandelt.\n- Beträge sind Brutto-Beträge als Text mit Komma und Euro-Zeichen, `menge` ist eine Zahl.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/buchhaltung/rechnung_lesen \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"nummer\": \"RE-2026-0142\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/buchhaltung/rechnung_lesen\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"nummer\": \"RE-2026-0142\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/buchhaltung/rechnung_lesen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"nummer\": \"RE-2026-0142\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"nummer":{"type":"string","description":"Rechnungsnummer genau so, wie sie auf der Rechnung steht, zum Beispiel `RE-2026-0142`. Pflicht. Führende und nachfolgende Leerzeichen werden entfernt, sonst wird exakt verglichen.","example":"RE-2026-0142"}},"required":["nummer"]},"example":{"nummer":"RE-2026-0142"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"buchhaltung","werkzeug":"rechnung_lesen","stufe":"lesen","data":{"success":true,"rechnung":{"nummer":"RE-2026-0142","kunde":"Beispiel GmbH","belegdatum":"2026-09-15","faellig_am":"2026-09-29","tage_ueberfaellig":41,"offen":"1.190,00 €","status":"overdue","gesamt_brutto":"1.190,00 €","positionen":[{"titel":"Beratung Oktober","menge":10,"einheit":"Stunde","brutto":"1.190,00 €"}]}}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/toni_call/list_telefon_agenten":{"post":{"tags":["Telefon-Agenten"],"operationId":"toni_call__list_telefon_agenten","summary":"Telefon-Agenten auflisten","description":"Listet die Agenten deiner Firma, die telefonieren können. Aufgenommen wird ein Agent, wenn er aktiv ist und entweder den Kanal „Telefon“ trägt oder mindestens eine aktive Rufnummer hat. Zurück kommt `agenten`, eine Liste aus `id` (die Agenten-ID), `name` und `nummern` (Rufnummern im internationalen Format wie `+4921155501234`). Es werden höchstens 200 Agenten der Firma betrachtet. Agenten anderer Firmen erscheinen nie. Die Antwort steht flach neben `success`, nicht in `data`.\n\n**Gut zu wissen**\n\n- `nummern` enthält nur aktive Rufnummern des Firmen-Telefonkontos. Ein Agent, der ausschließlich über eine Nebenstelle erreichbar ist, kann mit leerer Liste erscheinen.\n- Die Funktion nimmt keine Parameter entgegen.\n\n**Recht am Schlüssel:** `lesen`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/toni_call/list_telefon_agenten \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/toni_call/list_telefon_agenten\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={},\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/toni_call/list_telefon_agenten\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"lesen","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{}},"example":[]}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"toni_call","werkzeug":"list_telefon_agenten","stufe":"lesen","data":{"success":true,"agenten":[{"id":205514,"name":"Empfang Beispiel GmbH","nummern":["+4921155501234"]}]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „lesen“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/quote/create_invoice_draft":{"post":{"tags":["Angebote und Rechnungsentwürfe"],"operationId":"quote__create_invoice_draft","summary":"Rechnungsentwurf anlegen","description":"Legt einen Rechnungsentwurf für einen Lead deiner Firma an. Der Lead muss zu deiner Firma gehören, sonst kommt „Diesen Kunden (Lead) gibt es in dieser Firma nicht.“ Pro Position zählen `titel` (Pflicht, gekürzt auf 255 Zeichen), `menge` (Standard 1), `preis_netto` (Euro, Standard 0), `steuersatz` (Prozent, Standard 19) und `einheit` (Standard „Stück“, höchstens 30 Zeichen). Positionen ohne Titel werden übersprungen; bleibt keine übrig, kommt der Fehler „Es fehlen Positionen“. Das Belegdatum ist heute. Zurück kommen `document_id`, `gross_total` (Brutto in Cent), `status` (`draft`), `link` und `hinweis`. Die Rechnung wird nicht gestellt und nicht versendet.\n\n**Gut zu wissen**\n\n- Der Aufruf ist nicht idempotent: jeder Aufruf legt einen weiteren Entwurf an.\n- Ein `leistungszeitraum_von` oder `_bis` in einem anderen Format als `JJJJ-MM-TT` wird stillschweigend ignoriert.\n- Die Antwort steht flach neben `success`. `gross_total` ist ein ganzzahliger Centbetrag, kein formatierter Text.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/quote/create_invoice_draft \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"lead_id\": 48211,\n    \"positionen\": [\n        {\n            \"titel\": \"Beratung Prozessoptimierung\",\n            \"menge\": 2.5,\n            \"preis_netto\": 120,\n            \"steuersatz\": 19,\n            \"einheit\": \"Stunde\"\n        }\n    ],\n    \"betreff\": \"Rechnung Beratung Oktober 2026\",\n    \"leistungszeitraum_von\": \"2026-10-01\",\n    \"leistungszeitraum_bis\": \"2026-10-31\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/quote/create_invoice_draft\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"lead_id\": 48211,\n        \"positionen\": [\n            {\n                \"titel\": \"Beratung Prozessoptimierung\",\n                \"menge\": 2.5,\n                \"preis_netto\": 120,\n                \"steuersatz\": 19,\n                \"einheit\": \"Stunde\",\n            },\n        ],\n        \"betreff\": \"Rechnung Beratung Oktober 2026\",\n        \"leistungszeitraum_von\": \"2026-10-01\",\n        \"leistungszeitraum_bis\": \"2026-10-31\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/quote/create_invoice_draft\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"lead_id\": 48211,\n      \"positionen\": [\n          {\n              \"titel\": \"Beratung Prozessoptimierung\",\n              \"menge\": 2.5,\n              \"preis_netto\": 120,\n              \"steuersatz\": 19,\n              \"einheit\": \"Stunde\"\n          }\n      ],\n      \"betreff\": \"Rechnung Beratung Oktober 2026\",\n      \"leistungszeitraum_von\": \"2026-10-01\",\n      \"leistungszeitraum_bis\": \"2026-10-31\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lead_id":{"type":"integer","description":"ID des Leads (Kunden) in deinem CRM. Pflicht. Der Lead liefert Name, Anschrift und E-Mail für den Rechnungsempfänger.","example":48211},"positionen":{"type":"array","items":{"type":"object","properties":{"titel":{"type":"string"},"menge":{"type":"number"},"preis_netto":{"type":"number","description":"Einzelpreis netto in Euro"},"steuersatz":{"type":"number","description":"z. B. 19 oder 7"},"einheit":{"type":"string"}}},"description":"Liste der Rechnungspositionen. Jede Position ist ein Objekt mit `titel` (Text, Pflicht), `menge` (Zahl, Standard 1), `preis_netto` (Einzelpreis netto in Euro), `steuersatz` (Prozent, zum Beispiel 19 oder 7, Standard 19) und `einheit` (Text, Standard „Stück“). Mindestens eine Position mit Titel ist nötig.","example":[{"titel":"Beratung Prozessoptimierung","menge":2.5,"preis_netto":120,"steuersatz":19,"einheit":"Stunde"}]},"betreff":{"type":"string","description":"Optionaler Betreff der Rechnung, höchstens 200 Zeichen. Ohne Angabe bleibt er leer.","example":"Rechnung Beratung Oktober 2026"},"leistungszeitraum_von":{"type":"string","description":"Optional: Beginn des Leistungszeitraums im Format `JJJJ-MM-TT`, zum Beispiel `2026-10-01`. Andere Formate werden ignoriert.","example":"2026-10-01"},"leistungszeitraum_bis":{"type":"string","description":"Optional: Ende des Leistungszeitraums im Format `JJJJ-MM-TT`, zum Beispiel `2026-10-31`. Andere Formate werden ignoriert.","example":"2026-10-31"}},"required":["lead_id","positionen"]},"example":{"lead_id":48211,"positionen":[{"titel":"Beratung Prozessoptimierung","menge":2.5,"preis_netto":120,"steuersatz":19,"einheit":"Stunde"}],"betreff":"Rechnung Beratung Oktober 2026","leistungszeitraum_von":"2026-10-01","leistungszeitraum_bis":"2026-10-31"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"quote","werkzeug":"create_invoice_draft","stufe":"schreiben","data":{"success":true,"document_id":18452,"gross_total":35700,"status":"draft","link":"#/buchhaltung?tab=documents&doc=18452","hinweis":"Nur ein Entwurf — stellen und versenden macht der Nutzer im Beleg-Editor."}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}},"/platform/quote/draft_quote":{"post":{"tags":["Angebote und Rechnungsentwürfe"],"operationId":"quote__draft_quote","summary":"Angebotsentwurf aus Gesprächen erstellen","description":"Erstellt aus deinem Briefing und den bisherigen Gesprächen mit dem Kunden (Anrufe, E-Mails, Interviews) einen Angebotsentwurf. Der Kunde wird über `lead_id` bestimmt, sonst über `company_name` im CRM deiner Firma; findet sich dort nichts und genau ein Eintrag im Kundenbestand des Recruiting, wird daraus ein neuer Lead angelegt. Die Preise setzt der Server aus Kalkulationsgrundlage, Artikeln, Produkten und Angebotskatalog, nicht die KI; Einkaufspreise bleiben intern. Zurück kommen `document_id`, `doc_number`, `net_total` (einmalig, Cent), `recurring_net_total` und `recurring_cycle`, `warnings`, `kalkulation` (Annahmen, offene Punkte, Positionen), `lead_id`, `lead_status` und `link`.\n\n**Gut zu wissen**\n\n- Verbraucht KI-Rechenleistung.\n- Der Aufruf kann deutlich länger als einige Sekunden dauern, weil Positionen und Kundengeschichte nacheinander von der KI erstellt werden.\n- Mehrdeutiger Firmenname: `success` ist `false`, `error` nennt die Kandidaten und `data.candidates` liefert sie mit `id`, `company` und `city` (höchstens 20, `data.has_more` zeigt weitere an). Dann mit `lead_id` wiederholen.\n- Nicht idempotent: jeder erfolgreiche Aufruf legt einen weiteren Entwurf an.\n\n**Recht am Schlüssel:** `schreiben`.\n\n**Beispiel**\n\n```bash\ncurl -X POST https://ki-markt.com/v1/platform/quote/draft_quote \\\n  -H \"Authorization: Bearer $KM_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"briefing\": \"Angebot für die Einführung einer digitalen Wartungsliste an drei Standorten, Start im Januar.\",\n    \"company_name\": \"Beispiel Logistik GmbH\"\n}'\n```\n\n```python\nimport os, requests\n\nantwort = requests.post(\n    \"https://ki-markt.com/v1/platform/quote/draft_quote\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KM_KEY']}\"},\n    json={\n        \"briefing\": \"Angebot für die Einführung einer digitalen Wartungsliste an drei Standorten, Start im Januar.\",\n        \"company_name\": \"Beispiel Logistik GmbH\",\n    },\n    timeout=60,\n)\nprint(antwort.json())\n```\n\n```javascript\nconst antwort = await fetch(\"https://ki-markt.com/v1/platform/quote/draft_quote\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.KM_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n      \"briefing\": \"Angebot für die Einführung einer digitalen Wartungsliste an drei Standorten, Start im Januar.\",\n      \"company_name\": \"Beispiel Logistik GmbH\"\n  }),\n});\nconsole.log(await antwort.json());\n```","x-stufe":"schreiben","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"briefing":{"type":"string","description":"Kurze Beschreibung dessen, was das Angebot enthalten oder berücksichtigen soll, zum Beispiel Umfang, Standorte, Starttermin. Pflicht. Das Briefing steht an erster Stelle der Grundlage und genügt auch, wenn es sonst keine Gespräche gibt.","example":"Angebot für die Einführung einer digitalen Wartungsliste an drei Standorten, Start im Januar."},"lead_id":{"type":"integer","description":"ID des Leads (Kunden) deiner Firma, falls bekannt. Eine fremde oder unbekannte ID gilt als nicht gefunden; ist zusätzlich `company_name` gesetzt, wird damit weitergesucht."},"company_name":{"type":"string","description":"Firmenname des Kunden, falls die `lead_id` nicht bekannt ist; mindestens 2 Zeichen. Ein genauer Treffer (Groß-/Kleinschreibung egal) gewinnt gegen mehrere Teiltreffer, mehrere Teiltreffer ohne genauen Treffer sind mehrdeutig.","example":"Beispiel Logistik GmbH"}},"required":["briefing"]},"example":{"briefing":"Angebot für die Einführung einer digitalen Wartungsliste an drei Standorten, Start im Januar.","company_name":"Beispiel Logistik GmbH"}}}},"responses":{"200":{"description":"Ergebnis der Funktion in der Hülle `platform.result`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlattformErgebnis"},"example":{"object":"platform.result","bereich":"quote","werkzeug":"draft_quote","stufe":"schreiben","data":{"success":true,"data":{"document_id":18453,"doc_number":null,"net_total":290000,"recurring_net_total":4900,"recurring_cycle":"monthly","warnings":["Keine Kalkulationsgrundlage hinterlegt — Positionen ohne Preis, wo keine andere Quelle greift."],"kalkulation":{"annahmen":["Laufzeit 12 Monate laut Gespräch"],"offene_punkte":["Anzahl der Nutzer je Standort klären"],"positionen":[{"name":"Einrichtung digitale Wartungsliste","basis":"calc:0","quantity":1,"unit":"Stück","unit_price_net":290000,"billing_cycle":"once","herleitung":"Kalkulationsgrundlage Position 0","price_missing":false,"produkt":null,"evidence":"call:12 – Wir brauchen die Liste an allen drei Standorten."}]},"lead_id":48211,"lead_status":"found","link":"#/buchhaltung"},"message":"Entwurf #- angelegt (einmalig 2.900,00 € / monatlich 49,00 €) — NICHT versendet. Nenne dem Nutzer Annahmen und offene Punkte und den Link #/buchhaltung.","timestamp":"2026-11-09 10:15:00","meta":[]}}}}},"401":{"description":"Der Schlüssel fehlt, ist falsch oder widerrufen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"403":{"description":"Dem Schlüssel fehlt das Recht „schreiben“ (oder die Anfrage kommt von einer nicht erlaubten Adresse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"422":{"description":"Die Funktion ließ sich nicht ausführen; `message` nennt den Grund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}},"429":{"description":"Zu viele Anfragen pro Minute oder Monatslimit erreicht.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fehler"}}}}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Dein Schlüssel `km_live_…` aus dem Konto unter „API“. Er gehört zu deinem Unternehmen."}},"schemas":{"Fehler":{"type":"object","description":"Einheitliches Fehlerformat. `code` ist ein stabiler Schlüssel zum Auswerten, `message` ein Satz für Menschen.","properties":{"error":{"type":"object","properties":{"message":{"type":"string","description":"Erklärung in einem Satz."},"type":{"type":"string","description":"Art des Fehlers, z. B. invalid_request_error, permission_error, rate_limit_error."},"param":{"type":["string","null"],"description":"Betroffenes Feld, falls es sich benennen lässt."},"code":{"type":["string","null"],"description":"Maschinenlesbarer Schlüssel, z. B. recht_fehlt oder insufficient_quota."}}}},"example":{"error":{"message":"Dieser Schlüssel hat kein Recht für „schreiben“. Das Recht stellst du im Konto unter „API“ am Schlüssel ein.","type":"permission_error","param":null,"code":"recht_fehlt"}}},"PlattformErgebnis":{"type":"object","description":"Hülle jeder Plattform-Funktion. Der Aufbau von `data` hängt von der Funktion ab und steht bei jeder Funktion als Beispiel.","properties":{"object":{"type":"string","example":"platform.result"},"bereich":{"type":"string","description":"Bereich des Aufrufs, z. B. books."},"werkzeug":{"type":"string","description":"Name der aufgerufenen Funktion."},"stufe":{"type":"string","enum":["lesen","schreiben","veroeffentlichen","loeschen"],"description":"Recht, das der Schlüssel für diese Funktion braucht."},"data":{"description":"Rückgabe der Funktion. Fast immer ein Objekt mit `success` (true/false) und den Nutzdaten unter `data`."}}}}}}