Schnittstelle

Die Gridbert-Schnittstelle

Diese Seite dokumentiert dieselbe Schnittstelle, mit der auch app.gridbert.at rechnet. Keine zweite, abgespeckte Variante. Sie ist erzeugt, nicht getippt: Werkzeuge, Endpunkte und Grenzen kommen direkt aus dem Gateway-Code.

Was die Schnittstelle ist

Gridbert bietet zwei Wege zu denselben Werkzeugen: einen MCP-Server (unten) für Chatbots und eigene Agenten, und HTTP-Endpunkte, über die app.gridbert.at und die Kontoseiten selbst laufen. Für wen das relevant ist: alle, die einen eigenen Client, ein Skript oder einen Agenten gegen Gridbert bauen wollen. Für den fertigen Chat-Einstieg gibt es die Anleitungen je Agent.

Verbinden über MCP

Die Adresse für jeden MCP-fähigen Client:

https://mcp.gridbert.at/mcp

Verbindungsaufbau läuft über OAuth 2.1 mit Dynamic Client Registration, die Einrichtung je Agent beschreibt den Klickweg, hier die technischen Eckdaten:

AS-Discoveryhttps://mcp.gridbert.at/.well-known/oauth-authorization-server
Resource-Discoveryhttps://mcp.gridbert.at/.well-known/oauth-protected-resource
JWKShttps://mcp.gridbert.at/jwks.json
Client-Registrierung (DCR)https://mcp.gridbert.at/register
Scopesmcp-tools offline_access
Grant-Typenauthorization_code, refresh_token, client_credentials
PKCES256, Pflicht
Token-Endpunkt-Authnone, client_secret_post (öffentlicher Client)
Zugangs-Token gültig30 Minuten
Refresh-Token gültig30 Tage
Autorisierungs-Code gültig1 Minute
Protokollversion2025-06-18

Unter POST /mcp stehen vier JSON-RPC-Methoden bereit: initialize, notifications/initialized, tools/list, tools/call. Jede Werkzeug-Antwort trägt ihr Ergebnis doppelt: als Text-Block (content) und, wo ein Werkzeug ein Ausgabe-Schema deklariert, zusätzlich strukturiert als structuredContent. Beide sind byte-gleich derselbe Rechenweg, nur einmal als Text und einmal als Objekt.

Werkzeuge

27 Werkzeuge stehen jedem verbundenen Haushaltskonto zur Verfügung. Die Familie zeigt, woher ein Ergebnis kommt:

Gedächtnis liest oder schreibt dein persönliches Profil · Rechenwerk rechnet deterministisch mit energietools · Gridbert-Domäne ist Gateway-eigene Logik (Fakten, Bereitschaft, Rückfragen).

Die Beschreibung je Werkzeug ist wörtlich zitiert, so bekommt sie der Agent.

  • analyze_load_profile Rechenwerk (energietools) nur lesend

    Lastprofil-Metriken (Grundlast, Spitzen, Anomalien, Sparpotenziale) + Ursachen-Signale (E-Heizung, PV-Eigenverbrauch, Dauerläufer) + signal-getriebene Rückfragen-Kandidaten aus einer Lastgang-Serie (lastganganalyse-Prozess, Schritte 'lastprofil_metriken' + 'ursachen_signale'). IMMER als erster Analyse-Schritt nach list_load_series. lastprofil läuft auch auf Tageswerten weiter (Granularitäts-Caveat statt Verweigerung); signale verweigert bei Tageswerten (interval_minutes >= 60) — dann signale=null + signale_grund (Q15-Opt-in-Empfehlung), lastprofil bleibt trotzdem verfügbar. Bei PV-Haushalten greifen automatisch Netzbezug-Guards (basis_label='Netzbezug'). Fakt-vor-Heuristik: jeder Signal-Wert trägt eine quelle + profil_abgleich: quelle='profil'/'rechnung'/'messung' ist ein gespeicherter FAKT und IST die Antwort (z.B. aus submit_lastgang_facts) — quelle='heuristik' ist NUR eine Schätzung aus dem Lastprofil. Bei profil_abgleich.status='widerspruch' den Widerspruch dem User BENENNEN, nie stillschweigend zugunsten der Heuristik auflösen. mit_chartdaten=true liefert zusätzlich verdichtete Chart-Reihen (Stunden-Tagesprofil, Nachtlast je Tag) für Anzeige-Oberflächen — im Chat NICHT nötig, dort das Feld weglassen.

    Eingabe

    is_pv boolean true, falls der Haushalt eine PV-Anlage hat (aktiviert die Netzbezug-Guards).
    mit_chartdaten boolean optional, Default false — für Anzeige-Oberflächen (z.B. die Gridbert-App), NICHT für den Chat. true liefert zusätzlich data.chartdaten: ein Stunden-Tagesprofil (gesamt + Werktag/Wochenende, je 24 Werte) und eine Nachtlastreihe je Tag (max. ~120 Tage) — verdichtete Anzeige-Aggregate, keine Rohserie. Ohne dieses Flag bleibt das Ergebnis unverändert.
    price_per_kwh number optional, brutto EUR/kWh — für Kosten-Kontext (lastprofil)
    pv_feedin_kwh number optional — Summe aus dem Einspeise-serie_ref, falls vorhanden. Ohne Angabe versucht der Gateway automatisch einen zweiten aktiven Einspeise-Consent desselben Accounts zu finden.
    serie_ref* string aus list_load_series/request_data_release

    Ausgabe (oberste Ebene)

    data object Zusammengeführtes Ergebnis (lastgang_analysis.run_analyze_load_profile).
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    meta object | null Herkunft der Rechnung/Daten aus energietools (quelle, stand, snapshot_version) — Inhalt variiert je Capability.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • answer_lastgang_question Gridbert-Domäne

    Speichert die Antwort auf eine offene Lastgang-Rückfrage (ereignis_id aus list_open_lastgang_questions) — entweder label (EXAKT eines der optionen[].label dieser Frage) ODER freitext (frei formuliert, höchstens 200 Zeichen), NIE beides gleichzeitig erfinden: nur übernehmen, was der User wirklich gesagt hat. Liefert danach dieselbe Rückschau wie die Web-Antwortseite — was diese Auffälligkeit seit Beginn der Messwerte gemacht hat, längstens über die letzten zwölf Monate (Tage, kWh, wenn auflösbar Euro) —, damit die Antwort für den User einen sofortigen Gegenwert hat, nicht nur einen 'danke, notiert'. Eine leere rueckschau ist kein Fehler (z.B. ohne Datenfreigabe oder zu wenig Messwerte) — die Antwort ist trotzdem gespeichert. Im Berater-Workspace ist das der Weg, die telefonisch aufgenommene Antwort des Kunden einzupflegen; sie wird als Berater-Angabe vermerkt und dem Haushalt später nicht als seine eigene zurückzitiert. Auch dort gilt: nur übernehmen, was der Kunde wirklich gesagt hat.

    Eingabe

    ereignis_id* integer aus list_open_lastgang_questions, NICHT raten oder selbst vergeben.
    freitext string Frei formulierte Antwort, falls keine Option passt.
    label string Exakt eines der optionen[].label der Frage. Weglassen, wenn stattdessen freitext gesetzt wird.

    Ausgabe (oberste Ebene)

    gespeichert boolean immer true — bei Ablehnung wird NICHTS gespeichert.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    rueckschau array Sätze der Rückschau in Alltagssprache (Tage, kWh, wenn auflösbar Euro). Kann LEER sein — kein Fehler, nur zu wenig Daten/keine Freigabe/kein Preis; die Antwort bleibt trotzdem gespeichert.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • capability_gap Gridbert-Domäne

    Melde einen fehlenden Bedarf, wenn ein Werkzeug für eine Energie-Frage fehlt (Unmet-Demand-Signal).

    Eingabe

    kategorie* string Bereich des fehlenden Bedarfs.
    kurzbeschreibung* string Was hat konkret gefehlt? Zwei bis vier Sätze — welche Frage offen blieb, welche Zahl gebraucht wurde, ob sie extern beschafft werden musste. Keine Zählpunkte, Beträge oder Kontaktdaten.

    Ausgabe (oberste Ebene)

    aufgenommen boolean immer true, wenn das Signal angenommen wurde.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    inhaltstragend boolean immer true — der Freitext wird gespeichert (markierte F12-Ausnahme zur Telemetrie-Regel, redigiert im EventEmitter).
    kategorie string Eingeordnete Kategorie. Eine unbekannte Kategorie fällt bewusst auf 'sonstiges' zurück, statt das Signal zu verwerfen.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • compile_done Gedächtnis (engram)

    Schließt einen geclaimten Compile-Job ab (nach compile_next + upsert_page). keys = die Ziel-Seiten, in die das Artefakt gemergt wurde (jede muss einen [quelle: <item_id>]-Anker tragen, dessen Werte wörtlich in der Quelle stehen — der Server prüft deterministisch); keys=[] = explizites Noise-Signal. Für jede noch untypisierte Ziel-Seite typing[key] = { type, status, relations, confidence } AUSSCHLIESSLICH mit dem T-Box-Vokabular aus dem tbox-Feld der letzten compile_next-Antwort (tbox.types/tbox.relations/tbox.statuses) mitgeben. claim_token aus compile_next.item.claim_token UNVERÄNDERT zurückgeben — er beweist die Lease (sonst lease_lost).

    Eingabe

    claim_token string Fencing-Token aus compile_next.item.claim_token — unverändert zurückgeben.
    item_id* string Das geclaimte Inbox-Item (aus compile_next).
    keys* array Ziel-Seiten-Keys des Merges; [] = Noise.
    typing object Typing je noch untypisierter Ziel-Seite: { "<key>": { "type": "<T-Box-Typ|null>", "status": "<status|null>", "relations": { "<prädikat>": ["<key>"] }, "confidence": 0..1 } }.

    Ausgabe (oberste Ebene)

    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    proxied boolean immer true — die Nutzlast kommt aus der engram-Instanz, nicht aus dem Gateway.
    result object | null Rohes MCP-Tool-Result der Instanz: {content: [...], isError: bool}. Der eigentliche Inhalt steckt als JSON-Text in content[0].text.
    tool string Name des proxierten Vault-Tools.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • compile_next Gedächtnis (engram)

    Claimt genau EIN pending Item + Routing-Index + Zielblätter + Compile-Regeln + Union-T-Box (tbox.types/tbox.relations/tbox.statuses — das VOLLSTÄNDIGE Typ-Vokabular für compile_done.typing steht direkt in diesem Result, kein separater Tool-Call nötig). Leer = nichts zu tun.

    Ausgabe (oberste Ebene)

    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    proxied boolean immer true — die Nutzlast kommt aus der engram-Instanz, nicht aus dem Gateway.
    result object | null Rohes MCP-Tool-Result der Instanz: {content: [...], isError: bool}. Der eigentliche Inhalt steckt als JSON-Text in content[0].text.
    tool string Name des proxierten Vault-Tools.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • energieberatung_info Rechenwerk (energietools) nur lesend

    Kostenlose, produktneutrale Energieberatungsstelle(n) für ein Bundesland (Träger, Kontakt-URL, Quelle). Braucht 'bundesland' ODER 'plz' — die PLZ wird intern aufgelöst; bei mehrdeutiger/unbekannter PLZ kommt eine strukturierte Rückfrage nach 'bundesland'. Reine Informationsauskunft — Gridbert führt keine Beratung durch und vermittelt nicht.

    Eingabe

    bundesland string Bundesland, für das die Beratungsstelle(n) gesucht werden.
    plz string PLZ, aus der das Bundesland aufgelöst wird (Alternative zu bundesland).

    Ausgabe (oberste Ebene)

    beratungsstellen array Je Eintrag: Träger, Kontakt-URL, Quelle.
    bundesland string Aufgelöstes Bundesland.
    ergebnisse array Immer [] — Platzhalter des Leer-Vertrags.
    grund string Warum leer — z.B. 'keine Lastgang-Serien am Account'.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    leer boolean true = Leer-Vertrag (errors.empty_result): kein Fehler, aber auch keine Ergebnisse. Die Nutzdatenfelder dieses Tools fehlen dann.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    stand any Datenstand der Einträge.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • energiegemeinschaften_info Rechenwerk (energietools) nur lesend

    Fakten zu Energiegemeinschaften (GEA, EEG lokal, EEG regional, BEG): Netzentgelt-Vorteile je Rechtsform mit Rechtsquelle und Gültigkeit, die ElWG-Änderung zur Netzentgelt-Reduktion für BEG greift erst ab 31.12.2026 — NICHT schon ab 1.10.2026 (das frühere Datum betrifft nur Begriffs-Definitionen, nicht die Netzentgelt-Bestimmung selbst — diesen Unterschied beim Beantworten nicht verwischen), außerdem Marktstand und die sechs Beitrittsschritte. Optionales 'bundesland' liefert zusätzlich bekannte Verzeichnis-Einträge aus der amtlichen Landkarte — das Verzeichnis ist NICHT vollständig (freiwillig befüllt, deckt nur einen Teil der Bürgerenergiegemeinschaften ab, keine Erneuerbare-Energie-Gemeinschaften); ohne Treffer liefert das Tool einen ehrlichen Hinweis statt einer leeren Behauptung. Sag dem User das Datenstand-Datum dazu. Keine Rechtsberatung.

    Eingabe

    bundesland string Optional — liefert zusätzlich bekannte Verzeichnis-Einträge des Bundeslands.

    Ausgabe (oberste Ebene)

    beitrittsschritte array Die sechs Schritte zum Beitritt.
    bundesland string Nur, wenn eines übergeben wurde.
    elwg_aenderung object | null Strukturiertes Objekt mit den Inkrafttreten-Terminen + Quellen (Netzentgelt-Reduktion für BEG erst ab 31.12.2026).
    ergebnisse array Immer [] — Platzhalter des Leer-Vertrags.
    fazit_haushalt any Kuratiertes 'was muss ich als Haushalt tun'.
    grund string Warum leer — z.B. 'keine Lastgang-Serien am Account'.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    leer boolean true = Leer-Vertrag (errors.empty_result): kein Fehler, aber auch keine Ergebnisse. Die Nutzdatenfelder dieses Tools fehlen dann.
    marktstand any Marktstand in Österreich.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    rechtsformen array GEA / EEG lokal / EEG regional / BEG mit Rechtsquelle und Gültigkeit.
    stand any Datenstand — dem User nennen.
    verzeichnis array Bekannte Verzeichnis-Einträge des Bundeslands — NICHT vollständig.
    verzeichnis_hinweis string Statt einer leeren Liste: warum es keinen Treffer gibt.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • foerderungen_check Rechenwerk (energietools) nur lesend

    Offene, verifizierte Förderungen für PV, Speicher, Heizungstausch, Sanierung, Balkonkraftwerk, Geräte-Reparatur und Wallbox — Bund + das gewählte Bundesland, je mit Fördersatz, Bedingungen, Antrags-Link, Zeitfenster (falls terminiert) und Quelle+Stand. Geschlossene oder unsicher markierte Einträge werden NIE ausgespielt. Braucht 'bundesland' ODER 'plz' — die PLZ wird intern aufgelöst; bei mehrdeutiger/unbekannter PLZ (z.B. eine PLZ, die zwei Bundesländer überspannt) kommt eine strukturierte Rückfrage nach 'bundesland' statt eines geratenen Ergebnisses. Optional 'kategorien' filtert auf einzelne Kategorien, z.B. ['pv','speicher']. Sag dem User das Datenstand-Datum ('stand') dazu — das ist KEINE Rechtsberatung, vor jeder Antragstellung immer die verlinkte Primärquelle prüfen. Am besten NACH einem Tarifvergleich anbieten, nicht davor.

    Eingabe

    bundesland string Bundesland, für das Förderungen gefiltert werden.
    kategorien array Filter, z.B. ['pv','speicher']. Leer/weggelassen = alle Kategorien.
    plz string PLZ, aus der das Bundesland aufgelöst wird (Alternative zu bundesland).

    Ausgabe (oberste Ebene)

    anzahl integer Anzahl ausgelieferter Förderungen.
    bundesland string Aufgelöstes Bundesland.
    ergebnisse array Immer [] — Platzhalter des Leer-Vertrags.
    foerderungen array Je Eintrag: Fördersatz, Bedingungen, Antrags-Link, Zeitfenster, Quelle.
    grund string Warum leer — z.B. 'keine Lastgang-Serien am Account'.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    leer boolean true = Leer-Vertrag (errors.empty_result): kein Fehler, aber auch keine Ergebnisse. Die Nutzdatenfelder dieses Tools fehlen dann.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    stand any Datenstand — dem User nennen. Keine Rechtsberatung.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • get_data_release_status Gridbert-Domäne nur lesend

    Fragt den Status einer Datenfreigabe ab (pending/active/revoked/…) + den aktuellen Datenstand der Serie (lastganganalyse-Prozess, Schritt 'datenfreigabe_status'). WARTE-UX ist PFLICHT: nach jeder Bestätigung im Netzbetreiber-Portal meldet der Netzbetreiber die Freigabe zurück — gemessen war die Hälfte nach rund fünf Stunden durch, einzelne brauchten mehrere Tage (Markt-Latenz, nicht dein Fehler). Sag das dem User ehrlich, versprich KEINE Minutenangabe und poll nicht endlos: Gridbert prüft stündlich und mailt von selbst, sobald sie da ist — und meldet sich auch, wenn eine Anfrage ungewöhnlich lange steht. Erinnere bei 'pending' an die ZWEITE, spätere Freigabe-Anfrage für Stammdaten (Doc §2) — kein Fehler, wenn sie erst Stunden nach der ersten erscheint. Ohne zaehlpunkt liefert es den Status ALLER Freigaben des Accounts. GRANULARITÄT: sobald Daten fließen, sagen 'aufloesung_minuten' und 'q15_aktiv', wie fein sie ankommen. Ist 'q15_empfehlung' gesetzt, liefert der Zähler nur grobe Werte (z. B. Tagessummen) — sprich die Empfehlung dann AKTIV aus: die Viertelstundenauslesung ist ein Opt-in, das der User bei seinem Netzbetreiber anfordern kann, und ohne sie sind Lastgang-Tools (analyze_load_profile, spot_backtest) nicht möglich. Bei 'q15_aktiv': null ist die Auflösung UNBEKANNT (noch keine Daten) — dann nichts über die Granularität behaupten.

    Eingabe

    zaehlpunkt string Optional — ohne Angabe: alle Freigaben des Accounts

    Ausgabe (oberste Ebene)

    ergebnisse array Immer [] — Platzhalter des Leer-Vertrags.
    freigaben array Eine Zeile je Freigabe.
    grund string Warum leer — z.B. 'keine Lastgang-Serien am Account'.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    leer boolean true = Leer-Vertrag (errors.empty_result): kein Fehler, aber auch keine Ergebnisse. Die Nutzdatenfelder dieses Tools fehlen dann.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • get_knowledge Rechenwerk (energietools) nur lesend

    Deterministische Auslieferung der Wissensschicht (wie sich AT-Stromkosten zusammensetzen o. ä.). Reine Text-Auslieferung, kein Rechen-Result. 'thema' ist ein fester Wiki-Slug — Kern-Themen: 'stromkosten-zusammensetzung', 'netz-netzentgelte', 'tarife', 'steuern', 'foerderung', 'glossar'. Bei unbekanntem Thema listet die Fehlermeldung die gültigen Slugs auf.

    Eingabe

    thema* string Wiki-Slug, z.B. 'stromkosten-zusammensetzung' (mit Bindestrich, nicht Unterstrich)

    Ausgabe (oberste Ebene)

    data object Wissens-Artikel der Capability 'get_knowledge'.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    meta object | null Herkunft der Rechnung/Daten aus energietools (quelle, stand, snapshot_version) — Inhalt variiert je Capability.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • get_page Gedächtnis (engram) nur lesend

    Liefert eine Seite der Instanz per Key.

    Eingabe

    key* string Seiten-Key (Slug).

    Ausgabe (oberste Ebene)

    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    proxied boolean immer true — die Nutzlast kommt aus der engram-Instanz, nicht aus dem Gateway.
    result object | null Rohes MCP-Tool-Result der Instanz: {content: [...], isError: bool}. Der eigentliche Inhalt steckt als JSON-Text in content[0].text.
    tool string Name des proxierten Vault-Tools.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • get_switch_info Gridbert-Domäne nur lesend

    Wechsel-Infos (Schritte, Fristen, Kontaktweg) aus Katalogdaten. Reine Information — KEIN Vollmacht-Flow, keine Durchführung.

    Eingabe

    lieferant string Name des Ziel-Lieferanten, optional.

    Ausgabe (oberste Ebene)

    fristen object Rücktrittsrecht, Kündigungsfrist, übliche Wechseldauer.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    hinweis_vollmacht string Klarstellung: Gridbert wechselt NICHT selbst.
    kontakt_hinweis string Nur mit Lieferant: wo der Kontaktweg steht.
    lieferant string Nur, wenn ein Lieferant übergeben wurde.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    schritte array Die generischen AT-Wechselschritte.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • ingest Gedächtnis (engram)

    Schreibt ein RAW-Artefakt (JSON/Text-Fakten-Submission) in den append-only Raw-Store der Instanz (SSOT). Wird später über compile_next kompiliert.

    Eingabe

    content* string Der zu speichernde Inhalt (JSON oder Text).
    content_type string MIME-Type des Inhalts, Default 'application/json'.
    source string Herkunft des Artefakts (frei wählbare Kennung).

    Ausgabe (oberste Ebene)

    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    proxied boolean immer true — die Nutzlast kommt aus der engram-Instanz, nicht aus dem Gateway.
    result object | null Rohes MCP-Tool-Result der Instanz: {content: [...], isError: bool}. Der eigentliche Inhalt steckt als JSON-Text in content[0].text.
    tool string Name des proxierten Vault-Tools.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • list_load_series Gridbert-Domäne nur lesend

    Listet alle Lastgang-Serien am Account (Zählpunkt MASKIERT, Richtung, Zeitraum, Coverage, Granularität, Datenstand — lastganganalyse-Prozess, Schritt 'serien_uebersicht'). IMMER als ERSTER Schritt aufrufen: bevor eine neue Datenfreigabe angefordert wird (kein doppelter Consent-Flow) und bevor ein Analyse-Tool (analyze_load_profile/load_trend/spot_backtest) aufgerufen wird — liefert den serie_ref, den die Analyse-Tools brauchen.

    Ausgabe (oberste Ebene)

    ergebnisse array Immer [] — Platzhalter des Leer-Vertrags.
    grund string Warum leer — z.B. 'keine Lastgang-Serien am Account'.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    leer boolean true = Leer-Vertrag (errors.empty_result): kein Fehler, aber auch keine Ergebnisse. Die Nutzdatenfelder dieses Tools fehlen dann.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    serien array Eine Zeile je Serie.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • list_open_lastgang_questions Gridbert-Domäne nur lesend

    Listet die offenen Rückfragen dieses Kontos zu Lastgang-Auffälligkeiten (z.B. 'was lief da nachts?') — Frage, Optionen und Zeitraum je Ereignis, damit du die Frage im Chat stellen kannst. 'Offen' heißt HIER: noch nicht beantwortet, egal ob schon per Mail verschickt — ein Konto, das im Chat von einem Gerät erzählt, muss dieselbe Frage nicht nochmal per Mail bekommen. Kein Input: das Konto kommt immer aus der Anmeldung, nie als Parameter. Rufe danach answer_lastgang_question mit der ereignis_id auf, sobald der User geantwortet hat.

    Ausgabe (oberste Ebene)

    ergebnisse array Immer [] — Platzhalter des Leer-Vertrags.
    fragen array Eine Zeile je offener Frage.
    grund string Warum leer — z.B. 'keine Lastgang-Serien am Account'.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    leer boolean true = Leer-Vertrag (errors.empty_result): kein Fehler, aber auch keine Ergebnisse. Die Nutzdatenfelder dieses Tools fehlen dann.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • load_trend Rechenwerk (energietools) nur lesend

    Mehrjahres-Trend (YoY) + Treiber-Zerlegung nach Leistungsband x Tageszeit x Werktag/Wochenende aus einer Lastgang-Serie (lastganganalyse-Prozess, Schritte 'mehrjahres_trend' + 'treiber_zerlegung'). trend: Kalender-YoY nur bei >=2 vollen Kalenderjahren (Coverage-Guard) — sonst Fenster-YoY über deckungsgleiche (Monat,Tag,Std,Min)-Slots; NIE ein Teiljahr gegen ein Volljahr stellen; läuft auch auf Tageswerten weiter (Granularitäts-Caveat). attribution benennt je Treiber eine Geräte-KLASSE als Hypothese — NIE einen Gerätenamen (15-min-Grenze, Abnahme-Kriterium 15) — nur mit >=2 Jahren Q15-Historie verfügbar, sonst attribution=null + attribution_grund (trend bleibt trotzdem verfügbar).

    Eingabe

    jahr_a integer attribution-Basisjahr, leer = zweitjüngstes Jahr der Serie
    jahr_b integer attribution-Vergleichsjahr, leer = jüngstes Jahr der Serie
    serie_ref* string aus list_load_series/request_data_release

    Ausgabe (oberste Ebene)

    data object Zusammengeführtes Ergebnis (lastgang_analysis.run_load_trend).
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    meta object | null Herkunft der Rechnung/Daten aus energietools (quelle, stand, snapshot_version) — Inhalt variiert je Capability.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • pv_potenzial Rechenwerk (energietools) nur lesend

    NUR bei vorhandener Lastgang-Serie (serie_ref aus list_load_series) mit Viertelstundenwerten. Beantwortet 'Was würde mir eine PV-Anlage bringen?' auf dem ECHTEN Verbrauchsverlauf statt auf einem Standardprofil: je Anlagengröße Ertrag, Eigenverbrauch, Eigenverbrauchsquote, Autarkie, Einspeisung, vermiedene Netzkosten und Amortisation. Das Sonnenertragsprofil des Standorts holt der Gateway (PVGIS) — NIE vom User-LLM. Verweigert bei Tageswerten mit Begründung, weil sich daraus die Tageszeit des Verbrauchs nicht ablesen lässt. Liefert zwei getrennte Ergebnisse: 'gemessen' (nichts modelliert) und 'jahr_modelliert' (Tagessummen + gemessene Tagesform, als Modell gekennzeichnet). Euro-Beträge gibt es nur auf Jahresbasis — ein Teiljahr wird NICHT hochgerechnet. Die Einspeisevergütung ist eine Annahme und wird als Szenario-Band gerechnet.

    Eingabe

    arbeitspreis_netto_ct_kwh number Arbeitspreis netto in ct/kWh (Alternative zu energiepreis_brutto_ct_kwh).
    ausrichtung string Süd, Südost, Südwest, Ost, West (Default Süd)
    diskontrate number Diskontrate für die Kapitalwertrechnung (z.B. 0.04 = 4%).
    einspeise_szenarien_ct_kwh array Annahme-Band, Default [3, 5, 8] netto ct/kWh
    energiepreis_brutto_ct_kwh number Arbeitspreis von der Rechnung (brutto)
    gemeinde string Gemeinde des Anschlusses. Nur nötig, wenn die Antwort danach fragt: bei geteilten PLZ (z.B. 1140, 1210) liegen zwei Netzgebiete nebeneinander.
    gemessene_anlage_kwp number Installierte Leistung der BESTEHENDEN Anlage in kWp, zu der gemessener_jahresertrag_kwh gehört. Gridbert rechnet daraus den Ertrag je kWp. Nicht mit kwp_varianten verwechseln — das sind die durchzurechnenden Größen, das hier ist die reale.
    gemessener_jahresertrag_kwh number NUR bei einer BESTEHENDEN Anlage: tatsächlich gemessener Jahresertrag in kWh (vom Wechselrichter/Zähler). Ersetzt den PVGIS-Modellertrag — sinnvoll bei Verschattung, Mischausrichtung oder Balkonkraftwerken, wo das Modell weit danebenliegt. Nur zusammen mit gemessene_anlage_kwp verwenden; beide Zahlen erfragen, nie schätzen.
    investition_eur_pro_kwp object Investition brutto EUR/kWp je Größe, z.B. {"10": 1400}
    kwp_varianten array Anlagengrößen in kWp, Default [5, 10]
    neigung_grad integer Default 35
    nutzungsdauer_jahre integer Nutzungsdauer der Anlage in Jahren für die Amortisationsrechnung.
    plz* string PLZ des Standorts, für das Sonnenertragsprofil (PVGIS).
    serie_ref* string aus list_load_series/request_data_release
    speicher_kwh number Zusatzvariante mit Speicher (0 = ohne)

    Ausgabe (oberste Ebene)

    data object Ergebnis der Capability 'pv_potenzial' (+ gateway-seitiger Standort).
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    meta object | null Herkunft der Rechnung/Daten aus energietools (quelle, stand, snapshot_version) — Inhalt variiert je Capability.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • read_invoice_facts Gridbert-Domäne nur lesend

    Liest aus einer hochgeladenen Rechnung (rechnung_ref aus POST /accounts/rechnung) die Felder, die submit_invoice_facts braucht — je Feld mit wörtlichem Zitat von der Rechnung und einer Konfidenz (hoch|mittel|niedrig|fehlt). Rechnet NICHTS und speichert NICHTS: die gelesenen Werte gehören dem User vorgelegt und erst nach seiner Bestätigung an submit_invoice_facts übergeben. Ein Feld mit konfidenz='niedrig' oder 'mittel' ist zu bestätigen, eines mit 'fehlt' zu erfragen — nie selbst ergänzen und nie aus dem Gesprächsverlauf raten. 'mittel' heisst zusätzlich: der Wert stammt aus einer gescannten Seite. Wer nur drei Stufen kennt, darf den vierten Wert NICHT wegwerfen — er ist da und er ist vorzulegen. Beträge und Mengen kommen SYSTEMATISCH als 'niedrig' zurück; das ist kein Mangel, sondern die gemessene Erkennungsgüte (Verbrauch 86 %, Rechnungsbetrag 90 %) — sie sind ausnahmslos vorzulegen. pruefbar=false heisst, die Rechnung ist keine prüfbare Jahres-, Zwischen- oder Monatsrechnung (Teilbetragsvorschreibung, Einspeisung, Energiegemeinschaft, Grosskunde über 100.000 kWh, ausländische Rechnung, keine Energierechnung): dann 'ablehnungsgrund' dem User im Klartext sagen und KEINE Prüfung anbieten. 'quellen_anker' ist fertig für die Übergabe an submit_invoice_facts aufgebaut, nicht neu bauen. Fotos, Scans und PDFs ohne Textebene werden mit not_configured abgelehnt statt geraten — dafür ist der Modell-Pfad noch nicht gebaut.

    Eingabe

    rechnung_ref* string Kennung aus POST /accounts/rechnung. Nicht raten und nicht aus dem Gespräch rekonstruieren — sie ist eine Stunde gültig.

    Ausgabe (oberste Ebene)

    ablehnungsgrund string | null teilbetrag | einspeisung | energiegemeinschaft | grosskunde | ausland | keine_energierechnung — oder null, wenn prüfbar.
    energieart string strom | gas | kombi | unbekannt.
    felder array Ein Eintrag je gelesenem Feld.
    gelesen_mit string textebene = exakt aus dem PDF gelesen, ohne Modell. Der einzige heute gebaute Weg; Fotos und Scans werden abgelehnt statt geraten.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    pruefbar boolean false = keine prüfbare Jahres-, Zwischen- oder Monatsrechnung. Dann steht der Grund in 'ablehnungsgrund' und es gibt keine Prüfung.
    quellen_anker array Fertig für die Übergabe an submit_invoice_facts: Objekte {feld, zitat}. NICHT neu bauen — dort wird geprüft, ob der übergebene Zahlenwert im Zitat wirklich vorkommt.
    rechnungstyp string jahresrechnung | zwischenrechnung | monatsrechnung | unbekannt.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • request_data_release Gridbert-Domäne

    Fordert die Datenfreigabe für einen Zählpunkt an (EDA-Consent, CM_REQ_ONL — lastganganalyse-Prozess, Schritt 'datenfreigabe_anfordern'). Der Zählpunkt MUSS aus den Invoice-Fakten der Instanz stammen (get_page/search_pages) — nicht vom User abtippen lassen. Nur aufrufen, wenn list_load_series noch keine aktive Freigabe für diesen Zählpunkt zeigt (kein doppelter Consent-Flow). energy_direction ist PFLICHT (consumption|generation) — bewusst setzen, NICHT raten: eine falsche Richtung wedged den Zählpunkt beim Netzbetreiber (langes 'pending', dann Fehler bei jedem Retry). Ist die Richtung nicht klar aus den Invoice-Fakten ableitbar, den User zuerst fragen: 'Hast du eine PV-Anlage mit Einspeisung an diesem Zählpunkt?' Kündige dem User EXAKT an: 'Du bekommst zwei Freigabe-Anfragen im Netzbetreiber-Portal unter Freigaben→Offen (Provider EP100505) — zuerst die Energiedaten, wenige Stunden später eine zweite für Stammdaten (Adresse/Zählerdaten).' Beide sind eigene Consents mit eigenem Lifecycle — die zweite (ST/MasterData) feuert der Netzbetreiber automatisch, du musst sie nicht selbst auslösen, aber im Portal ebenfalls bestätigen. Nach jeder Bestätigung meldet der Netzbetreiber die Freigabe zurück; wie lange das dauert, ist sehr verschieden — gemessen war die Hälfte nach rund fünf Stunden durch, einzelne brauchten mehrere Tage (ehrlich sagen, kein Live-Spinner, keine Minutenangabe versprechen). Gridbert prüft stündlich und mailt von selbst, sobald sie da ist. Bei bestätigter PV-Einspeisung: ZWEITER Aufruf mit energy_direction='generation' und dem Einspeise-Zählpunkt (eigene Nummer, i.d.R. Suffix …9901).

    Eingabe

    energy_direction* string PFLICHT, bewusst setzen: 'consumption' für Bezug, 'generation' NUR für den separaten Einspeise-Zählpunkt bei PV. Nicht ableitbar? Erst den User fragen, nicht raten.
    plz* string PLZ des Zählpunkts (4-stellig).
    zaehlpunkt* string 33-stelliger AT-Zählpunkt aus den Invoice-Fakten (kein Abtippen durch den User)

    Ausgabe (oberste Ebene)

    bereits_freigegeben boolean Nur im Bestandsfall: true = es lief schon eine Freigabe, kein zweiter CM_REQ_ONL.
    energy_direction string Nur bei frischer Anforderung: 'consumption' oder 'generation'.
    esp_provider string Nur bei frischer Anforderung: Provider-Kennung im Portal.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    portal_pfad string Nur bei frischer Anforderung: 'Freigaben → Offen'.
    serie_ref string Referenz auf die Serie — der Schlüssel für alle Lastgang-Tools.
    status string Consent-Status: 'pending' nach frischer Anforderung, sonst der Stand der bestehenden Freigabe ('pending'|'active').
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
    zaehlpunkt_masked string Zählpunkt MASKIERT. Der Klartext verlässt den Gateway nie.
  • search_pages Gedächtnis (engram) nur lesend

    Deterministische FTS-Suche über die kompilierten Seiten der Instanz (LLM-frei) — das GEDÄCHTNIS des Haushalts. Hier liegen aus Rechnungen gemerkte Fakten (Jahresverbrauch, Arbeits-/Grundpreis, Lieferant, Zeitraum) UND selbst genannte Haushalts-Fakten (Heizart, Geräte, PV, …). ZUERST aufrufen bei JEDER Frage zu dem, was der User schon eingebracht hat — Verbrauch, Kosten, Tarif, Lieferant, WIE er heizt —, BEVOR du aus einem Lastprofil-Signal/einer Heuristik schätzt, nach manuellen Daten fragst oder 'weiß ich nicht' sagst. Ein gespeicherter Fakt schlägt immer die Heuristik. Suchtext als 'query'; 'limit' optional (Default 10).

    Eingabe

    limit integer max. Treffer (Default 10)
    query* string Suchtext (Freitext)

    Ausgabe (oberste Ebene)

    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    proxied boolean immer true — die Nutzlast kommt aus der engram-Instanz, nicht aus dem Gateway.
    result object | null Rohes MCP-Tool-Result der Instanz: {content: [...], isError: bool}. Der eigentliche Inhalt steckt als JSON-Text in content[0].text.
    tool string Name des proxierten Vault-Tools.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • speicher_dimensionierung Rechenwerk (energietools) nur lesend

    NUR bei vorhandener Q15-Lastgang-Serie. Beantwortet 'Wie groß soll der Batteriespeicher sein?': je Größe Eigenverbrauchsquote, Autarkie, Vollzyklen, Amortisation, Kapitalwert — und den Grenznutzen, also ab welcher Größe die nächste Kilowattstunde fast nichts mehr bringt. Sagt auch ehrlich Nein, wenn keine Größe einen positiven Kapitalwert erreicht. Speicher-Alterung ist NICHT eingepreist; die Vollzyklen je Größe stehen im Ergebnis. Braucht die Anlagengröße (kwp).

    Eingabe

    arbeitspreis_netto_ct_kwh number Arbeitspreis netto in ct/kWh (Alternative zu energiepreis_brutto_ct_kwh).
    ausrichtung string Süd, Südost, Südwest, Ost, West (Default Süd).
    diskontrate number Default 0.04
    einspeise_netto_ct_kwh number Default 5
    energiepreis_brutto_ct_kwh number Arbeitspreis brutto in ct/kWh.
    gemeinde string Gemeinde des Anschlusses. Nur nötig, wenn die Antwort danach fragt: bei geteilten PLZ (z.B. 1140, 1210) liegen zwei Netzgebiete nebeneinander.
    gemessene_anlage_kwp number Installierte Leistung in kWp, zu der der gemessene Jahresertrag gehört. Meist dieselbe Zahl wie kwp.
    gemessener_jahresertrag_kwh number NUR bei einer BESTEHENDEN Anlage: tatsächlich gemessener Jahresertrag in kWh. Ersetzt den PVGIS-Modellertrag — bei Verschattung oder Mischausrichtung liegt das Modell weit daneben, und die Speichergröße hängt daran. Nur zusammen mit gemessene_anlage_kwp; beide erfragen, nie schätzen.
    groessen_kwh array Speichergrößen, Default [2.5, 5, 7.5, 10, 15]
    kwp* number Anlagengröße in kWp
    neigung_grad integer Dachneigung in Grad (Default 35).
    nutzungsdauer_jahre integer Default 15
    plz* string PLZ des Lieferorts (4-stellig).
    serie_ref* string aus list_load_series/request_data_release
    speicher_kosten_eur_pro_kwh number Default 600

    Ausgabe (oberste Ebene)

    data object Ergebnis der Capability 'speicher_dimensionierung' (+ Standort).
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    meta object | null Herkunft der Rechnung/Daten aus energietools (quelle, stand, snapshot_version) — Inhalt variiert je Capability.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • spot_backtest Rechenwerk (energietools) nur lesend

    NUR bei vorhandener Lastgang-Serie (serie_ref aus list_load_series). Für den normalen Tarifvergleich aus einer Rechnung ist dieses Tool NICHT nötig — dafür tariff_compare, das ganz ohne Lastgang rechnet. Profilgewichteter Spot-Backtest (echter Verbrauchs-Shape x EPEX-Stundenpreise, aus dem Box-Postgres — NIE vom User-LLM übergeben) vs. aktueller Fixpreis, plus Tarifwechsel-Ersparnis (lastganganalyse-Prozess, Schritt 'spot_und_tarifvergleich'). tarif_ersparnis ist nur verfügbar, wenn plz/energiepreis_brutto_ct_kwh/aktuelle_grundgebuehr_brutto_eur_monat/aktueller_lieferant/jahresverbrauch_kwh vollständig sind — sonst verfuegbar=false + grund (nie eine stille 0). Hebel-Reihenfolge: Tarif zuerst prüfen. Verweigert bei Tageswerten mit Begründung.

    Eingabe

    aktuelle_grundgebuehr_brutto_eur_monat number Grundgebühr brutto in €/Monat.
    aktueller_lieferant string Name des aktuellen Stromlieferanten.
    aufschlag_ct number Annahme (Lieferanten-Aufschlag), Default aus energietools
    energiepreis_brutto_ct_kwh number Arbeitspreis brutto in ct/kWh.
    jahresverbrauch_kwh number Jahresverbrauch in kWh.
    plz string PLZ des Lieferorts (4-stellig).
    serie_ref* string aus list_load_series/request_data_release

    Ausgabe (oberste Ebene)

    data object Ergebnis der Capability 'spot_backtest'.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    meta object | null Herkunft der Rechnung/Daten aus energietools (quelle, stand, snapshot_version) — Inhalt variiert je Capability.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • submit_invoice_facts Gridbert-Domäne

    Übergibt EXAKT abgelesene Rechnungswerte + wörtliche Zitate. Validiert deterministisch (energieart muss strom|gas|kombi sein — Nicht-Energie-Rechnungen z.B. Restaurant werden mit error_class=validation_rejected und benanntem Grund abgelehnt, nichts wird gespeichert), rechnet finalize, schreibt die Fakten-Submission in den Raw-Store. PFLICHT: energieart, lieferant, plz, verbrauch_kwh, zeitraum_von, zeitraum_bis (ISO YYYY-MM-DD oder DE TT.MM.JJJJ) und quellen_anker. Zusätzlich muss arbeitspreis (ct/kWh) ODER summe_energieentgelte vorliegen. quellen_anker sind Objekte {feld, zitat} — je ein wörtliches Zitat für den Verbrauch und einen Betrag, z.B. quellen_anker=[{"feld":"verbrauch_kwh","zitat":"Jahresverbrauch 2562,3 kWh"},{"feld":"arbeitspreis","zitat":"Arbeitspreis 16,02 ct/kWh"}]. Lehnt bei Unplausibilität feld-genau mit Rückfrage ab (nichts wird gespeichert). Dieses Tool speichert nur — die Frage 'zahle ich zu viel?' beantwortet erst tariff_compare, das danach im selben Zug zu rechnen ist.

    Eingabe

    arbeitspreis object Arbeitspreis: {wert_ct_kwh, ist_netto}. Erforderlich, falls keine summe_energieentgelte.
    energieart* string Energieart der Rechnung: 'strom', 'gas' oder 'kombi'.
    grundgebuehr object Grundgebühr: {wert_eur, zeitraum(monat|jahr), ist_netto}.
    lieferant* string Name des Lieferanten auf der Rechnung.
    plz* string PLZ des Lieferorts (4-stellig).
    quellen_anker* array Wörtliche Belegstellen je Feld — mindestens ein Zitat für Verbrauch und einen Betrag.
    summe_energieentgelte object Summe des Energieentgelt-Blocks in € (Zeile 'Energiekosten'/'Summe Energieentgelte'): {wert_eur, ist_netto}. Erforderlich, falls kein arbeitspreis — z.B. wenn nur eine Blocksumme auf der Rechnung steht, kein ct/kWh-Wert.
    verbrauch_kwh* number Verbrauch im Abrechnungszeitraum in kWh.
    zaehlpunkt string Zählpunkt von der Rechnung (33 Zeichen, AT + 31 Ziffern; Punkte und Leerzeichen egal). Optional — aber wenn er auf der Rechnung steht, mitgeben: er benennt das Netzgebiet, das sonst aus der PLZ erschlossen wird, und die liegt falsch, wo ein Netzbetreiber über die Bundeslandgrenze versorgt. Liegt eine Datenfreigabe vor, ergänzt der Gateway ihn selbst.
    zeitraum_bis* string Abrechnungsende, ISO YYYY-MM-DD oder DE TT.MM.JJJJ
    zeitraum_von* string Abrechnungsbeginn, ISO YYYY-MM-DD oder DE TT.MM.JJJJ

    Ausgabe (oberste Ebene)

    gespeichert boolean immer true — bei Ablehnung wird NICHTS gespeichert.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    rechnung object | null Ergebnis der Capability 'finalize_invoice' — der Rechenweg, aus dem die Eingaben für tariff_compare stammen (nicht erneut beim User erfragen).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • submit_lastgang_facts Gridbert-Domäne

    Speichert Profil-Fakten zum Haushalt (Heizung, PV-Eckdaten, Dauerläufer, Lastverschiebung, Heizstromtarif, Q15-Opt-in). ZWEI Anlässe, beide richtig: (1) die beantworteten Rückfragen aus der Lastgang-Analyse (lastganganalyse-Prozess, Schritt 'rueckfragen_persistieren'); (2) alles, was der User von sich aus über seinen Verbrauch erzählt — typischerweise direkt nach analyze_load_profile, wenn er die Auffälligkeit auf dem Schirm hat und sie ungefragt erklärt ('der Verbrauch in der Früh ist der Boiler', 'das Auto lade ich unregelmäßig'). Speichere das, statt es im Gesprächsverlauf zu lassen: aus Viertelstundenwerten ist es nie wieder ableitbar. Liegt dagegen eine KONKRETE offene Rückfrage zu einem Ereignis vor, gehört sie über answer_lastgang_question beantwortet — nur so wird sie als erledigt vermerkt. Validiert deterministisch gegen die bekannten Felder — unbekannte Felder oder unplausible Werte werden mit error_class=validation_rejected abgelehnt (nichts wird gespeichert). Gleiche Rejection-Semantik wie submit_invoice_facts (D2.2).

    Eingabe

    fakten* array Liste der Fakten als Feld/Wert/Beleg-Objekte — beantwortete Rückfragen ebenso wie ungefragt Erzähltes.

    Ausgabe (oberste Ebene)

    anzahl_fakten integer Wie viele Fakten übernommen wurden.
    gespeichert boolean immer true — bei Ablehnung wird NICHTS gespeichert.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • tariff_compare Rechenwerk (energietools) nur lesend

    Vergleicht den aktuellen STROMtarif mit tagesaktuellen Alternativen (Rechenweg je Alternative). Braucht KEINEN Lastgang und KEINE serie_ref — die fünf Rechnungswerte unten reichen (serie_ref gehört zu spot_backtest/load_trend, nicht hierher). Bei Tarif-/Preisfragen dieses Tool nutzen statt Websuche: der Katalog ist tagesaktuell und jede Zahl kommt mit Rechenweg. Aktuell nur Strom: energieart ist PFLICHT (strom|gas) und 'gas' wird sauber als not_supported abgelehnt — setze es bewusst, nicht raten. Erfrage beim User zuerst: PLZ, Jahresverbrauch (kWh), aktueller Lieferant, Arbeitspreis (ct/kWh brutto), Grundgebühr (€/Monat brutto). Beträge in €/kWh bzw. €/Jahr vorher in ct/kWh bzw. €/Monat umrechnen. Je Tarif im Ergebnis: rabatt_befristet=true bedeutet ein NUR im 1. Jahr wirkender Neukundenbonus — dann jahreskosten_jahr2_eur (Preis ab Jahr 2) mit ausweisen, den Bonus NICHT als Dauer-Ersparnis verkaufen. Beachte netzkosten_vollstaendig: ist es false, ist kein Netzbetreiber hinterlegt und der €-Betrag heißt energiepreis_anteil_eur (nur Energiepreis-Anteil, NICHT jahreskosten_eur) — dem User NICHT als Gesamtrechnung präsentieren. Die VNB-Auflösung macht der Gateway (nb_key vorgelöst): gib den zaehlpunkt mit, wenn du ihn hast. netzbetreiber_herkunft im Ergebnis sagt, worauf sie beruht — bei 'plz' ist der Netzbetreiber ANGENOMMEN (und die Netzkosten mit ihm), bei 'zaehlpunkt' belegt. Vor einer Gesamtkosten-Aussage auf 'plz'-Basis den Zählpunkt erfragen, statt die Zahl als exakt zu verkaufen.

    Eingabe

    aktuelle_grundgebuehr_brutto_eur_monat* number Grundgebühr brutto in €/Monat (nicht €/Jahr)
    aktueller_energiepreis_brutto_ct_kwh* number Arbeitspreis brutto in ct/kWh (nicht €/kWh)
    aktueller_lieferant* string Name des aktuellen Stromlieferanten.
    energieart* string Pflicht: 'strom' (aktuell einziger Support) oder 'gas' (→ not_supported)
    jahresverbrauch_kwh* number Jahresverbrauch in kWh.
    plz* string PLZ des Lieferorts (4-stellig).
    zaehlpunkt string Optional, aber für die Netzkosten der bessere Schlüssel: 33-stelliger AT-Zählpunkt aus den Invoice-Fakten oder list_load_series (kein Abtippen durch den User). Er benennt den Netzbetreiber eindeutig; ohne ihn wird er aus der PLZ angenommen und liegt in Gemeinden mit fremdem Netzbetreiber falsch.

    Ausgabe (oberste Ebene)

    data object Vergleichsergebnis der energietools-Capability 'tariff_compare'.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    meta object | null Herkunft der Rechnung/Daten aus energietools (quelle, stand, snapshot_version) — Inhalt variiert je Capability.
    netzbetreiber_herkunft string | null Worauf die Netzbetreiber-Auflösung beruht: 'zaehlpunkt' = belegt, 'plz' = ANGENOMMEN (und mit ihm die Netzkosten). Gateway-seitig gesetzt, nicht von energietools.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • upsert_page Gedächtnis (engram)

    Schreibt eine Seite FAITHFULLY (1:1, kein LLM). Serverseitiges Verify-Gate auf diesem Pfad: NUR der Orphan-Check; Quellen-Anker- und Typing-Checks gaten compile_done. Clobber-Guard: bestehende Seite wird nur mit overwrite=true ersetzt — vorher mit get_page lesen.

    Eingabe

    body* string Voller Seiten-Body (Markdown), 1:1 gespeichert.
    key* string Opaker external_key (Slug).
    overwrite boolean true = bestehende Seite ersetzen; Default false = Ablehnung, wenn sie existiert.
    title* string Seitentitel — wirkt nur beim Neuanlegen (Update behält den Titel).

    Ausgabe (oberste Ebene)

    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    proxied boolean immer true — die Nutzlast kommt aus der engram-Instanz, nicht aus dem Gateway.
    result object | null Rohes MCP-Tool-Result der Instanz: {content: [...], isError: bool}. Der eigentliche Inhalt steckt als JSON-Text in content[0].text.
    tool string Name des proxierten Vault-Tools.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • versorger_abdeckung Rechenwerk (energietools) nur lesend

    Welche STROM-Lieferanten sind an einer PLZ/im Netzgebiet verfügbar (Abdeckungs-Block). Aktuell nur Strom: energieart='gas' wird als not_supported abgelehnt (die Abdeckungsdaten sind Strom-only — es würden sonst Strom-Marken als vermeintliche Gas-Liste erscheinen).

    Eingabe

    energieart string aktuell nur 'strom'; 'gas' wird als not_supported abgelehnt
    plz* string PLZ des Lieferorts (4-stellig).

    Ausgabe (oberste Ebene)

    data object Abdeckungs-Block der Capability 'versorger_abdeckung'.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    meta object | null Herkunft der Rechnung/Daten aus energietools (quelle, stand, snapshot_version) — Inhalt variiert je Capability.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.

Berater-Werkzeuge

Ein Beraterkonto sieht zusätzlich 6 Portfolio-Werkzeuge. Für Zugriff auf einen Kundenbereich gibst du deinem Berater die Freigabe im eigenen Konto, nicht umgekehrt.

  • get_tbox Gedächtnis (engram) nur lesend

    Liefert die deklarative Union-T-Box (core ∪ gridbert-at-energie) + Version der Instanz — für Ontologie-Diagnose ausserhalb eines laufenden Compile-Jobs (der normale Compile-Flow bekommt das Vokabular bereits inline über compile_next.tbox).

    Ausgabe (oberste Ebene)

    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    proxied boolean immer true — die Nutzlast kommt aus der engram-Instanz, nicht aus dem Gateway.
    result object | null Rohes MCP-Tool-Result der Instanz: {content: [...], isError: bool}. Der eigentliche Inhalt steckt als JSON-Text in content[0].text.
    tool string Name des proxierten Vault-Tools.
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • invite_client Berater-Portfolio

    Lädt einen Kunden per E-Mail-Adresse ein, den eigenen Bereich für diesen Berater freizugeben. Funktioniert auch, wenn der Kunde noch kein Gridbert-Konto hat. Der Kunde entscheidet selbst: die Einladung verschafft KEINEN Zugriff, sie führt nur zu einer Anfrage, der er in seinem Konto zustimmen muss. Das Result enthält einen Einladungslink, den der Berater auch selbst weitergeben kann.

    Eingabe

    bezeichnung* string Anzeigename dieses Mandats im Berater-Portfolio, z.B. 'Haushalt Familie K, Wien 1070' oder 'Betrieb Mustermann GmbH'
    einwilligung string Vermerk, wie die Zusage zustande kam, z.B. 'Am Telefon besprochen, 05.08.2026'. Der Kunde sieht ihn in seinem Konto — er ersetzt die Zustimmung NICHT.
    email* string E-Mail-Adresse des Kunden
    kundentyp string 'betrieb' für Unternehmen mit Lastprofilzähler; dann Sie-Form und Betriebs-Wortwahl in allen Mails. Weggelassen = 'haushalt' (Du-Form, Default).

    Ausgabe (oberste Ebene)

    bezeichnung string Anzeigename des Mandats im Portfolio.
    eingeladen string Die eingeladene E-Mail-Adresse.
    einladungslink string Link, den der Berater auch selbst weitergeben kann.
    gueltig_bis string Ablauf der Einladung als ISO-Zeitstempel.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
    zugriff string Immer der Wartezustand — nie ein 'ja'. Der Kunde entscheidet.
  • list_workspaces Berater-Portfolio nur lesend

    Listet die Arbeitsbereiche des Beraters: eigener Bereich + alle Kunden mit aktiver Freigabe (Grant), inkl. Datenstand.

    Ausgabe (oberste Ebene)

    aktiver_workspace string | null Slug des aktiven Bereichs, null = eigener Bereich.
    eigener_bereich object Der eigene Bereich des Beraters.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
    workspaces array Ein Eintrag je aktivem Grant.
  • portfolio_overview Berater-Portfolio nur lesend

    Status-Übersicht über ALLE Mandanten des Beraters auf einen Blick: je Mandant, ob eine Rechnung und ob Lastgang-Daten hinterlegt sind, wann zuletzt gerechnet wurde, und ob der Zugriff gerade trägt. Beantwortet 'bei wem fehlt was?'. Enthält BEWUSST keine Zahlen — keinen Verbrauch, keinen Preis, keinen Betrag. Für die Werte eines Mandanten mit select_workspace dorthin wechseln.

    Ausgabe (oberste Ebene)

    anzahl integer Anzahl Mandanten.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    mandanten array Eine Status-Zeile je Mandant.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • portfolio_tariff_check Berater-Portfolio nur lesend

    Rechnet für JEDEN Mandanten mit hinterlegter Rechnung einen Tarifvergleich und gibt je Mandant eine Ampel zurück: 'ok' (unter 5 % Ersparnis), 'pruefen' (5-15 %), 'handeln' (über 15 %), 'keine_daten' (keine vollständige Rechnung), 'nicht_verfuegbar'. Beantwortet 'bei wem lohnt ein genauerer Blick?'. Das Result enthält KEINE Beträge — nenne dem Berater keine, sie stehen nicht drin. Für den vollen Rechenweg eines Mandanten mit select_workspace dorthin wechseln und tariff_compare rufen. Beachte netzkosten_vollstaendig je Zeile: bei false beruht die Ampel nur auf dem Energiepreis-Anteil.

    Ausgabe (oberste Ebene)

    ampel_bedeutung object | null Legende der fünf Ampelwerte.
    anzahl integer Anzahl Mandanten.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    mandanten array Eine Ampel-Zeile je Mandant.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
  • select_workspace Berater-Portfolio

    Wechselt den aktiven Arbeitsbereich des Beraters (Kunde per Slug oder eindeutigem Label; 'eigen' = eigener Bereich). Die Auswahl gilt account-weit bis zum nächsten Wechsel.

    Eingabe

    workspace* string Kunden-Slug oder eindeutiges Label aus list_workspaces; 'eigen' wechselt zurück in den eigenen Bereich

    Ausgabe (oberste Ebene)

    aktiver_workspace string | null Slug des NEU gewählten Bereichs. null = eigener Bereich.
    hinweis string | null Optionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
    label string Anzeigename des neuen Bereichs.
    ok boolean immer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
    workspace object Nur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.

Ein Beraterkonto sieht zusätzlich sechs Portfolio-Werkzeuge (oben gelistet) und bekommt dieselben Werkzeug-Texte in der Sie-Form, wenn ein Kunde als 'betrieb' markiert ist (kundentyp='betrieb'). Der Katalog selbst ändert sich dadurch nicht.

HTTP-Endpunkte

46 Endpunkte, nach Aufgabe gruppiert. Die Auth-Spalte zeigt, was ein Aufruf braucht: ein Sitzungs-Cookie (Browser, angemeldet), ein Bearer-Token (MCP-Client), einen signierten Link aus einer E-Mail, oder gar nichts.

Konto

  • POST /accounts/delete Session-Cookie

    Löscht das eigene Konto endgültig, inklusive Instanz, Zugängen und Messwerten.

    Request

    bestaetigung* string Die eigene E-Mail-Adresse, zur Bestätigung wörtlich eingetippt.

    Antwort

    JSON

    • fertig: true, wenn die Löschung vollständig durch ist.
    • konto_geloescht: true/false.
    • offen: Schritte, die beim nächsten Aufruf weiterlaufen, falls einer offen blieb.

    Fehler

    • 400 bestaetigung_stimmt_nicht: Die eingetippte Adresse stimmt nicht mit der Konto-Adresse überein.
    • 401 no_session: Keine Sitzung.
    • 429 rate_limited: Zu viele Versuche von dieser IP-Adresse.
    • 503 loeschung_unvollstaendig: Ein Teilschritt war gerade nicht erreichbar. Derselbe Aufruf macht dort weiter.

    Limits

    • 20 Aktionen pro Minute je IP-Adresse
  • POST /accounts/lastgang Session-Cookie

    Lädt eine Verbrauchsdatei aus dem Netzbetreiber-Portal hoch, die zweite Quelle für Lastgang-Daten neben der Datenfreigabe.

    Request

    datei* multipart/form-data Die Portal-Exportdatei.

    Antwort

    JSON, Status 201

    • status: 'gespeichert'.
    • quellformat: Erkanntes Dateiformat.
    • serien: Eine Zeile je erkannter Zählserie, mit Zeitraum und Anzahl neuer/aktualisierter Messwerte.

    Fehler

    • 401 no_session: Keine Sitzung.
    • 413 file_too_large: Datei über dem Limit.
    • 422 unreadable: Datei nicht lesbar oder falsches Format.
    • 429 rate_limited: Zu viele Uploads von dieser IP-Adresse.
    • 503 storage_unavailable: Lastgang-Speicher gerade nicht angebunden.

    Limits

    • 3 Uploads pro Minute je IP-Adresse
    • Datei bis 20 MB
  • POST /accounts/login öffentlich

    Meldet ein bestätigtes Konto an und setzt das Sitzungs-Cookie für den Kontobereich.

    Request

    email* string E-Mail-Adresse.
    password* string Passwort.

    Antwort

    JSON + Set-Cookie

    • ok: true bei Erfolg.

    Fehler

    • 401 invalid_credentials: E-Mail-Adresse oder Passwort falsch, bewusst eine Meldung für beide Fälle.
    • 429 rate_limited: Zu viele Anmeldeversuche von dieser IP-Adresse.

    Limits

    • 10 Anmeldeversuche pro Minute je IP-Adresse
  • POST /accounts/logout Session-Cookie

    Beendet die Sitzung und löscht das Sitzungs-Cookie.

    Antwort

    Status 204, kein Inhalt

  • GET /accounts/me Session-Cookie

    Zeigt den Stand des eigenen Kontos: Bestätigung, Einrichtung, Kundentyp, Alarm-Einstellung.

    Antwort

    JSON

    • email: Hinterlegte Adresse.
    • email_verified: Bestätigt oder nicht.
    • provisioning_status: Stand der Einrichtung.
    • instance_slug: Kennung der eigenen Instanz.
    • created_at: Angelegt am.
    • alarm_mails: true = bekommt Nachrichten, wenn dem Zähler etwas auffällt.
    • kundentyp: haushalt oder betrieb, steuert Du/Sie in den Texten.

    Fehler

    • 401 no_session: Keine oder abgelaufene Sitzung.
  • POST /accounts/password/reset-confirm öffentlich

    Setzt mit dem Link-Token aus der Reset-Mail ein neues Passwort.

    Request

    token* string Reset-Token aus der Mail.
    password* string Neues Passwort.

    Antwort

    JSON

    • ok: true bei Erfolg.

    Fehler

    • 401 kein fester Code: 'error' trägt Klartext, keinen festen Code: z. B. 'invalid or already used reset token', 'reset token expired' oder 'unknown account'.
    • 429 rate_limited: Zu viele Versuche von dieser IP-Adresse.

    Limits

    • 5 Anfragen pro Minute je IP-Adresse
    • Link 1 Stunde gültig
  • POST /accounts/password/reset-request öffentlich

    Stößt den Passwort-Reset an und verschickt bei einer bekannten Adresse einen Link.

    Request

    email* string E-Mail-Adresse.

    Antwort

    JSON, Status 202

    • status: 'accepted': immer dieselbe Antwort, unabhängig davon, ob die Adresse existiert.

    Fehler

    • 429 rate_limited: Zu viele Anfragen von dieser IP-Adresse.

    Limits

    • 5 Anfragen pro Minute je IP-Adresse
  • POST /accounts/rechnung Bearer-Token

    Nimmt eine Rechnungsdatei entgegen und gibt eine Kennung zurück, mit der read_invoice_facts sie liest. Die Datei passt nicht durch die Werkzeug-Schnittstelle und geht deshalb diesen Weg.

    Request

    datei* multipart/form-data Die Rechnung als PDF, JPG oder PNG.

    Antwort

    JSON, Status 201

    • rechnung_ref: Kennung für read_invoice_facts. Eine Stunde gültig.
    • seiten: Seitenzahl der Datei.
    • quellformat: pdf, jpeg oder png, aus der Datei erkannt und nicht aus dem Namen.
    • textebene: true heisst, die Rechnung wird ohne Modell exakt gelesen. false heisst Foto oder Scan; dafür ist der Modell-Pfad noch nicht gebaut.

    Fehler

    • 401 no_session: Keine Sitzung und kein gültiges Token.
    • 413 file_too_large: Datei über dem Limit.
    • 415 unsupported_format: Kein PDF, JPG oder PNG.
    • 422 unreadable: Datei nicht zu öffnen oder zu viele Seiten.
    • 429 rate_limited: Zu viele Uploads von dieser IP-Adresse.

    Limits

    • 3 Rechnungs-Uploads pro Minute je IP-Adresse
    • höchstens 20 MB pro Rechnungsdatei
  • DELETE /accounts/rechnung/{rechnung_ref} Bearer-Token

    Löscht eine hochgeladene Rechnung sofort, sobald die Werte bestätigt sind. Ohne diesen Aufruf verschwindet sie spätestens nach einer Stunde von selbst.

    Request

    rechnung_ref* Pfad-Segment Die Kennung aus dem Upload.

    Antwort

    Status 204, kein Inhalt

    Fehler

    • 401 no_session: Keine Sitzung und kein gültiges Token.
  • POST /accounts/register öffentlich

    Legt ein neues Konto an und verschickt eine Bestätigungsmail.

    Request

    email* string E-Mail-Adresse, gleichzeitig der Login-Name.
    password* string Passwort, mindestens 8 Zeichen.

    Antwort

    JSON, Status 201

    • account_id: Kennung des neuen Kontos.

    Fehler

    • 422 kein fester Code: 'error' trägt Klartext, keinen festen Code: z. B. 'gültige E-Mail erforderlich' oder 'E-Mail bereits registriert', je nachdem was nicht passt.
    • 429 rate_limited: Zu viele Registrierungen von dieser IP-Adresse.

    Limits

    • 5 Registrierungen pro Minute je IP-Adresse
    • 20 neue Konten pro Tag je IP-Adresse
  • POST /accounts/resend-verify öffentlich

    Schickt die Bestätigungsmail erneut, wenn sie nicht angekommen ist.

    Request

    email* string E-Mail-Adresse.

    Antwort

    JSON, Status 202

    • status: 'accepted': immer dieselbe Antwort, unabhängig davon, ob die Adresse existiert.

    Fehler

    • 429 rate_limited: Zu viele Anfragen von dieser IP-Adresse.

    Limits

    • 5 Anfragen pro Minute je IP-Adresse
  • POST /accounts/verify öffentlich

    Bestätigt die E-Mail-Adresse mit dem Token aus der Bestätigungsmail und stößt danach die Einrichtung des Kontos im Hintergrund an.

    Request

    account_id* string Kontokennung aus der Registrierung.
    token* string Bestätigungs-Token aus der Mail.

    Antwort

    JSON

    • verified: true, wenn bestätigt.
    • provisioning_scheduled: true: die Einrichtung läuft im Hintergrund weiter.

    Fehler

    • 401 kein fester Code: 'error' trägt Klartext, keinen festen Code: z. B. 'unknown account or no pending verification' oder 'invalid verification token'.
    • 429 rate_limited: Zu viele Bestätigungsversuche von dieser IP-Adresse.

    Limits

    • 10 Bestätigungen pro Minute je IP-Adresse

Zugriff & Freigaben

  • GET /accounts/einladung öffentlich

    Zeigt, wer einen Zugriff angeboten hat, bevor man sich dafür anmeldet oder registriert.

    Request

    token* query Einladungs-Token aus dem Einladungslink.

    Antwort

    JSON

    • berater: E-Mail-Adresse der einladenden Person.
    • email: Eingeladene Adresse.
    • bezeichnung: Name des Mandats.
    • gueltig_bis: Ablauf der Einladung.
    • kundentyp: haushalt oder betrieb.

    Fehler

    • 404 kein fester Code: 'error' trägt Klartext, keinen festen Code: 'kein Token', wenn der Query-Parameter fehlt, oder 'Einladung nicht gefunden', wenn der Token unbekannt, zurückgezogen oder abgelaufen ist. Bewusst dieselbe Antwort in allen Fällen, damit der Statuscode keine Auskunft über ein fremdes Konto verrät.
  • POST /accounts/me/alarme Session-Cookie

    Schaltet die Nachrichten ein oder aus, die kommen, wenn dem eigenen Zähler etwas auffällt.

    Request

    aus* boolean true = Nachrichten abstellen, false = wieder einschalten.

    Antwort

    JSON

    • alarm_mails: Neuer Stand: true = eingeschaltet.

    Fehler

    • 401 no_session: Keine Sitzung.
    • 422 kein fester Code: 'error' trägt Klartext, keinen festen Code: 'invalid body' bei kaputtem JSON, 'aus muss true oder false sein' wenn das Feld fehlt oder kein Wahrheitswert ist.
    • 429 rate_limited: Zu viele Aktionen von dieser IP-Adresse.

    Limits

    • 20 Aktionen pro Minute je IP-Adresse
  • GET /accounts/me/freigaben Session-Cookie

    Zeigt, wer auf die eigenen Daten schauen darf, inklusive beendeter Freigaben.

    Antwort

    JSON

    • freigaben: Eine Zeile je Freigabe: Status, Gegenseite, Zeitpunkte.

    Fehler

    • 401 no_session: Keine Sitzung.
  • POST /accounts/me/freigaben/{grant_id}/widerrufen Session-Cookie

    Beendet eine Freigabe, wirkt ab dem nächsten Aufruf der Gegenseite.

    Request

    grant_id* Pfad Kennung der Freigabe.

    Antwort

    JSON

    • status: Neuer Zustand der Freigabe.

    Fehler

    • 401 no_session: Keine Sitzung.
    • 404 kein fester Code: 'error' trägt Klartext ('Freigabe nicht gefunden.'), keinen festen Code: Freigabe existiert nicht oder gehört nicht zu diesem Konto.
    • 429 rate_limited: Zu viele Aktionen von dieser IP-Adresse.

    Limits

    • 20 Aktionen pro Minute je IP-Adresse
  • POST /accounts/me/freigaben/{grant_id}/zustimmen Session-Cookie

    Stimmt einer angebotenen Freigabe zu. Erst danach kann die Gegenseite die Daten sehen.

    Request

    grant_id* Pfad Kennung der Freigabe.

    Antwort

    JSON

    • status: Neuer Zustand der Freigabe.

    Fehler

    • 401 no_session: Keine Sitzung.
    • 404 kein fester Code: 'error' trägt Klartext ('Freigabe nicht gefunden.'), keinen festen Code: Freigabe existiert nicht oder gehört nicht zu diesem Konto.
    • 429 rate_limited: Zu viele Aktionen von dieser IP-Adresse.

    Limits

    • 20 Aktionen pro Minute je IP-Adresse
  • GET /accounts/me/verbindungen Session-Cookie

    Zeigt, welche Chatbots gerade mit dem eigenen Konto verbunden sind.

    Antwort

    JSON

    • verbindungen: Eine Zeile je verbundener App: Name, ob Dauerzugriff, gültig bis, zuletzt verbunden.

    Fehler

    • 401 no_session: Keine Sitzung.
  • POST /accounts/me/verbindungen/entziehen Session-Cookie

    Trennt eine verbundene App sofort, nicht erst zum Ablauf des Zugangs.

    Request

    client_id* string Kennung der zu trennenden App, aus der Verbindungsliste.

    Antwort

    JSON

    • entzogen: Anzahl entwerteter Zugänge.
    • verbindungen: Die aktualisierte Liste.

    Fehler

    • 401 no_session: Keine Sitzung.
    • 422 kein fester Code: 'error' trägt Klartext, keinen festen Code: 'invalid body' bei kaputtem JSON, 'client_id fehlt' wenn das Feld fehlt oder leer ist.
    • 429 rate_limited: Zu viele Aktionen von dieser IP-Adresse.

    Limits

    • 20 Aktionen pro Minute je IP-Adresse
  • GET /alarm/abmelden signierter Link aus einer E-Mail

    Ein-Klick-Abmeldung von den Alarm-Mails direkt aus der E-Mail, ohne Anmeldung.

    Request

    token* query Signierter Abmelde-Token aus der Mail.

    Antwort

    HTML-Seite

    Fehler

    • 400 kein fester Code: Token nicht lesbar. Die Seite zeigt einen Hinweis, keine Auskunft über die Adresse.

App-Session

  • GET /app/aktivitaet Session-Cookie

    Mitschrift, was der Chat zuletzt an Werkzeugen aufgerufen hat: Werkzeug, Zeit, Erfolg, nie Argumente oder Ergebnisse.

    Request

    tage query, 1–30 Zeitraum zurück, Default 1 Tag.

    Antwort

    JSON

    • eintraege: Eine Zeile je Aufruf: Werkzeug, Familie, Zeitpunkt, Erfolg, Fehlerklasse.

    Fehler

    • 401 no_session: Keine Sitzung.
    • 422 invalid_tage: 'tage' außerhalb 1–30.
  • POST /app/eda-abfrage Session-Cookie

    Stößt eine Datenfreigabe-Anfrage beim Netzbetreiber aus der App heraus an, dasselbe Werkzeug wie im Chat.

    Request

    (Body)* object Die Argumente des Werkzeugs request_data_release (zaehlpunkt, plz, energy_direction).

    Antwort

    JSON: dasselbe Result wie im Chat

    Fehler

    • 401 no_session: Keine Sitzung.
    • 415 unsupported_media_type: Content-Type ist nicht application/json.
    • 422 invalid_body: Body kein gültiges JSON-Objekt.
    • 429 rate_limited: Zu viele Aufrufe von dieser IP-Adresse.

    Limits

    • 60 Aufrufe pro Minute je IP-Adresse
  • POST /app/rueckfrage Session-Cookie

    Beantwortet eine offene Lastgang-Rückfrage aus der App heraus, dasselbe Werkzeug wie im Chat.

    Request

    (Body)* object Die Argumente des Werkzeugs answer_lastgang_question.

    Antwort

    JSON: dasselbe Result wie im Chat

    Fehler

    • 401 no_session: Keine Sitzung.
    • 415 unsupported_media_type: Content-Type ist nicht application/json.
    • 422 invalid_body: Body kein gültiges JSON-Objekt.
    • 429 rate_limited: Zu viele Aufrufe von dieser IP-Adresse.

    Limits

    • 60 Aufrufe pro Minute je IP-Adresse
  • POST /app/tool Session-Cookie

    Ruft ein lesendes Werkzeug des Katalogs über das Sitzungs-Cookie auf, dieselbe Antwort wie im Chat.

    Request

    name* string Werkzeugname aus dem Katalog.
    arguments object Eingabe des Werkzeugs, wie im inputSchema beschrieben.

    Antwort

    JSON: dasselbe Result wie ein MCP-tools/call

    Fehler

    • 401 no_session: Keine Sitzung.
    • 403 nur_lesend: Das Werkzeug ist über diesen Weg nicht aufrufbar. Nur lesende Werkzeuge, unbekannte Namen bekommen dieselbe Antwort.
    • 415 unsupported_media_type: Content-Type ist nicht application/json.
    • 422 invalid_body: Body kein gültiges JSON-Objekt oder 'name' fehlt.
    • 429 rate_limited: Zu viele Aufrufe von dieser IP-Adresse.

    Limits

    • 60 Aufrufe pro Minute je IP-Adresse

Lastgang-Rückfragen

  • GET /lastgang/antwort signierter Link aus einer E-Mail

    Zeigt die Antwortmöglichkeiten zu einer Auffälligkeit aus der Alarm-Mail.

    Request

    t* query Verschlüsselter Token aus der Mail (Ereignis + Option).

    Antwort

    HTML-Seite

    Fehler

    • 404 kein fester Code: Link gilt nicht mehr: schon beantwortet oder abgeschnitten.
  • POST /lastgang/antwort signierter Link aus einer E-Mail

    Speichert die gewählte oder frei getippte Antwort zu einer Auffälligkeit.

    Request

    t* string (Form) Derselbe Token wie beim Anzeigen.
    freitext string (Form), bis 200 Zeichen Eigene Erklärung, wenn keine der Optionen passt.

    Antwort

    HTML-Seite (Dankesseite, ggf. mit Rückschau)

    Fehler

    • 404 kein fester Code: Link gilt nicht mehr.
    • 429 kein fester Code: Zu viele Versuche von dieser IP-Adresse in kurzer Zeit.
    • 500 kein fester Code: Speichern fehlgeschlagen, später erneut versuchen.

    Limits

    • 10 Antworten pro Minute je IP-Adresse

Match meinen Strom

  • POST /matchme/demo öffentlich

    Rechnet ohne Anmeldung ein Beispielergebnis der Partnersuche vor, aus frei eingegebenen Eckdaten.

    Request

    jahresverbrauch_kwh* number Jahresverbrauch laut Rechnung.
    waermepumpe* boolean Wärmepumpe vorhanden?
    eauto* boolean E-Auto vorhanden?
    warmwasser_elektrisch* boolean Warmwasser elektrisch?

    Antwort

    JSON

    Fehler

    • 422 validation_rejected: Eingabe außerhalb der erlaubten Werte.
    • 429 rate_limited: Zu viele Aufrufe: je IP-Adresse und insgesamt.

    Limits

    • 5 Aufrufe pro Minute je IP-Adresse
    • 60 Aufrufe pro Minute insgesamt (zusätzliche Bremse, nicht an eine einzelne IP-Adresse gebunden)
  • POST /matchme/einladung Session-Cookie

    Erzeugt einen persönlichen Einladungslink fürs Matching mit einer bestimmten Person.

    Antwort

    JSON, Status 201

    • link: Einladungslink zum Weitergeben.
    • gueltig_bis: Ablaufdatum.

    Fehler

    • 401 no_session: Keine Sitzung.
    • 429 rate_limited: Zu viele Aktionen von dieser IP-Adresse.

    Limits

    • 20 Aktionen pro Minute je IP-Adresse
  • POST /matchme/einladung/einloesen Session-Cookie

    Nimmt eine Matching-Einladung an und bildet das Paar.

    Request

    token* string Token aus dem Einladungslink.

    Antwort

    JSON

    • partner_label: E-Mail-Adresse der einladenden Person.

    Fehler

    • 401 no_session: Keine Sitzung.
    • 429 rate_limited: Zu viele Aktionen von dieser IP-Adresse.

    Limits

    • 20 Aktionen pro Minute je IP-Adresse
  • POST /matchme/einladung/vorschau öffentlich

    Zeigt vor der Anmeldung, von wem eine Matching-Einladung kommt.

    Request

    token* string Token aus dem Einladungslink, nur im Body, nie in der Adresszeile.

    Antwort

    JSON

    • von: E-Mail-Adresse der einladenden Person.

    Fehler

    • 429 rate_limited: Zu viele Anfragen von dieser IP-Adresse.

    Limits

    • 20 Aktionen pro Minute je IP-Adresse
  • POST /matchme/einladung/widerruf Session-Cookie

    Zieht eine eigene, noch offene Einladung zurück.

    Request

    id* string Kennung der Einladung.

    Antwort

    JSON

    Fehler

    • 401 no_session: Keine Sitzung.
    • 422 validation_rejected: 'id' fehlt.
    • 429 rate_limited: Zu viele Aktionen von dieser IP-Adresse.

    Limits

    • 20 Aktionen pro Minute je IP-Adresse
  • GET /matchme/einladungen Session-Cookie

    Listet die eigenen Einladungen, verschickte und erhaltene.

    Antwort

    JSON

    • einladungen: Eine Zeile je Einladung: Status, Rolle, Zeitpunkte, Partner (nur bei eingelöst).

    Fehler

    • 401 no_session: Keine Sitzung.
  • GET /matchme/matches Session-Cookie

    Zeigt die Rangliste passender Partnerprofile, ohne Namen, ohne Zählpunkte, ohne genaue Jahresmengen.

    Antwort

    JSON

    Fehler

    • 401 no_session: Keine Sitzung.
    • 403 kein_optin: Eigenes Profil ist nicht für die Partnersuche freigegeben.
    • 409 kein_lastgang: Keine verwertbare eigene Verbrauchsserie.
  • GET /matchme/optin Session-Cookie

    Zeigt, ob das eigene Profil gerade für die Partnersuche anderer mitgerechnet werden darf.

    Antwort

    JSON

    • status: 'active' | 'revoked' | 'none'.

    Fehler

    • 401 no_session: Keine Sitzung.
  • POST /matchme/optin Session-Cookie

    Gibt das eigene Profil für die Partnersuche frei.

    Antwort

    JSON

    • status: 'active'.

    Fehler

    • 401 no_session: Keine Sitzung.
    • 429 rate_limited: Zu viele Aktionen von dieser IP-Adresse.

    Limits

    • 20 Aktionen pro Minute je IP-Adresse
  • POST /matchme/optin/widerruf Session-Cookie

    Nimmt das eigene Profil wieder aus der Partnersuche.

    Antwort

    JSON

    • status: 'revoked' oder 'none', wenn es ohnehin keinen aktiven Opt-in gab.

    Fehler

    • 401 no_session: Keine Sitzung.
    • 429 rate_limited: Zu viele Aktionen von dieser IP-Adresse.

    Limits

    • 20 Aktionen pro Minute je IP-Adresse
  • GET /matchme/status Session-Cookie

    Zeigt den eigenen Stand: Opt-in, Zählpunkt (maskiert), Datenlage.

    Antwort

    JSON

    Fehler

    • 401 no_session: Keine Sitzung.

Kontakt

  • POST /contact öffentlich

    Kontaktformular der Website, schickt eine Nachricht.

    Request

    (Formularfelder)* object Name, E-Mail-Adresse, Nachricht.

    Antwort

    JSON, Status 202

    • status: 'accepted'.

    Fehler

    • 422 kein fester Code: Body fehlt oder ungültig.
    • 429 rate_limited: Zu viele Nachrichten von dieser IP-Adresse.

    Limits

    • 5 Nachrichten pro Minute je IP-Adresse

OAuth / Verbindung

  • GET /.well-known/oauth-authorization-server öffentlich

    Discovery-Dokument des Authorization Servers (RFC 8414): Endpunkte, Scopes, unterstützte Verfahren.

    Antwort

    JSON

  • GET /.well-known/oauth-protected-resource öffentlich

    Discovery-Dokument der geschützten Ressource (RFC 9728). Bindet mcp.gridbert.at an seinen Authorization Server.

    Antwort

    JSON

  • GET /authorize öffentlich (Anmelde-/Verbindungsseite)

    Zeigt die Anmelde- bzw. Registrierungsseite, über die ein Mensch die Verbindung seines Chatbots bestätigt.

    Request

    client_id, redirect_uri, …* query OAuth-Parameter des Connectors (PKCE, Scope, State).

    Antwort

    HTML-Seite

    Fehler

    • 400 kein fester Code: Pflichtangaben fehlen oder die Anfrage ist unbekannt.
  • POST /authorize öffentlich (Zugangsdaten im Formular, kein Cookie/Header)

    Verarbeitet Anmeldung, Registrierung oder Bestätigung auf der Verbindungsseite und leitet mit einem Autorisierungs-Code zurück.

    Request

    intent string 'login' (Default), 'register' oder 'connect': welches der drei Formulare abgeschickt wurde.
    email, password string Bei Anmeldung oder Registrierung.
    client_id, redirect_uri, code_challenge, …* string OAuth-Parameter, unverändert aus dem vorherigen Schritt übernommen.

    Antwort

    302-Redirect mit Code (oder erneut die HTML-Seite bei einem Fehler)

    Fehler

    • 401 access_denied: Anmeldedaten falsch.
    • 429 rate_limited: Zu viele Anmeldeversuche von dieser IP-Adresse.

    Limits

    • 30 Anmeldungen pro Minute je IP-Adresse
  • GET /jwks.json öffentlich

    Öffentlicher Schlüssel, mit dem Zugangs-Token signiert sind (JWK Set).

    Antwort

    JSON

  • POST /mcp Bearer-Token

    Der eine JSON-RPC-Endpunkt, über den ein verbundener Chatbot alle Werkzeuge aufruft.

    Request

    jsonrpc, id, method, params* JSON-RPC 2.0 method ist eine der unterstützten Methoden (initialize, tools/list, tools/call, notifications/initialized).

    Antwort

    JSON-RPC-Antwort mit content[] und structuredContent

    Fehler

    • 401 invalid_token: Kein oder ungültiges Bearer-Token.
    • 400 parse error: Body kein gültiges JSON.
    • 400 unknown method: Nicht unterstützte Methode.

    Limits

    • 30 Aufrufe Startguthaben
    • füllt mit 0.5 Aufrufen pro Sekunde nach
    • 2000 Aufrufe pro Tag je Konto
    • Anfrage bis 256 KB
    • Werkzeug-Eingabe bis 150 KB
  • POST /register öffentlich (Client-Registrierung, DCR)

    Dynamic Client Registration (RFC 7591). Ein Chatbot meldet sich einmalig als Client an, bevor er eine Verbindung anbietet.

    Request

    client_name string Anzeigename des Clients.
    redirect_uris* string[] Erlaubte Rücksprung-Adressen nach der Anmeldung.

    Antwort

    JSON, Status 201

    • client_id: Neue Client-Kennung.
    • token_endpoint_auth_method: 'none': öffentlicher Client mit PKCE.

    Fehler

    • 400 invalid_client_metadata: Body kein gültiges JSON.
    • 422 invalid_redirect_uri: Rücksprung-Adresse nicht erlaubt oder Cap erreicht.

    Limits

    • 5 Registrierungen pro Minute je IP-Adresse
    • höchstens 20 offene Client-Registrierungen je IP-Adresse
  • POST /token öffentlich (Client-Zugangsdaten im Body)

    Tauscht einen Autorisierungs-Code oder ein Refresh-Token gegen ein Zugangs-Token.

    Request

    grant_type* string 'authorization_code' oder 'refresh_token'.
    code, redirect_uri, client_id, code_verifier string Bei grant_type=authorization_code (PKCE).
    refresh_token, client_id string Bei grant_type=refresh_token.

    Antwort

    JSON

    • access_token: Kurzlebiges Zugangs-Token.
    • refresh_token: Langlebiges Token zum Erneuern.
    • expires_in: Gültigkeit des Zugangs-Tokens in Sekunden.

    Fehler

    • 400 unsupported_grant_type: grant_type ist weder authorization_code noch refresh_token.
    • 400 invalid_grant: Code, Token oder PKCE passt nicht oder ist abgelaufen.
    • 429 rate_limited: Zu viele Anfragen von diesem Client.

    Limits

    • 30 Token-Anfragen pro Minute je Client

Grenzen und Regeln

Rate-Limits

  • 10 Anmeldeversuche pro Minute je IP-Adresse
  • 10 Antworten pro Minute je IP-Adresse
  • 10 Bestätigungen pro Minute je IP-Adresse
  • 20 Aktionen pro Minute je IP-Adresse
  • 20 neue Konten pro Tag je IP-Adresse
  • 2000 Aufrufe pro Tag je Konto
  • 3 Rechnungs-Uploads pro Minute je IP-Adresse
  • 3 Uploads pro Minute je IP-Adresse
  • 30 Anmeldungen pro Minute je IP-Adresse
  • 30 Aufrufe Startguthaben
  • 30 Token-Anfragen pro Minute je Client
  • 5 Anfragen pro Minute je IP-Adresse
  • 5 Aufrufe pro Minute je IP-Adresse
  • 5 Nachrichten pro Minute je IP-Adresse
  • 5 Registrierungen pro Minute je IP-Adresse
  • 60 Aufrufe pro Minute insgesamt (zusätzliche Bremse, nicht an eine einzelne IP-Adresse gebunden)
  • 60 Aufrufe pro Minute je IP-Adresse
  • füllt mit 0.5 Aufrufen pro Sekunde nach
  • höchstens 20 MB pro Rechnungsdatei

Größe der Anfragen

  • Anfrage bis 16 KB auf den OAuth-/Konto-Endpunkten
  • Anfrage bis 256 KB auf /mcp
  • Werkzeug-Eingabe bis 150 KB je Werkzeug-Aufruf
  • Datei bis 20 MB beim Lastgang-Upload

Herkunft im Browser

Nur eine hinterlegte Liste von Ursprüngen darf die Konto-Endpunkte per Browser-JavaScript ansprechen. Ein weiterer Ursprung wird über den Kontakt freigeschaltet, nicht selbst eingetragen.

Umgang mit Daten

  • Rechnungs- und Lastgang-Inhalte stehen nie in Server-Logs, nur in den Tool-Ergebnissen selbst.
  • Ein Fehler nennt die Fehlerklasse (z. B. rate_limited, not_configured), nie den internen Grund dahinter.
  • Jede Euro-Zahl aus einem Werkzeug trägt ihren Rechenweg mit. Es wird nichts im Sprachmodell nachgerechnet.

Nicht Teil der Schnittstelle

  • GET /lastgang/antwort, POST /lastgang/antwort: Ein Link aus einer E-Mail, kein Aufruf für einen eigenen Client. Der Token gehört zu genau einer Mail.
  • GET /alarm/abmelden: Ebenfalls ein Link aus einer E-Mail, kein Aufruf für einen eigenen Client.
  • /admin/*: Betreiber-Werkzeug, nicht öffentlich. Kein Bearer-Token dieser Schnittstelle öffnet es.

Stand

16. September 2026, erzeugt aus dem Code, nicht von Hand nachgetragen. Ändert sich ein Werkzeug oder ein Endpunkt im Gateway, zieht diese Seite beim nächsten Build nach.

Du willst selbst etwas auf Gridbert bauen? Die Einrichtung je Agent zeigt den Klickweg, diese Seite die Referenz dahinter. Fragen dazu gehen an den Kontakt.