Testbetrieb · A/B/E bereitgestellt

cpmhub REST-API
für trawa

Aufträge, Prozessstatus und Zählerstände im bestehenden CPM/XAP.

Stand 13.11.2026 · Version 0.5.2 · Ticket 22160

Stand 13.11.2026 · API-Vertrag 0.5.2 · Ticket 22160. A–E einschließlich Stapel, kontrolliertem lokalem Storno und Zählerstandskorrekturen sind für die getrennte trawa-Testkopie implementiert. Der aktuelle Bereitstellungsnachweis steht im Ticket. C/D und Korrekturen benötigen einen eigenen Freischalter. Webhooks sind technisch vorbereitet und werden erst mit vereinbartem Ziel und Signaturschlüssel aktiviert; solange steht Polling zur Verfügung. Die Testkopie versendet keine Marktkommunikation. CPM und XAP arbeiten auf kopierten Daten. Die MIS-Netzbetreiberabfrage ist gezielt lesend freigegeben. Alle Beispiele sind synthetisch. Nicht bekannte Daten werden als null ausgewiesen.

CPM-Testkopie öffnen · Testauftrag per API erfassen

Die CPM-Testkopie nutzt die kopierte XAP-Datenbank. XAP hat keine eigene Weboberfläche.

Basispfad: /cpm/evu/cpmhub/v1

OpenAPI-Vertrag herunterladen · Hostingbestätigung EU/EWR

Zugang und Mandant

OAuth2 Client Credentials für das trawa-Backend; Rollen cpmhub:read und cpmhub:write. Der geprüfte technische Client bestimmt den Mandanten. Ein Mandantenfeld im JSON schaltet niemals den Zugriff um. Jede Objekt-, Vertrags- und Gerätereferenz wird zusätzlich geprüft. Client-Geheimnisse gehören nicht in Browsercode. Die Demo-Eingabeseite verwendet eine eigene Benutzeranmeldung mit Authorization Code und PKCE S256. Der öffentliche Client cpmhub-demo enthält kein Geheimnis. Der Betreiber weist dem technischen Client bzw. Testbenutzer die Client-Rollen cpmhub:read und/oder cpmhub:write unter cpm-trawa-test zu. Im Zugriffstoken stehen sie unter resource_access.cpm-trawa-test.roles. Audience cpm-trawa-test und der freigegebene Client werden zusätzlich geprüft. Die API verlangt keine OAuth2-Scopes cpmhub:read/write; scope=openid bei der Demo ist korrekt. Erforderliche Rollen stehen je Operation unter x-erforderliche-client-rollen.

Annahme und Verarbeitung

HTTP 201/202 bestätigt einen dauerhaften Eingang. Die Marktantwort folgt später. Sämtliche Übergaben laufen durch die gemeinsamen Importprüfungen und den Vertragsimport. Technische Fehler werden niemals als Markt-Ablehnung ausgegeben. Wiederholungen nach Verbindungsabbruch verwenden unveränderte Kennungen. Ein Konflikt erzeugt keine zweite Anlage.

Zählerstandprüfung

Der Hintergrundjob prüft alle fünf Sekunden bis zu fünf fällige Eingänge. Bei Rückstau oder technischen Wiederholungen dauert die Übernahme länger. in_pruefung bedeutet fachliche Prüfung durch solvit, nicht Warten auf den nächsten Job. Auch kleine Werte können gegenüber Vorwert und Verbrauchsprognose unplausibel sein. Erst uebernommen zusammen mit freigegebener_wert_id belegt die tatsächliche XAP-Übernahme. Ungeprüfte Werte werden nicht im Zählerstandsabruf veröffentlicht.

Kündigungsbestätigung

Das bestätigte Lieferende und die Vormerkung der Kundenbestätigung werden gemeinsam gespeichert. Mail und PDF werden anschließend aus dem bestätigten Vertragsstand bearbeitet; ein Dokumentfehler nimmt das Lieferende nicht zurück. Nach drei erfolglosen Versuchen liegt der Auftrag zur geprüften Freigabe in den cpmhub-Klärungen. Die Testkopie hält Mail und PDF ausdrücklich zurück.

Versionen und Ersatzübergaben

Idempotenz gilt mandantenbezogen. Gleiche Übergabe mit identischem normalisiertem Inhalt liefert das gespeicherte Ergebnis, abweichender Inhalt ergibt 409. Eine Korrektur nach Ablehnung hat eine neue uebergabe_id und ersetzt_uebergabe_id. Vertrag und Lieferstelle bleiben stabil; korrigierte Vertragsdaten erhöhen vertrag_version. Nicht mitgelieferte Lieferstellen werden nicht gelöscht. Bereits übernommene Lieferstellen werden nicht über eine Ersatzübergabe still überschrieben.

Klärung und Versuche

Eine offene Anfrage darf auch bei leerem gespeichertem Anfrageinhalt nicht erneut gestartet werden. Versandzeit und Prozessreferenz werden ebenfalls geprüft. Ohne negative Antwort, negative technische Quittung oder belegten lokalen Fehler wird 409 zurückgegeben. versuche zählt die protokollierten Anfragen der aktuellen Phase einschließlich Erstversuch; zwei automatische Neustarts können daher drei Versuche ergeben. Fehlt ein belastbarer Protokollbeleg, bleibt versuche null. seit bezieht sich auf den jüngsten belegten Versuch bzw. die aktuelle Neustartfreigabe.

Noch nicht unterstützte Zusatzfelder

Der aktuelle Auftragseingang bildet die vorhandenen Vertragsimport-Felder ab. PDF-Dokumente, SEPA-Nachweise, Unterschriftsdaten, STARQ-interne Nummern, Konzern-/Segmentmerkmale, Preisgewichtung, explizite Netzbetreiber-ID, Marktpartner-/Kundennummer des Vorversorgers und erste Abschlagsfälligkeit sind zusätzliche Formatfelder für einen späteren Ausbau. Wenn befüllt, ergibt der Eingang 422 FELD_NOCH_NICHT_UNTERSTUETZT; es wird nichts still verworfen. Ein bestehender Lastschrifteinzug mit IBAN nutzt die vorhandenen Importprüfungen. RLM wird lastgangbasiert monatlich abgerechnet, die übrigen Kundenarten über Ablesung jährlich mit Abschlägen. Widersprüchliche Angaben werden abgelehnt.

Ereignisse vollständig abrufen

GET /status liefert unveränderliche Versionen. Alle Seiten und anschließend den fortsetzungs_cursor verarbeiten. ereignisse enthält alle in der jeweiligen Version neu aufgezeichneten Belege, auch später eingetroffene ältere Belege. Nur letztes_ereignis auszuwerten kann Zwischenereignisse auslassen. Freigegebene Hub-Zählerstände und manuelle Entscheidungen werden zusammen mit ihrer Statusversion gespeichert. Andere CPM-Prozessänderungen werden periodisch beobachtet; ein Einzelabruf aktualisiert die Sicht sofort.

Grenzen

JSON in UTF-8; Datumswerte YYYY-MM-DD, Zeitpunkte RFC 3339 mit Zeitzone. Preise netto in Euro; Umsatzsteuer als Anteil 0.19, Prozentanteile 0..100. Maximal 50 Übergaben pro Vertrag, 100 Aufträge pro Stapel, 100 Zählerstände pro Liste, 10 MiB je PDF, 20 MiB je HTTP-Anfrage. Keine URLs für serverseitigen Dokumentabruf. Zugangsdaten und vollständige Nutzdaten werden nicht in das Aufrufprotokoll geschrieben.

Webhooks

Ereignis status.geaendert; Nutzdaten im Schema Webhook. Ziele und Signaturschlüssel werden betrieblich vereinbart, nicht frei per API registriert. HTTPS und HMAC-SHA256; Header X-Cpmhub-Delivery-Id, X-Cpmhub-Timestamp (Unix-Sekunden), X-Cpmhub-Signature (sha256=<hex>), X-Cpmhub-Key-Id. Signatur über timestamp + "." + unveränderte UTF-8-Bodybytes. Empfänger prüft Signatur, maximal fünf Minuten Zeitabweichung und die Übereinstimmung von Delivery-Id und zustellung_id im signierten Body. status enthält die unveränderliche Statusversion; ereignis kann bei einer Statusänderung ohne neues Fachereignis null sein. 2xx bestätigt Eingang. Bei allen anderen Antworten oder Verbindungsfehlern bis zu acht Versuche: Wartezeiten 1 min, 5 min, 15 min, 1 h, danach je 6 h. Anschließend bleibt die Zustellung in der CPM-Klärung sichtbar und kann nach Prüfung erneut freigegeben werden. Ziel-/Schlüsseländerung leitet ältere Belege nicht automatisch um. Keine Exactly-once-Zusage; Polling bleibt verfügbar.

Offene fachliche Abnahme

Nicht automatisch zulässige Änderungen und bereits versendete Stornos benötigen operative Klärung. Die Kündigungsfrist wird aus dem belegten Broker-Empfang gemäß GPKE (BK6-24-174, Teil 2, Abschnitt 1.2.2) ermittelt; ohne Beleg bleibt sie unbekannt. Die Testumgebung verarbeitet eingespielte Marktantworten und sendet nichts an den echten Markt. Hosting-/EU/EWR-Erklärungen sind für die Testumgebung vom Betreiber bestätigt; die schriftliche Erklärung steht in hosting-testumgebung.txt.

A – Aufträge · demo · im_test_bereitgestellt

Vertrag mit Lieferstellenübergaben übermitteln

POST /auftraege

201 bedeutet dauerhafter Eingang, keine erfolgreiche Anmeldung. Dasselbe vollständige Paket mit identischer paket_id und gleichem normalisiertem Inhalt liefert die unveränderte Eingangsquittung erneut (HTTP 200). Den aktuellen Verarbeitungsstand liefert GET auf den Auftrag. Geänderter Inhalt unter derselben Kennung ergibt 409. Nach Ablehnung: neue uebergabe_id und ersetzt_uebergabe_id; bei Vertragskorrektur zusätzlich höhere vertrag_version.

Rolle: cpmhub:write

Anfrage und Antworten
{
  "operationId": "auftragAnnehmen",
  "summary": "Vertrag mit Lieferstellenübergaben übermitteln",
  "description": "201 bedeutet dauerhafter Eingang, keine erfolgreiche Anmeldung. Dasselbe vollständige Paket mit identischer paket_id und gleichem normalisiertem Inhalt liefert die unveränderte Eingangsquittung erneut (HTTP 200). Den aktuellen Verarbeitungsstand liefert GET auf den Auftrag. Geänderter Inhalt unter derselben Kennung ergibt 409. Nach Ablehnung: neue uebergabe_id und ersetzt_uebergabe_id; bei Vertragskorrektur zusätzlich höhere vertrag_version.",
  "tags": [
    "A – Aufträge"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:write"
  ],
  "parameters": [],
  "responses": {
    "201": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Auftrag"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "200": {
      "description": "Identische Wiederholung; gespeicherte Quittung.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Auftrag"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "demo",
  "x-bereitstellungsstand": "im_test_bereitgestellt",
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/AuftragEingang"
        },
        "example": {
          "vertrag": {
            "schema_version": "1.0",
            "vertrag_id": "20000000-0000-4000-8000-000000000001",
            "vertrag_version": 1,
            "externe_vertragsnummer": "TRAWA-TEST-001",
            "vertragsart": "Einzug",
            "versorgungsart": "Strom",
            "eingangskanal": "Schriftlich",
            "auftragseingang": "2026-11-12",
            "kunde": {
              "kunde_id": "10000000-0000-4000-8000-000000000001",
              "anrede": "Firma",
              "firmenname": "Synthetischer Testbetrieb GmbH",
              "email": "auftrag@example.invalid",
              "kommunikation": {
                "dokumente_per_mail": false,
                "bestaetigungsmail": false
              }
            },
            "produkt": {
              "tarif": "TRAWA-TEST",
              "vertragstyp": "Sondervertragskunde",
              "waermestromoption": "KEINE"
            },
            "preise": {
              "arbeitspreis_eur_kwh": 0.12,
              "grundpreis_pauschale_eur_monat": 10,
              "ust_satz": 0.19,
              "preisgarantie": {
                "wert": 12,
                "einheit": "Monate"
              }
            },
            "laufzeit": {
              "mindestlaufzeit": {
                "wert": 12,
                "einheit": "Monate"
              },
              "anschlusslaufzeit": {
                "wert": 1,
                "einheit": "Monate"
              },
              "kuendigungsfrist": {
                "wert": 1,
                "einheit": "Monate",
                "bezug": "Rollierend"
              },
              "kuendigungsfrist_anschluss": {
                "wert": 1,
                "einheit": "Monate",
                "bezug": "Rollierend"
              }
            },
            "uebergaben": [
              {
                "uebergabe_id": "40000000-0000-4000-8000-000000000001",
                "lieferstelle_id": "30000000-0000-4000-8000-000000000001"
              }
            ],
            "dokumente": []
          },
          "uebergaben": [
            {
              "schema_version": "1.0",
              "uebergabe_id": "40000000-0000-4000-8000-000000000001",
              "paket_id": "50000000-0000-4000-8000-000000000001",
              "erstellt_am": "2026-11-12T10:00:00+01:00",
              "vertrag_ref": {
                "vertrag_id": "20000000-0000-4000-8000-000000000001",
                "vertrag_version": 1
              },
              "lieferstelle": {
                "lieferstelle_id": "30000000-0000-4000-8000-000000000001",
                "lieferbeginn": "2027-01-01",
                "netzfrequenz": "50",
                "adresse": {
                  "strasse": "Teststraße",
                  "hausnummer": "12a",
                  "plz": "12345",
                  "ort": "Teststadt"
                }
              },
              "rechnungsadresse": {
                "anrede": "Firma",
                "firmenname": "Synthetischer Testbetrieb GmbH",
                "strasse": "Teststraße",
                "hausnummer": "12a",
                "plz": "12345",
                "ort": "Teststadt",
                "email": "rechnung@example.invalid"
              },
              "zaehler": {
                "zaehlernummer": "TRAWA-TEST-001",
                "zaehlertyp": "MME",
                "prognoseverbrauch_kwh": 10000
              },
              "zahlung": {
                "lastschrifteinzug": false
              },
              "angebotszusammenfassung_erzeugen": false,
              "bestaetigungsmail": false,
              "importmodus": "Anmeldung"
            }
          ],
          "dokumente": []
        }
      }
    }
  }
}

A – Aufträge · demo · im_test_bereitgestellt

Quittungen und Bearbeitungsstand lesen

GET /auftraege/{id}

Rolle: cpmhub:read

ParameterOrtPflichtBedeutung
idpathjaKennung des jeweiligen Objekts; siehe Beschreibung der Operation.
Anfrage und Antworten
{
  "operationId": "auftragLesen",
  "summary": "Quittungen und Bearbeitungsstand lesen",
  "description": "",
  "tags": [
    "A – Aufträge"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:read"
  ],
  "parameters": [
    {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Interne, von der API vergebene Kennung.",
        "format": "uuid"
      },
      "description": "Kennung des jeweiligen Objekts; siehe Beschreibung der Operation."
    }
  ],
  "responses": {
    "200": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Auftrag"
          }
        }
      },
      "headers": {
        "ETag": {
          "description": "Versionskennung einschließlich Anführungszeichen, für If-Match unverändert übernehmen.",
          "schema": {
            "type": "string"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "demo",
  "x-bereitstellungsstand": "im_test_bereitgestellt"
}

A – Aufträge · test · im_test_bereitgestellt

Auftragsstapel asynchron annehmen

POST /auftraege/stapel

Stapel-ID und Empfang sofort; Verarbeitung und Quittung je Übergabe später. Wiederholung derselben stapel_id ist idempotent und liefert die erste Quittung unverändert. Vollständige Strukturprüfung erfolgt vor Annahme; danach bleiben fachliche Einzelfehler je Position sichtbar. Das Manifest enthält SHA-256 des normalisierten gesamten Stapels und je Position.

Rolle: cpmhub:write

Anfrage und Antworten
{
  "operationId": "stapelAnnehmen",
  "summary": "Auftragsstapel asynchron annehmen",
  "description": "Stapel-ID und Empfang sofort; Verarbeitung und Quittung je Übergabe später. Wiederholung derselben stapel_id ist idempotent und liefert die erste Quittung unverändert. Vollständige Strukturprüfung erfolgt vor Annahme; danach bleiben fachliche Einzelfehler je Position sichtbar. Das Manifest enthält SHA-256 des normalisierten gesamten Stapels und je Position.",
  "tags": [
    "A – Aufträge"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:write"
  ],
  "parameters": [],
  "responses": {
    "202": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Stapel"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "test",
  "x-bereitstellungsstand": "im_test_bereitgestellt",
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/StapelEingang"
        }
      }
    }
  }
}

A – Aufträge · test · im_test_bereitgestellt

Stapelstand und Einzelquittungen lesen

GET /auftraege/stapel/{id}

Rolle: cpmhub:read

ParameterOrtPflichtBedeutung
idpathjaKennung des jeweiligen Objekts; siehe Beschreibung der Operation.
Anfrage und Antworten
{
  "operationId": "stapelLesen",
  "summary": "Stapelstand und Einzelquittungen lesen",
  "description": "",
  "tags": [
    "A – Aufträge"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:read"
  ],
  "parameters": [
    {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Interne, von der API vergebene Kennung.",
        "format": "uuid"
      },
      "description": "Kennung des jeweiligen Objekts; siehe Beschreibung der Operation."
    }
  ],
  "responses": {
    "200": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Stapel"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "test",
  "x-bereitstellungsstand": "im_test_bereitgestellt"
}

A – Aufträge · test · im_test_bereitgestellt

Auftrag kontrolliert abbrechen

POST /auftraege/{id}/storno

Direkter lokaler Abbruch nur vor Beginn von Kündigung oder Anmeldung. Nach Versand: 409 und operative Klärung, auch wenn noch keine Bestätigung eingegangen ist. Keine Behauptung einer Markt-Rücknahme.

Rolle: cpmhub:write

ParameterOrtPflichtBedeutung
idpathjaKennung des jeweiligen Objekts; siehe Beschreibung der Operation.
If-MatchheaderjaAktuelle Versionskennung aus ETag der vorherigen GET-Antwort.
Idempotency-KeyheaderjaNeue UUID je fachlichem Änderungsauftrag; bei Transportwiederholung wiederverwenden.
Anfrage und Antworten
{
  "operationId": "auftragStornieren",
  "summary": "Auftrag kontrolliert abbrechen",
  "description": "Direkter lokaler Abbruch nur vor Beginn von Kündigung oder Anmeldung. Nach Versand: 409 und operative Klärung, auch wenn noch keine Bestätigung eingegangen ist. Keine Behauptung einer Markt-Rücknahme.",
  "tags": [
    "A – Aufträge"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:write"
  ],
  "parameters": [
    {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Interne, von der API vergebene Kennung.",
        "format": "uuid"
      },
      "description": "Kennung des jeweiligen Objekts; siehe Beschreibung der Operation."
    },
    {
      "name": "If-Match",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string"
      },
      "description": "Aktuelle Versionskennung aus ETag der vorherigen GET-Antwort."
    },
    {
      "name": "Idempotency-Key",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
        "format": "uuid"
      },
      "description": "Neue UUID je fachlichem Änderungsauftrag; bei Transportwiederholung wiederverwenden."
    }
  ],
  "responses": {
    "202": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Bearbeitungsauftrag"
          }
        }
      },
      "headers": {
        "Location": {
          "description": "Abruf des Bearbeitungsauftrags.",
          "schema": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "test",
  "x-bereitstellungsstand": "im_test_bereitgestellt",
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Storno"
        }
      }
    }
  }
}

B – Status · demo · im_test_bereitgestellt

Aktuellen Vertragsstatus lesen

GET /vertraege/{nr}/status

nr ist die solvit-Vertragsnummer oder die externe vertrag_id aus einem API-Auftrag. Bei mehreren Lieferstellen zum externen Vertrag ist lieferstelle_id erforderlich. Der Einzelabruf aktualisiert die Sicht unmittelbar aus den vorhandenen CPM-Prozessdaten.

Rolle: cpmhub:read

ParameterOrtPflichtBedeutung
nrpathjasolvit-Vertragsnummer oder externe vertrag_id, keine Datenbank-ID. Bei mehreren Lieferstellen lieferstelle_id angeben.
lieferstelle_idquerynein
Anfrage und Antworten
{
  "operationId": "vertragsstatusLesen",
  "summary": "Aktuellen Vertragsstatus lesen",
  "description": "nr ist die solvit-Vertragsnummer oder die externe vertrag_id aus einem API-Auftrag. Bei mehreren Lieferstellen zum externen Vertrag ist lieferstelle_id erforderlich. Der Einzelabruf aktualisiert die Sicht unmittelbar aus den vorhandenen CPM-Prozessdaten.",
  "tags": [
    "B – Status"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:read"
  ],
  "parameters": [
    {
      "name": "nr",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string"
      },
      "description": "solvit-Vertragsnummer oder externe vertrag_id, keine Datenbank-ID. Bei mehreren Lieferstellen lieferstelle_id angeben."
    },
    {
      "name": "lieferstelle_id",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string",
        "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
        "format": "uuid"
      },
      "description": ""
    }
  ],
  "responses": {
    "200": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Vertragsstatus"
          }
        }
      },
      "headers": {
        "ETag": {
          "description": "Versionskennung einschließlich Anführungszeichen, für If-Match unverändert übernehmen.",
          "schema": {
            "type": "string"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "demo",
  "x-bereitstellungsstand": "im_test_bereitgestellt"
}

B – Status · demo · im_test_bereitgestellt

Geänderte Vertragsstatus seitenweise lesen

GET /status

Änderungsprotokoll der beobachteten Statussichten; ein Vertrag kann mit mehreren Versionen vorkommen. Erster Abruf optional mit geaendert_seit. Alle naechster_cursor-Seiten abarbeiten, danach beim nächsten Poll fortsetzungs_cursor als cursor verwenden. Cursor nicht mit geaendert_seit kombinieren. Der fortsetzungs_cursor enthält eine commit-geordnete Sequenz; snapshot_bis dient nur zur Orientierung. Neuere Änderungen verändern laufende Seiten nicht. Deduplizieren anhand status_id und version. Die Hintergrundsicht wird in begrenzten Portionen aktualisiert; sie kann dem Einzelabruf nachlaufen. initialisierung_abgeschlossen bleibt bis zum ersten vollständigen erfolgreichen Bestandslauf false.

Rolle: cpmhub:read

ParameterOrtPflichtBedeutung
limitquerynein
cursorquerynein
geaendert_seitquerynein
Anfrage und Antworten
{
  "operationId": "statusAenderungenLesen",
  "summary": "Geänderte Vertragsstatus seitenweise lesen",
  "description": "Änderungsprotokoll der beobachteten Statussichten; ein Vertrag kann mit mehreren Versionen vorkommen. Erster Abruf optional mit geaendert_seit. Alle naechster_cursor-Seiten abarbeiten, danach beim nächsten Poll fortsetzungs_cursor als cursor verwenden. Cursor nicht mit geaendert_seit kombinieren. Der fortsetzungs_cursor enthält eine commit-geordnete Sequenz; snapshot_bis dient nur zur Orientierung. Neuere Änderungen verändern laufende Seiten nicht. Deduplizieren anhand status_id und version. Die Hintergrundsicht wird in begrenzten Portionen aktualisiert; sie kann dem Einzelabruf nachlaufen. initialisierung_abgeschlossen bleibt bis zum ersten vollständigen erfolgreichen Bestandslauf false.",
  "tags": [
    "B – Status"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:read"
  ],
  "parameters": [
    {
      "name": "limit",
      "in": "query",
      "required": false,
      "schema": {
        "type": "integer",
        "minimum": 1,
        "maximum": 500,
        "default": 100
      },
      "description": ""
    },
    {
      "name": "cursor",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string"
      },
      "description": ""
    },
    {
      "name": "geaendert_seit",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string",
        "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
        "format": "date-time"
      },
      "description": ""
    }
  ],
  "responses": {
    "200": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/StatusSeite"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "demo",
  "x-bereitstellungsstand": "im_test_bereitgestellt"
}

C – Klärung · test · im_test_bereitgestellt

Korrektur oder Entscheidung einreichen

POST /vertraege/{nr}/klaerung

Rolle: cpmhub:write

ParameterOrtPflichtBedeutung
nrpathjasolvit-Vertragsnummer oder externe vertrag_id, keine Datenbank-ID. Bei mehreren Lieferstellen lieferstelle_id angeben.
lieferstelle_idquerynein
If-MatchheaderjaAktuelle Versionskennung aus ETag der vorherigen GET-Antwort.
Idempotency-KeyheaderjaNeue UUID je fachlichem Änderungsauftrag; bei Transportwiederholung wiederverwenden.
Anfrage und Antworten
{
  "operationId": "klaerungEinreichen",
  "summary": "Korrektur oder Entscheidung einreichen",
  "description": "",
  "tags": [
    "C – Klärung"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:write"
  ],
  "parameters": [
    {
      "name": "nr",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string"
      },
      "description": "solvit-Vertragsnummer oder externe vertrag_id, keine Datenbank-ID. Bei mehreren Lieferstellen lieferstelle_id angeben."
    },
    {
      "name": "lieferstelle_id",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string",
        "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
        "format": "uuid"
      },
      "description": ""
    },
    {
      "name": "If-Match",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string"
      },
      "description": "Aktuelle Versionskennung aus ETag der vorherigen GET-Antwort."
    },
    {
      "name": "Idempotency-Key",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
        "format": "uuid"
      },
      "description": "Neue UUID je fachlichem Änderungsauftrag; bei Transportwiederholung wiederverwenden."
    }
  ],
  "responses": {
    "202": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Bearbeitungsauftrag"
          }
        }
      },
      "headers": {
        "Location": {
          "description": "Abruf des Bearbeitungsauftrags.",
          "schema": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "test",
  "x-bereitstellungsstand": "im_test_bereitgestellt",
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Klaerung"
        }
      }
    }
  }
}

D – Lieferende · test · im_test_bereitgestellt

Reguläre Abmeldung beauftragen

POST /vertraege/{nr}/lieferende

Rolle: cpmhub:write

ParameterOrtPflichtBedeutung
nrpathjasolvit-Vertragsnummer oder externe vertrag_id, keine Datenbank-ID. Bei mehreren Lieferstellen lieferstelle_id angeben.
lieferstelle_idquerynein
If-MatchheaderjaAktuelle Versionskennung aus ETag der vorherigen GET-Antwort.
Idempotency-KeyheaderjaNeue UUID je fachlichem Änderungsauftrag; bei Transportwiederholung wiederverwenden.
Anfrage und Antworten
{
  "operationId": "lieferendeEinreichen",
  "summary": "Reguläre Abmeldung beauftragen",
  "description": "",
  "tags": [
    "D – Lieferende"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:write"
  ],
  "parameters": [
    {
      "name": "nr",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string"
      },
      "description": "solvit-Vertragsnummer oder externe vertrag_id, keine Datenbank-ID. Bei mehreren Lieferstellen lieferstelle_id angeben."
    },
    {
      "name": "lieferstelle_id",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string",
        "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
        "format": "uuid"
      },
      "description": ""
    },
    {
      "name": "If-Match",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string"
      },
      "description": "Aktuelle Versionskennung aus ETag der vorherigen GET-Antwort."
    },
    {
      "name": "Idempotency-Key",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
        "format": "uuid"
      },
      "description": "Neue UUID je fachlichem Änderungsauftrag; bei Transportwiederholung wiederverwenden."
    }
  ],
  "responses": {
    "202": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Bearbeitungsauftrag"
          }
        }
      },
      "headers": {
        "Location": {
          "description": "Abruf des Bearbeitungsauftrags.",
          "schema": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "test",
  "x-bereitstellungsstand": "im_test_bereitgestellt",
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Lieferende"
        }
      }
    }
  }
}

D – Lieferende · test · im_test_bereitgestellt

Eingegangene Kündigung beantworten

POST /vertraege/{nr}/kuendigung/antwort

Rolle: cpmhub:write

ParameterOrtPflichtBedeutung
nrpathjasolvit-Vertragsnummer oder externe vertrag_id, keine Datenbank-ID. Bei mehreren Lieferstellen lieferstelle_id angeben.
lieferstelle_idquerynein
If-MatchheaderjaAktuelle Versionskennung aus ETag der vorherigen GET-Antwort.
Idempotency-KeyheaderjaNeue UUID je fachlichem Änderungsauftrag; bei Transportwiederholung wiederverwenden.
Anfrage und Antworten
{
  "operationId": "kuendigungsantwortEinreichen",
  "summary": "Eingegangene Kündigung beantworten",
  "description": "",
  "tags": [
    "D – Lieferende"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:write"
  ],
  "parameters": [
    {
      "name": "nr",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string"
      },
      "description": "solvit-Vertragsnummer oder externe vertrag_id, keine Datenbank-ID. Bei mehreren Lieferstellen lieferstelle_id angeben."
    },
    {
      "name": "lieferstelle_id",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string",
        "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
        "format": "uuid"
      },
      "description": ""
    },
    {
      "name": "If-Match",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string"
      },
      "description": "Aktuelle Versionskennung aus ETag der vorherigen GET-Antwort."
    },
    {
      "name": "Idempotency-Key",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
        "format": "uuid"
      },
      "description": "Neue UUID je fachlichem Änderungsauftrag; bei Transportwiederholung wiederverwenden."
    }
  ],
  "responses": {
    "202": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Bearbeitungsauftrag"
          }
        }
      },
      "headers": {
        "Location": {
          "description": "Abruf des Bearbeitungsauftrags.",
          "schema": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "test",
  "x-bereitstellungsstand": "im_test_bereitgestellt",
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Kuendigungsantwort"
        }
      }
    }
  }
}

E – Zählerstände · demo · im_test_bereitgestellt

Zählerstand oder Liste zur Prüfung übermitteln

POST /zaehlerstaende

Nur Eingang in den geprüften Bearbeitungsweg. Für Listen gilt dieselbe Quittung je uebermittlung_id. Identische Wiederholung liefert unverändert die erste Quittung (202), geänderter Inhalt 409. Ein defektes Element rollt die gesamte Liste zurück. GET /zaehlerstaende/{id} liefert den aktuellen Bearbeitungsstand. in_pruefung enthält keine noch ungeklärte fachliche Ablehnung.

Rolle: cpmhub:write

Anfrage und Antworten
{
  "operationId": "zaehlerstaendeEinreichen",
  "summary": "Zählerstand oder Liste zur Prüfung übermitteln",
  "description": "Nur Eingang in den geprüften Bearbeitungsweg. Für Listen gilt dieselbe Quittung je uebermittlung_id. Identische Wiederholung liefert unverändert die erste Quittung (202), geänderter Inhalt 409. Ein defektes Element rollt die gesamte Liste zurück. GET /zaehlerstaende/{id} liefert den aktuellen Bearbeitungsstand. in_pruefung enthält keine noch ungeklärte fachliche Ablehnung.",
  "tags": [
    "E – Zählerstände"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:write"
  ],
  "parameters": [],
  "responses": {
    "202": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ZaehlerstandQuittung"
              },
              {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ZaehlerstandQuittung"
                },
                "minItems": 1,
                "maxItems": 100
              }
            ]
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "demo",
  "x-bereitstellungsstand": "im_test_bereitgestellt",
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "oneOf": [
            {
              "$ref": "#/components/schemas/ZaehlerstandEingang"
            },
            {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ZaehlerstandEingang"
              },
              "minItems": 1,
              "maxItems": 100
            }
          ]
        },
        "example": {
          "uebermittlung_id": "60000000-0000-4000-8000-000000000001",
          "zaehlernummer": "TRAWA-TEST-001",
          "ablesedatum": "2026-11-12",
          "herkunft": "trawa",
          "ablesegrund": "Zwischenablesung",
          "messwerte": [
            {
              "obis": "1-1:1.8.0",
              "wert": 12345.67,
              "einheit": "kWh"
            }
          ]
        }
      }
    }
  }
}

E – Zählerstände · demo · im_test_bereitgestellt

Freigegebene Zählerstände lesen

GET /zaehlerstaende

Mindestens malo, melo oder zaehlernummer erforderlich. von/bis einschließlich. Keine Herausgabe noch ungeklärter unplausibler Werte. Seiten gehören zu einem unveränderlichen Snapshot (24 Stunden gültig); Cursor mit denselben Filtern verwenden. Bei mehr als 5000 Messwertzeilen oder 1000 Ablesungen den Zeitraum eingrenzen. Ohne von gilt 1900-01-01, ohne bis der aktuelle Tag. Historische Herkunft wird ohne Beleg als unbekannt gekennzeichnet. daten enthält nur fortlaufende Stände. Verbrauchs-/Rückstellwerte stehen getrennt in mengen_und_rueckstellwerte mit wertarten, wandlerfaktoren und quellreferenzen. Beide Bereiche teilen denselben Cursor und dasselbe Limit; auch bei leerem daten kann es weitere Seiten geben. Cursor aus 0.5.1 sind nach dem Wechsel zu 0.5.2 ungültig; den ersten Abruf mit denselben Filtern neu starten.

Rolle: cpmhub:read

ParameterOrtPflichtBedeutung
limitquerynein
cursorquerynein
maloquerynein
meloquerynein
zaehlernummerquerynein
vonquerynein
bisquerynein
Anfrage und Antworten
{
  "operationId": "zaehlerstaendeLesen",
  "summary": "Freigegebene Zählerstände lesen",
  "description": "Mindestens malo, melo oder zaehlernummer erforderlich. von/bis einschließlich. Keine Herausgabe noch ungeklärter unplausibler Werte. Seiten gehören zu einem unveränderlichen Snapshot (24 Stunden gültig); Cursor mit denselben Filtern verwenden. Bei mehr als 5000 Messwertzeilen oder 1000 Ablesungen den Zeitraum eingrenzen. Ohne von gilt 1900-01-01, ohne bis der aktuelle Tag. Historische Herkunft wird ohne Beleg als unbekannt gekennzeichnet. daten enthält nur fortlaufende Stände. Verbrauchs-/Rückstellwerte stehen getrennt in mengen_und_rueckstellwerte mit wertarten, wandlerfaktoren und quellreferenzen. Beide Bereiche teilen denselben Cursor und dasselbe Limit; auch bei leerem daten kann es weitere Seiten geben. Cursor aus 0.5.1 sind nach dem Wechsel zu 0.5.2 ungültig; den ersten Abruf mit denselben Filtern neu starten.",
  "tags": [
    "E – Zählerstände"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:read"
  ],
  "parameters": [
    {
      "name": "limit",
      "in": "query",
      "required": false,
      "schema": {
        "type": "integer",
        "minimum": 1,
        "maximum": 500,
        "default": 100
      },
      "description": ""
    },
    {
      "name": "cursor",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string"
      },
      "description": ""
    },
    {
      "name": "malo",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string"
      },
      "description": ""
    },
    {
      "name": "melo",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string"
      },
      "description": ""
    },
    {
      "name": "zaehlernummer",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string"
      },
      "description": ""
    },
    {
      "name": "von",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string",
        "description": "Fachliches Datum, YYYY-MM-DD.",
        "format": "date"
      },
      "description": ""
    },
    {
      "name": "bis",
      "in": "query",
      "required": false,
      "schema": {
        "type": "string",
        "description": "Fachliches Datum, YYYY-MM-DD.",
        "format": "date"
      },
      "description": ""
    }
  ],
  "responses": {
    "200": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/ZaehlerstandSeite"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "demo",
  "x-bereitstellungsstand": "im_test_bereitgestellt"
}

E – Zählerstände · demo · im_test_bereitgestellt

Bearbeitungsstand des Zählerstandseingangs lesen

GET /zaehlerstaende/{id}

Rolle: cpmhub:read

ParameterOrtPflichtBedeutung
idpathjaKennung des jeweiligen Objekts; siehe Beschreibung der Operation.
Anfrage und Antworten
{
  "operationId": "zaehlerstandQuittungLesen",
  "summary": "Bearbeitungsstand des Zählerstandseingangs lesen",
  "description": "",
  "tags": [
    "E – Zählerstände"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:read"
  ],
  "parameters": [
    {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Interne, von der API vergebene Kennung.",
        "format": "uuid"
      },
      "description": "Kennung des jeweiligen Objekts; siehe Beschreibung der Operation."
    }
  ],
  "responses": {
    "200": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/ZaehlerstandQuittung"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "demo",
  "x-bereitstellungsstand": "im_test_bereitgestellt"
}

E – Zählerstände · test · im_test_bereitgestellt

Neue Korrekturversion einreichen

POST /zaehlerstaende/{id}/korrektur

Rolle: cpmhub:write

ParameterOrtPflichtBedeutung
idpathjaKennung des jeweiligen Objekts; siehe Beschreibung der Operation.
If-MatchheaderjaAktuelle Versionskennung aus ETag der vorherigen GET-Antwort.
Idempotency-KeyheaderjaNeue UUID je fachlichem Änderungsauftrag; bei Transportwiederholung wiederverwenden.
Anfrage und Antworten
{
  "operationId": "zaehlerstandKorrigieren",
  "summary": "Neue Korrekturversion einreichen",
  "description": "",
  "tags": [
    "E – Zählerstände"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:write"
  ],
  "parameters": [
    {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Interne, von der API vergebene Kennung.",
        "format": "uuid"
      },
      "description": "Kennung des jeweiligen Objekts; siehe Beschreibung der Operation."
    },
    {
      "name": "If-Match",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string"
      },
      "description": "Aktuelle Versionskennung aus ETag der vorherigen GET-Antwort."
    },
    {
      "name": "Idempotency-Key",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
        "format": "uuid"
      },
      "description": "Neue UUID je fachlichem Änderungsauftrag; bei Transportwiederholung wiederverwenden."
    }
  ],
  "responses": {
    "202": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/ZaehlerstandQuittung"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "test",
  "x-bereitstellungsstand": "im_test_bereitgestellt",
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/ZaehlerstandKorrektur"
        }
      }
    }
  }
}

C – Klärung · test · im_test_bereitgestellt

Stand einer Klärung oder D-Änderung lesen

GET /bearbeitungsauftraege/{id}

Rolle: cpmhub:read

ParameterOrtPflichtBedeutung
idpathjaKennung des jeweiligen Objekts; siehe Beschreibung der Operation.
Anfrage und Antworten
{
  "operationId": "bearbeitungsauftragLesen",
  "summary": "Stand einer Klärung oder D-Änderung lesen",
  "description": "",
  "tags": [
    "C – Klärung"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:read"
  ],
  "parameters": [
    {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Interne, von der API vergebene Kennung.",
        "format": "uuid"
      },
      "description": "Kennung des jeweiligen Objekts; siehe Beschreibung der Operation."
    }
  ],
  "responses": {
    "200": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Bearbeitungsauftrag"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "test",
  "x-bereitstellungsstand": "im_test_bereitgestellt"
}

E – Zählerstände · test · im_test_bereitgestellt

Freigegebene aktuelle oder historische Version lesen

GET /zaehlerstaende/werte/{id}

id stammt aus GET /zaehlerstaende. ETag enthält die Version für einen Korrekturauftrag. Historische Versionen verfallen nicht mit dem 24-Stunden-Seitencursor.

Rolle: cpmhub:read

ParameterOrtPflichtBedeutung
idpathjaKennung des jeweiligen Objekts; siehe Beschreibung der Operation.
Anfrage und Antworten
{
  "operationId": "ableseversionLesen",
  "summary": "Freigegebene aktuelle oder historische Version lesen",
  "description": "id stammt aus GET /zaehlerstaende. ETag enthält die Version für einen Korrekturauftrag. Historische Versionen verfallen nicht mit dem 24-Stunden-Seitencursor.",
  "tags": [
    "E – Zählerstände"
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "x-erforderliche-client-rollen": [
    "cpmhub:read"
  ],
  "parameters": [
    {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Interne, von der API vergebene Kennung.",
        "format": "uuid"
      },
      "description": "Kennung des jeweiligen Objekts; siehe Beschreibung der Operation."
    }
  ],
  "responses": {
    "200": {
      "description": "Erfolgreiche Antwort.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Zaehlerstand"
          }
        }
      },
      "headers": {
        "ETag": {
          "description": "Versionskennung einschließlich Anführungszeichen, für If-Match unverändert übernehmen.",
          "schema": {
            "type": "string"
          }
        }
      }
    },
    "400": {
      "description": "Ungültiges JSON oder Anfrageformat.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "401": {
      "description": "Token fehlt oder ist ungültig.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "403": {
      "description": "Mandant, Rolle oder Funktion nicht freigegeben.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "404": {
      "description": "Referenz nicht gefunden.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "409": {
      "description": "Idempotenz- oder Zustandskonflikt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "412": {
      "description": "If-Match oder Korrekturbasis veraltet.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "428": {
      "description": "If-Match fehlt.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "413": {
      "description": "Anfrage zu groß.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "422": {
      "description": "Fachliche Eingangsprüfung fehlgeschlagen.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "429": {
      "description": "Aufrufgrenze erreicht.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    },
    "503": {
      "description": "Abhängiger Dienst vorübergehend nicht erreichbar.",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Fehler"
          }
        }
      }
    }
  },
  "x-ausbaustufe": "test",
  "x-bereitstellungsstand": "im_test_bereitgestellt"
}

Datenmodelle

Felder außerhalb der jeweiligen Objektdefinition werden zurückgewiesen. Pflichtfelder stehen unter required; nullable markiert ausdrücklich zulässige null-Werte.

Adresse
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "strasse": {
      "type": "string",
      "maxLength": 140
    },
    "hausnummer": {
      "type": "string",
      "description": "Auch Zusätze und Bereiche, etwa 12a oder 2-8.",
      "maxLength": 30
    },
    "hausnummer_zusatz": {
      "type": "string",
      "maxLength": 60,
      "nullable": true
    },
    "plz": {
      "type": "string",
      "pattern": "^[0-9]{5}$"
    },
    "ort": {
      "type": "string",
      "maxLength": 140
    }
  },
  "required": [
    "strasse",
    "hausnummer",
    "plz",
    "ort"
  ]
}
Rechnungsadresse
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "strasse": {
      "type": "string",
      "maxLength": 140
    },
    "hausnummer": {
      "type": "string",
      "description": "Auch Zusätze und Bereiche, etwa 12a oder 2-8.",
      "maxLength": 30
    },
    "hausnummer_zusatz": {
      "type": "string",
      "maxLength": 60,
      "nullable": true
    },
    "plz": {
      "type": "string",
      "pattern": "^[0-9]{5}$"
    },
    "ort": {
      "type": "string",
      "maxLength": 140
    },
    "anrede": {
      "type": "string",
      "enum": [
        "Herr",
        "Frau",
        "Firma"
      ]
    },
    "titel": {
      "type": "string",
      "maxLength": 70,
      "nullable": true
    },
    "vorname": {
      "type": "string",
      "maxLength": 70,
      "nullable": true
    },
    "nachname": {
      "type": "string",
      "maxLength": 70,
      "nullable": true
    },
    "firmenname": {
      "type": "string",
      "maxLength": 140,
      "nullable": true
    },
    "co": {
      "type": "string",
      "maxLength": 140,
      "nullable": true
    },
    "postfach": {
      "type": "string",
      "maxLength": 30,
      "nullable": true
    },
    "email": {
      "type": "string",
      "format": "email",
      "maxLength": 254,
      "nullable": true
    }
  },
  "description": "Vertragsbezogene Rechnungsadresse. Bei Postfach können Straße und Hausnummer entfallen. Rechnungs-E-Mail und Versandkanal werden je Lieferstellenvertrag ausgewertet."
}
Kommunikation
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "email_rechnungen": {
      "type": "string",
      "format": "email",
      "nullable": true
    },
    "email_mahnungen": {
      "type": "string",
      "format": "email",
      "nullable": true
    },
    "dokumente_per_mail": {
      "type": "boolean"
    },
    "dokumente_stammdaten_per_mail": {
      "type": "boolean"
    },
    "bestaetigungsmail": {
      "type": "boolean"
    }
  }
}
Kunde
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "kunde_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "externe_kundennummer": {
      "type": "string",
      "maxLength": 100,
      "nullable": true
    },
    "starqhub_kundennummer": {
      "type": "string",
      "description": "Kompatibilitätsfeld des STARQ-Formats. Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "maxLength": 100,
      "nullable": true,
      "x-bereitstellungsstand": "spezifiziert"
    },
    "kundengruppe": {
      "type": "string",
      "maxLength": 100,
      "nullable": true
    },
    "konzernintern": {
      "type": "boolean",
      "description": "Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "x-bereitstellungsstand": "spezifiziert"
    },
    "segment": {
      "type": "string",
      "maxLength": 40,
      "nullable": true,
      "description": "Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "x-bereitstellungsstand": "spezifiziert"
    },
    "anrede": {
      "type": "string",
      "enum": [
        "Herr",
        "Frau",
        "Firma"
      ]
    },
    "titel": {
      "type": "string",
      "maxLength": 70,
      "nullable": true
    },
    "vorname": {
      "type": "string",
      "maxLength": 70,
      "nullable": true
    },
    "nachname": {
      "type": "string",
      "maxLength": 70,
      "nullable": true
    },
    "firmenname": {
      "type": "string",
      "maxLength": 140,
      "nullable": true
    },
    "telefon": {
      "type": "string",
      "maxLength": 70,
      "nullable": true
    },
    "email": {
      "type": "string",
      "format": "email",
      "nullable": true
    },
    "kommunikation": {
      "$ref": "#/components/schemas/Kommunikation"
    },
    "rechnungsadresse": {
      "type": "object",
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/Rechnungsadresse"
        }
      ]
    }
  },
  "required": [
    "kunde_id",
    "anrede"
  ],
  "description": "Bei Firma ist firmenname erforderlich, sonst nachname. Externe Kundennummern sind keine internen CPM-IDs."
}
Zeitraum
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "wert": {
      "type": "integer",
      "minimum": 0
    },
    "einheit": {
      "type": "string",
      "enum": [
        "Tage",
        "Wochen",
        "Monate",
        "Jahre"
      ]
    }
  },
  "required": [
    "wert",
    "einheit"
  ]
}
Kuendigungsfrist
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "wert": {
      "type": "integer",
      "minimum": 0
    },
    "einheit": {
      "type": "string",
      "enum": [
        "Tage",
        "Wochen",
        "Monate",
        "Jahre"
      ]
    },
    "bezug": {
      "type": "string",
      "description": "Bezugspunkt aus der konfigurierten CPM-Werteliste, etwa Rollierend."
    }
  },
  "required": [
    "wert",
    "einheit",
    "bezug"
  ]
}
Preisgarantie
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "wert": {
      "type": "integer",
      "minimum": 0
    },
    "einheit": {
      "type": "string",
      "enum": [
        "Tage",
        "Wochen",
        "Monate",
        "Jahre"
      ]
    },
    "bis": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD.",
      "format": "date",
      "nullable": true
    },
    "typ": {
      "type": "string",
      "description": "CPM-Preisgarantietyp.",
      "nullable": true
    }
  },
  "description": "Entweder wert/einheit oder bis. Keine automatische Vertragsbeendigung."
}
Preise
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "arbeitspreis_eur_kwh": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit. Negative Arbeitspreise sind wie im CSV-Massenimport zulässig; die gemeinsame Anlageprüfung verlangt einen Wert unter 1 EUR/kWh."
    },
    "arbeitspreis_nt_eur_kwh": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit. Negative Arbeitspreise sind wie im CSV-Massenimport zulässig; die gemeinsame Anlageprüfung verlangt einen Wert unter 1 EUR/kWh.",
      "nullable": true
    },
    "grundpreis_pauschale_eur_monat": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit."
    },
    "foerderbeitrag_eur_kwh": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit.",
      "nullable": true
    },
    "strukturierungskosten_eur_kwh": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit.",
      "nullable": true
    },
    "ausgleichsenergiepauschale_eur_kwh": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit.",
      "nullable": true
    },
    "ap_basis_ht_eur_kwh": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit.",
      "nullable": true
    },
    "ap_basis_nt_eur_kwh": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit.",
      "nullable": true
    },
    "ap_aufschlag_ht_eur_kwh": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit.",
      "nullable": true
    },
    "ap_aufschlag_nt_eur_kwh": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit.",
      "nullable": true
    },
    "gp_basis_eur_monat": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit.",
      "nullable": true
    },
    "gp_aufschlag_eur_monat": {
      "type": "number",
      "description": "Nettopreis in der im Feldnamen genannten Einheit.",
      "nullable": true
    },
    "ust_satz": {
      "type": "number",
      "description": "Anteil, beispielsweise 0.19 für 19 Prozent.",
      "minimum": 0,
      "maximum": 1,
      "nullable": true
    },
    "vorlaeufig": {
      "type": "boolean"
    },
    "gewichtung": {
      "type": "string",
      "nullable": true,
      "description": "Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "x-bereitstellungsstand": "spezifiziert"
    },
    "preisgarantie": {
      "$ref": "#/components/schemas/Preisgarantie"
    }
  },
  "required": [
    "arbeitspreis_eur_kwh",
    "grundpreis_pauschale_eur_monat",
    "preisgarantie"
  ]
}
Laufzeit
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "mindestlaufzeit": {
      "type": "object",
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/Zeitraum"
        }
      ]
    },
    "bindung_bis": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD.",
      "format": "date",
      "nullable": true
    },
    "anschlusslaufzeit": {
      "$ref": "#/components/schemas/Zeitraum"
    },
    "kuendigungsfrist": {
      "$ref": "#/components/schemas/Kuendigungsfrist"
    },
    "kuendigungsfrist_anschluss": {
      "$ref": "#/components/schemas/Kuendigungsfrist"
    }
  },
  "required": [
    "anschlusslaufzeit",
    "kuendigungsfrist",
    "kuendigungsfrist_anschluss"
  ],
  "description": "Mindestlaufzeit oder bindung_bis ist erforderlich. bindung_bis ist eine Vertragsbindung und kein Lieferende."
}
Produkt
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "tarif": {
      "type": "string",
      "maxLength": 255
    },
    "tariftyp": {
      "type": "string",
      "nullable": true
    },
    "vertragstyp": {
      "type": "string",
      "description": "CPM-Kundenart, z. B. RLMKunde oder Sondervertragskunde. Bei eindeutiger mandantenspezifischer Tarifzuordnung darf sie entfallen."
    },
    "waermestromoption": {
      "type": "string",
      "default": "KEINE"
    },
    "rabattcode": {
      "type": "string",
      "nullable": true
    }
  },
  "required": [
    "tarif"
  ]
}
Energiemix
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "anteil_fix_prozent": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100,
      "nullable": true
    },
    "anteil_flex_prozent": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100,
      "nullable": true
    },
    "anteil_solar_prozent": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100,
      "nullable": true
    },
    "anteil_wind_prozent": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100,
      "nullable": true
    },
    "spot_hkn_eur_kwh": {
      "type": "number",
      "nullable": true
    },
    "referenzpreis_eur_kwh": {
      "type": "number",
      "nullable": true
    }
  }
}
Dokumentverweis
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "dokument_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "typ": {
      "type": "string",
      "maxLength": 100
    },
    "dateiname": {
      "type": "string",
      "description": "Ein Dateiname ohne Verzeichnisbestandteile.",
      "maxLength": 255
    }
  },
  "required": [
    "dokument_id",
    "typ",
    "dateiname"
  ]
}
Vertrag
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "schema_version": {
      "type": "string",
      "enum": [
        "1.0"
      ]
    },
    "vertrag_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "vertrag_version": {
      "type": "integer",
      "minimum": 1
    },
    "externe_vertragsnummer": {
      "type": "string",
      "maxLength": 100,
      "nullable": true
    },
    "starqhub_vertragsnummer": {
      "type": "string",
      "maxLength": 100,
      "nullable": true,
      "description": "Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "x-bereitstellungsstand": "spezifiziert"
    },
    "vertragsart": {
      "type": "string",
      "enum": [
        "Lieferantenwechsel",
        "Einzug",
        "Neubezug"
      ]
    },
    "versorgungsart": {
      "type": "string",
      "enum": [
        "Strom",
        "Gas"
      ]
    },
    "eingangskanal": {
      "type": "string",
      "description": "CPM-Eingangskanal, z. B. Schriftlich."
    },
    "auftragseingang": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD.",
      "format": "date"
    },
    "unterzeichnet_kunde_am": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD. Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "format": "date",
      "nullable": true,
      "x-bereitstellungsstand": "spezifiziert"
    },
    "gegengezeichnet_am": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD. Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "format": "date",
      "nullable": true,
      "x-bereitstellungsstand": "spezifiziert"
    },
    "kunde": {
      "$ref": "#/components/schemas/Kunde"
    },
    "produkt": {
      "$ref": "#/components/schemas/Produkt"
    },
    "preise": {
      "$ref": "#/components/schemas/Preise"
    },
    "energiemix": {
      "$ref": "#/components/schemas/Energiemix"
    },
    "laufzeit": {
      "$ref": "#/components/schemas/Laufzeit"
    },
    "vertriebspartner": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "nullable": true
        },
        "kundennummer": {
          "type": "string",
          "nullable": true
        }
      }
    },
    "uebergaben": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "uebergabe_id": {
            "type": "string",
            "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
            "format": "uuid"
          },
          "lieferstelle_id": {
            "type": "string",
            "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
            "format": "uuid"
          }
        },
        "required": [
          "uebergabe_id",
          "lieferstelle_id"
        ]
      },
      "minItems": 1,
      "maxItems": 50
    },
    "dokumente": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Dokumentverweis"
      },
      "maxItems": 20,
      "description": "Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "x-bereitstellungsstand": "spezifiziert"
    }
  },
  "required": [
    "schema_version",
    "vertrag_id",
    "vertrag_version",
    "vertragsart",
    "versorgungsart",
    "eingangskanal",
    "auftragseingang",
    "kunde",
    "produkt",
    "preise",
    "laufzeit",
    "uebergaben"
  ]
}
Vorversorger
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "name": {
      "type": "string",
      "nullable": true
    },
    "marktpartner_id": {
      "type": "string",
      "nullable": true,
      "description": "Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "x-bereitstellungsstand": "spezifiziert"
    },
    "kundennummer": {
      "type": "string",
      "nullable": true,
      "description": "Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "x-bereitstellungsstand": "spezifiziert"
    },
    "kuendigung_durch_kunde": {
      "type": "boolean"
    }
  }
}
Lieferstelle
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "lieferstelle_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "malo_id": {
      "type": "string",
      "pattern": "^[0-9]{11}$",
      "nullable": true
    },
    "lieferbeginn": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD.",
      "format": "date"
    },
    "bezeichnung": {
      "type": "string",
      "maxLength": 255,
      "nullable": true
    },
    "adresse": {
      "$ref": "#/components/schemas/Adresse"
    },
    "selbstzahler_msb": {
      "type": "boolean"
    },
    "selbstzahler_nne": {
      "type": "boolean"
    },
    "vorversorger": {
      "type": "object",
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/Vorversorger"
        }
      ]
    },
    "netzbetreiber_id": {
      "type": "string",
      "nullable": true,
      "description": "Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "x-bereitstellungsstand": "spezifiziert"
    },
    "netzfrequenz": {
      "type": "string",
      "description": "Hz; 16.7 bezeichnet Bahnstrom.",
      "enum": [
        "50",
        "16.7"
      ]
    },
    "rueckspeise_malo_id": {
      "type": "string",
      "pattern": "^[0-9]{11}$",
      "nullable": true
    },
    "virtuelle_entnahmestelle": {
      "type": "string",
      "nullable": true
    },
    "bilanzkreis": {
      "type": "string",
      "nullable": true
    }
  },
  "required": [
    "lieferstelle_id",
    "lieferbeginn",
    "adresse"
  ],
  "description": "Bahnstrom wird mit den bestehenden CPM-Regeln validiert; keine eigene vereinfachte Marktprüfung."
}
Zaehler
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "melo_id": {
      "type": "string",
      "maxLength": 33,
      "nullable": true
    },
    "zaehlernummer": {
      "type": "string",
      "maxLength": 100,
      "nullable": true
    },
    "zaehlertyp": {
      "type": "string",
      "enum": [
        "KME",
        "MME",
        "IMS"
      ]
    },
    "prognoseverbrauch_kwh": {
      "type": "integer",
      "minimum": 0
    }
  },
  "required": [
    "prognoseverbrauch_kwh"
  ],
  "description": "Mindestens MaLo, MeLo oder Zählernummer muss die Lieferstelle identifizieren. Der vorhandene Anlageweg verarbeitet einen Zähler je Lieferstellenvertrag; mehrere gleichzeitige Zähler werden als Klärbedarf zurückgewiesen, niemals zusammengefügt."
}
SepaMandat
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "mandatstyp": {
      "type": "string",
      "enum": [
        "basis"
      ]
    },
    "erteilt_am": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD.",
      "format": "date"
    },
    "nachweis_dokument_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "mandatsreferenz": {
      "type": "string",
      "description": "Wird durch solvit vergeben.",
      "nullable": true
    }
  },
  "required": [
    "mandatstyp",
    "erteilt_am",
    "nachweis_dokument_id"
  ]
}
Zahlung
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "kontoinhaber": {
      "type": "string",
      "nullable": true
    },
    "iban": {
      "type": "string",
      "maxLength": 34,
      "nullable": true
    },
    "lastschrifteinzug": {
      "type": "boolean"
    },
    "sepa_mandat": {
      "type": "object",
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/SepaMandat"
        }
      ],
      "description": "Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "x-bereitstellungsstand": "spezifiziert"
    }
  },
  "required": [
    "lastschrifteinzug"
  ]
}
Uebergabe
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "schema_version": {
      "type": "string",
      "enum": [
        "1.0"
      ]
    },
    "uebergabe_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "paket_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "erstellt_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time"
    },
    "ersetzt_uebergabe_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid",
      "nullable": true
    },
    "vertrag_ref": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "vertrag_id": {
          "type": "string",
          "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
          "format": "uuid"
        },
        "vertrag_version": {
          "type": "integer",
          "minimum": 1
        },
        "starqhub_vertragsnummer": {
          "type": "string",
          "nullable": true,
          "description": "Ausbau: derzeit nicht befüllen; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
          "x-bereitstellungsstand": "spezifiziert"
        }
      },
      "required": [
        "vertrag_id",
        "vertrag_version"
      ]
    },
    "abrechnungsbasis": {
      "type": "string",
      "enum": [
        "lastgang",
        "ablesung"
      ]
    },
    "abrechnungsrhythmus": {
      "type": "string",
      "enum": [
        "monatlich",
        "jaehrlich_mit_abschlaegen"
      ]
    },
    "lieferstelle": {
      "$ref": "#/components/schemas/Lieferstelle"
    },
    "rechnungsadresse": {
      "$ref": "#/components/schemas/Rechnungsadresse"
    },
    "zaehler": {
      "$ref": "#/components/schemas/Zaehler"
    },
    "zahlung": {
      "$ref": "#/components/schemas/Zahlung"
    },
    "abschlag": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "monatlich_eur": {
          "type": "number",
          "minimum": 0
        },
        "erste_faelligkeit": {
          "type": "string",
          "description": "Ausbau: derzeit nicht befüllen; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
          "format": "date",
          "x-bereitstellungsstand": "spezifiziert"
        }
      },
      "required": [
        "monatlich_eur"
      ],
      "nullable": true
    },
    "importmodus": {
      "type": "string",
      "description": "Bestandsmigration ist standardmäßig gesperrt und erfordert die separate Freigabe des Mandanten und technischen Clients.",
      "enum": [
        "Anmeldung",
        "Bestandsmigration"
      ]
    },
    "leitweg_id": {
      "type": "string",
      "nullable": true
    },
    "bestellnummer": {
      "type": "string",
      "nullable": true
    },
    "angebotszusammenfassung_erzeugen": {
      "type": "boolean"
    },
    "bestaetigungsmail": {
      "type": "boolean"
    },
    "abschlagsplan": {
      "type": "boolean"
    },
    "entgelte": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "nne_gp": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "nne_ap": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "nne_ap_nt": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "konzessionsabgabe": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "konzessionsabgabe_nt": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "messstellenbetrieb": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "messdienstleistung": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "abrechnungspreis": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "eeg_umlage": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "kwk_umlage": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "par19_umlage": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "offshore_umlage": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "abschalt_umlage": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "stromsteuer": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "erdgassteuer": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "co2_abgabe": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "bilanzierungsumlage": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "konvertierungsentgelt": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        },
        "speicherumlage": {
          "type": "number",
          "description": "Euro in der Einheit des bestehenden CPM-Entgeltfelds.",
          "nullable": true
        }
      }
    }
  },
  "required": [
    "schema_version",
    "uebergabe_id",
    "paket_id",
    "erstellt_am",
    "vertrag_ref",
    "lieferstelle",
    "zaehler",
    "rechnungsadresse",
    "zahlung"
  ]
}
Dokument
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "dokument_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "dateiname": {
      "type": "string",
      "maxLength": 255
    },
    "media_type": {
      "type": "string",
      "enum": [
        "application/pdf"
      ]
    },
    "groesse_bytes": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10485760
    },
    "sha256": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$"
    },
    "inhalt_base64": {
      "type": "string",
      "format": "byte"
    }
  },
  "required": [
    "dokument_id",
    "dateiname",
    "media_type",
    "groesse_bytes",
    "sha256",
    "inhalt_base64"
  ]
}
AuftragEingang
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "vertrag": {
      "$ref": "#/components/schemas/Vertrag"
    },
    "uebergaben": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Uebergabe"
      },
      "minItems": 1,
      "maxItems": 50
    },
    "dokumente": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Dokument"
      },
      "maxItems": 20,
      "description": "Ausbau: derzeit nur leer bzw. nicht befüllt zulässig; sonst 422 FELD_NOCH_NICHT_UNTERSTUETZT.",
      "x-bereitstellungsstand": "spezifiziert"
    }
  },
  "required": [
    "vertrag",
    "uebergaben"
  ],
  "description": "Ein Vertrag mit 1..50 Lieferstellenübergaben. Verweise, Versionen und Dokumentprüfsummen müssen zusammenpassen. REST überträgt das vollständige Paket in einem Request; ein Datei-Manifest ist hier nicht das Fertigsignal. Der SFTP-Transport behält seine Manifestregeln."
}
Feldfehler
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "code": {
      "type": "string"
    },
    "feld": {
      "type": "string",
      "description": "JSON-Pfad des betroffenen Felds."
    },
    "text": {
      "type": "string"
    }
  },
  "required": [
    "code",
    "feld",
    "text"
  ]
}
Fehler
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "code": {
      "type": "string"
    },
    "text": {
      "type": "string"
    },
    "request_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid"
    },
    "fehler": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Feldfehler"
      }
    }
  },
  "required": [
    "code",
    "text",
    "request_id"
  ]
}
Quittung
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "auftrag_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid"
    },
    "quittung_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid"
    },
    "paket_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "kunde_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "vertrag_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "vertrag_version": {
      "type": "integer",
      "minimum": 1
    },
    "lieferstelle_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "uebergabe_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "ergebnis": {
      "type": "string",
      "enum": [
        "eingegangen",
        "uebernommen",
        "abgelehnt",
        "klaerung_erforderlich",
        "storniert"
      ]
    },
    "zeitpunkt": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time"
    },
    "solvit_kundennummer": {
      "type": "string",
      "nullable": true
    },
    "solvit_vertragsnummer": {
      "type": "string",
      "nullable": true
    },
    "fehler": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Feldfehler"
      }
    }
  },
  "required": [
    "auftrag_id",
    "quittung_id",
    "paket_id",
    "kunde_id",
    "vertrag_id",
    "vertrag_version",
    "lieferstelle_id",
    "uebergabe_id",
    "ergebnis",
    "zeitpunkt"
  ]
}
Auftrag
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "auftrag_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid"
    },
    "angelegt_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time"
    },
    "quittungen": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Quittung"
      }
    },
    "abgeschlossen": {
      "type": "boolean"
    }
  },
  "required": [
    "auftrag_id",
    "angelegt_am",
    "quittungen",
    "abgeschlossen"
  ]
}
StapelEingang
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "stapel_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "auftraege": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/AuftragEingang"
      },
      "minItems": 1,
      "maxItems": 100
    }
  },
  "required": [
    "stapel_id",
    "auftraege"
  ]
}
Stapel
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "stapel_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "angenommen_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time"
    },
    "anzahl": {
      "type": "integer",
      "minimum": 1
    },
    "bearbeitet": {
      "type": "integer",
      "minimum": 0
    },
    "abgeschlossen": {
      "type": "boolean"
    },
    "auftraege": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Auftrag"
      }
    },
    "manifest": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "sha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$"
        },
        "normalisierung": {
          "type": "string",
          "enum": [
            "cpmhub-canonical-json-v1"
          ]
        },
        "positionen": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "position": {
                "type": "integer",
                "minimum": 1
              },
              "sha256": {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$"
              }
            },
            "required": [
              "position",
              "sha256"
            ]
          }
        }
      },
      "required": [
        "sha256",
        "normalisierung",
        "positionen"
      ]
    },
    "positionen": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "position": {
            "type": "integer",
            "minimum": 1
          },
          "status": {
            "type": "string",
            "enum": [
              "ausstehend",
              "angenommen",
              "abgelehnt",
              "klaerung_erforderlich"
            ]
          },
          "auftrag_id": {
            "type": "string",
            "description": "Interne, von der API vergebene Kennung.",
            "format": "uuid",
            "nullable": true
          },
          "fehlercode": {
            "type": "string",
            "nullable": true
          },
          "fehlertext": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "position",
          "status"
        ]
      }
    }
  },
  "required": [
    "stapel_id",
    "angenommen_am",
    "anzahl",
    "bearbeitet",
    "abgeschlossen"
  ]
}
Ereignis
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "ereignis_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid"
    },
    "status_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid"
    },
    "ereignis_typ": {
      "type": "string",
      "enum": [
        "ANMELDUNG_VERSENDET",
        "ANMELDUNG_BESTAETIGT",
        "ANMELDUNG_ABGELEHNT",
        "LIEFERBEGINN_BESTAETIGT",
        "ABMELDUNG_VERSENDET",
        "ABMELDUNG_BESTAETIGT",
        "LIEFERENDE_BESTAETIGT",
        "MALO_ERMITTLUNG_ANGEFRAGT",
        "MALO_ERMITTELT",
        "MALO_DATEN_ABWEICHEND",
        "MALO_NICHT_ERMITTELBAR",
        "KUENDIGUNG_ALTLIEFERANT_VERSENDET",
        "KUENDIGUNG_ALTLIEFERANT_BESTAETIGT",
        "KUENDIGUNG_ALTLIEFERANT_ABGELEHNT",
        "ABMELDUNG_ABGELEHNT",
        "KUENDIGUNG_EINGEGANGEN",
        "ZUORDNUNG_BEENDET",
        "KLAERUNG_ERFORDERLICH",
        "KLAERUNG_UEBERNOMMEN",
        "ZAEHLERSTAND_UEBERNOMMEN",
        "ZAEHLERSTAND_UNPLAUSIBEL",
        "ZAEHLERSTAND_KORRIGIERT"
      ]
    },
    "zeitpunkt": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time"
    },
    "aufgezeichnet_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time"
    },
    "vertrag_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid",
      "nullable": true
    },
    "lieferstelle_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid",
      "nullable": true
    },
    "uebergabe_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid",
      "nullable": true
    },
    "solvit_vertragsnummer": {
      "type": "string",
      "nullable": true
    },
    "vorgang_id": {
      "type": "string",
      "nullable": true
    },
    "code": {
      "type": "string",
      "description": "Originalcode; unbekannte Codes bleiben erhalten.",
      "nullable": true
    },
    "klartext": {
      "type": "string",
      "nullable": true
    },
    "originalhinweis": {
      "type": "string",
      "nullable": true
    },
    "antwortfrist": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time",
      "nullable": true
    },
    "frist_grundlage": {
      "type": "string",
      "nullable": true
    },
    "fachdatum": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD.",
      "format": "date",
      "nullable": true
    },
    "zaehlerstand_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid",
      "nullable": true
    },
    "ersetzt_zaehlerstand_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid",
      "nullable": true
    },
    "kontext": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "sparte": {
          "type": "string",
          "nullable": true
        },
        "pruefidentifikator": {
          "type": "string",
          "nullable": true
        },
        "formatversion": {
          "type": "string",
          "nullable": true
        }
      }
    }
  },
  "required": [
    "ereignis_id",
    "ereignis_typ",
    "zeitpunkt",
    "aufgezeichnet_am",
    "vertrag_id",
    "lieferstelle_id"
  ],
  "description": "Nur tatsächliche Marktantworten erzeugen Markt-Ablehnungsereignisse. Technische Fehler sind separat."
}
Prozessschritt
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "schritt": {
      "type": "string",
      "enum": [
        "import",
        "malo_ident",
        "kuendigung_altlieferant",
        "anmeldung",
        "abmeldung",
        "kuendigung_eingang",
        "abgeschlossen",
        "unbekannt"
      ]
    },
    "seit": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time",
      "nullable": true
    },
    "versuche": {
      "type": "integer",
      "minimum": 0,
      "nullable": true
    },
    "rueckmeldung_code": {
      "type": "string",
      "nullable": true
    },
    "rueckmeldung_klartext": {
      "type": "string",
      "nullable": true
    },
    "technischer_fehler": {
      "type": "string",
      "nullable": true
    },
    "contrl_positiv_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time",
      "nullable": true
    },
    "contrl_negativ_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time",
      "nullable": true
    },
    "anerkennung_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time",
      "nullable": true
    },
    "naechste_bearbeitung_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time",
      "nullable": true
    },
    "naechste_bearbeitung_quelle": {
      "type": "string",
      "nullable": true
    },
    "antwortfrist": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time",
      "nullable": true
    },
    "frist_grundlage": {
      "type": "string",
      "nullable": true
    },
    "liegt_bei": {
      "type": "string",
      "enum": [
        "solvit",
        "trawa",
        "marktpartner",
        "unbekannt"
      ]
    },
    "empfehlung": {
      "type": "string",
      "nullable": true
    }
  },
  "required": [
    "schritt",
    "liegt_bei"
  ],
  "description": "Unbekannte Werte sind null, nicht geschätzt. Empfehlungen sind Hinweise, keine automatischen Marktentscheidungen."
}
Vertragsstatus
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "status_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid"
    },
    "vertrag_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid",
      "nullable": true
    },
    "lieferstelle_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid",
      "nullable": true
    },
    "solvit_vertragsnummer": {
      "type": "string",
      "nullable": true
    },
    "geaendert_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time"
    },
    "letztes_ereignis": {
      "type": "object",
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/Ereignis"
        }
      ]
    },
    "prozess": {
      "$ref": "#/components/schemas/Prozessschritt"
    },
    "ereignisse": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Ereignis"
      },
      "description": "Alle in dieser Statusversion neu aufgezeichneten Ereignisse. Alle Versionen aus GET /status abarbeiten und anhand ereignis_id deduplizieren. Ältere Versionen vor 0.4 können das Feld noch nicht enthalten."
    },
    "version": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": [
    "status_id",
    "vertrag_id",
    "lieferstelle_id",
    "geaendert_am",
    "prozess",
    "version"
  ],
  "description": "status_id ist eine interne API-Kennung. Externe UUIDs bleiben bei Altverträgen ohne belegte Zuordnung null. geaendert_am bezeichnet die Erfassung einer geänderten Sicht durch den cpmhub, nicht ein erfundenes historisches Quelldatum."
}
StatusSeite
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "daten": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Vertragsstatus"
      }
    },
    "naechster_cursor": {
      "type": "string",
      "nullable": true
    },
    "fortsetzungs_cursor": {
      "type": "string"
    },
    "snapshot_bis": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time"
    },
    "initialisierung_abgeschlossen": {
      "type": "boolean"
    }
  },
  "required": [
    "daten",
    "snapshot_bis",
    "fortsetzungs_cursor",
    "initialisierung_abgeschlossen"
  ]
}
Klaerung
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "entscheidung": {
      "type": "string",
      "enum": [
        "weiterfuehren",
        "lieferbeginn_verschieben",
        "beenden"
      ]
    },
    "korrekturen": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "malo_id": {
          "type": "string",
          "pattern": "^[0-9]{11}$",
          "nullable": true
        },
        "zaehlernummer": {
          "type": "string",
          "nullable": true
        },
        "lieferadresse": {
          "$ref": "#/components/schemas/Adresse"
        },
        "name": {
          "type": "string"
        },
        "netzbetreiber_id": {
          "type": "string"
        },
        "lieferbeginn": {
          "type": "string",
          "description": "Fachliches Datum, YYYY-MM-DD.",
          "format": "date"
        }
      }
    },
    "begruendung": {
      "type": "string",
      "maxLength": 2000
    }
  },
  "required": [
    "entscheidung"
  ],
  "description": "Nur zulässige Korrekturen im aktuellen Zustand. Höchstens zwei automatische Neustarts je Lieferstelle und Prozessphase; weitere Korrekturen setzen den Zähler nicht zurück. Vor erfolgreicher MaLo-Identifikation sind die benannten Korrekturen möglich. Nach erfolgreicher Vorphase ist die MaLo-/Netzbetreiberänderung operativ zu klären; in der Anmeldung ist automatisch nur ein späterer Lieferbeginn erlaubt. Keine Rücksetzung laufender Anfragen oder bereits erfolgreicher Phasen."
}
Lieferende
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "zum": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD.",
      "format": "date"
    },
    "grund": {
      "type": "string",
      "enum": [
        "Auszug",
        "Wechsel",
        "Stilllegung"
      ]
    },
    "begruendung": {
      "type": "string",
      "maxLength": 2000,
      "nullable": true
    }
  },
  "required": [
    "zum",
    "grund"
  ],
  "description": "Startet nach Prüfung den regulären Abmeldeprozess. Das Datum ist gewünscht, bis die Marktantwort es bestätigt."
}
Kuendigungsantwort
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "vorgang_id": {
      "type": "string"
    },
    "entscheidung": {
      "type": "string",
      "enum": [
        "bestaetigen",
        "ablehnen"
      ]
    },
    "begruendung": {
      "type": "string",
      "maxLength": 2000
    },
    "bindungsende": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD.",
      "format": "date",
      "nullable": true
    },
    "ablehnung_code": {
      "type": "string",
      "enum": [
        "A01",
        "A04",
        "A05",
        "A06",
        "A08",
        "A10",
        "A13",
        "A14",
        "A15",
        "A16",
        "A18",
        "A99"
      ]
    }
  },
  "required": [
    "vorgang_id",
    "entscheidung"
  ],
  "description": "Antwort auf einen konkreten eingegangenen Vorgang, unter Beachtung seiner Frist. Ablehnung benötigt eine Begründung."
}
Storno
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "begruendung": {
      "type": "string",
      "maxLength": 2000
    }
  }
}
Bearbeitungsauftrag
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid"
    },
    "eingegangen_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time"
    },
    "status": {
      "type": "string",
      "enum": [
        "eingegangen",
        "in_bearbeitung",
        "klaerung_erforderlich",
        "ausgefuehrt",
        "abgelehnt"
      ]
    },
    "code": {
      "type": "string",
      "nullable": true
    },
    "hinweis": {
      "type": "string",
      "nullable": true
    }
  },
  "required": [
    "id",
    "eingegangen_am",
    "status"
  ]
}
Messwert
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "obis": {
      "type": "string",
      "maxLength": 40
    },
    "wert": {
      "type": "number"
    },
    "einheit": {
      "type": "string",
      "enum": [
        "kWh",
        "m3"
      ]
    }
  },
  "required": [
    "obis",
    "wert",
    "einheit"
  ]
}
ZaehlerstandEingang
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "uebermittlung_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "malo": {
      "type": "string",
      "pattern": "^[0-9]{11}$",
      "nullable": true
    },
    "melo": {
      "type": "string",
      "maxLength": 33,
      "nullable": true
    },
    "zaehlernummer": {
      "type": "string",
      "maxLength": 100,
      "nullable": true
    },
    "ablesedatum": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD.",
      "format": "date"
    },
    "herkunft": {
      "type": "string",
      "enum": [
        "trawa",
        "kunde",
        "ersatzwert"
      ]
    },
    "ablesegrund": {
      "type": "string",
      "description": "Fachlicher Ablesegrund."
    },
    "messwerte": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Messwert"
      },
      "minItems": 1,
      "maxItems": 10
    }
  },
  "required": [
    "uebermittlung_id",
    "ablesedatum",
    "herkunft",
    "ablesegrund",
    "messwerte"
  ],
  "description": "Mindestens ein Identifikator erforderlich; mehrere Angaben müssen dasselbe Gerät identifizieren. MSB-Herkunft wird ausschließlich aus authentisierten Markteingängen übernommen, nicht frei behauptet."
}
ZaehlerstandQuittung
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid"
    },
    "uebermittlung_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "eingegangen_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time"
    },
    "status": {
      "type": "string",
      "enum": [
        "eingegangen",
        "in_pruefung",
        "uebernommen",
        "klaerung_erforderlich",
        "abgelehnt"
      ]
    },
    "version": {
      "type": "integer",
      "minimum": 1
    },
    "ersetzt_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid",
      "nullable": true
    },
    "freigegebener_wert_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid",
      "nullable": true
    }
  },
  "required": [
    "id",
    "uebermittlung_id",
    "eingegangen_am",
    "status",
    "version"
  ],
  "description": "Eingang bestätigt keine Plausibilität und keine Übernahme nach XAP."
}
Zaehlerstand
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid"
    },
    "malo": {
      "type": "string",
      "nullable": true
    },
    "melo": {
      "type": "string",
      "nullable": true
    },
    "zaehlernummer": {
      "type": "string",
      "nullable": true
    },
    "ablesedatum": {
      "type": "string",
      "description": "Fachliches Datum, YYYY-MM-DD.",
      "format": "date"
    },
    "herkunft": {
      "type": "string",
      "enum": [
        "msb",
        "kunde",
        "trawa",
        "ersatzwert",
        "unbekannt"
      ]
    },
    "ablesegrund": {
      "type": "string",
      "nullable": true
    },
    "messwerte": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Messwert"
      }
    },
    "plausibilitaetsstatus": {
      "type": "string",
      "enum": [
        "plausibel",
        "unplausibel_freigegeben",
        "ungeprueft_freigegeben"
      ]
    },
    "freigegeben_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time",
      "nullable": true
    },
    "version": {
      "type": "integer",
      "minimum": 1
    },
    "ersetzt_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid",
      "nullable": true
    },
    "versionsbasis": {
      "type": "string",
      "enum": [
        "beobachtung_seit_api"
      ]
    },
    "wertarten": {
      "type": "object",
      "additionalProperties": {
        "type": "string",
        "enum": [
          "zaehlerstand",
          "rueckstellwert",
          "verbrauch"
        ]
      },
      "description": "Einordnung je OBIS aus explizitem Verbrauchskennzeichen bzw. XAP-Rückstellkennzeichen. Bei älteren archivierten Versionen eventuell nicht vorhanden."
    },
    "wandlerfaktoren": {
      "type": "object",
      "additionalProperties": {
        "type": "number",
        "nullable": true
      },
      "description": "Hinterlegter Faktor je OBIS. Die API multipliziert oder glättet die unveränderten XAP-Rohwerte nicht."
    },
    "quellreferenzen": {
      "type": "object",
      "additionalProperties": {
        "type": "string",
        "nullable": true
      },
      "description": "Stabile Referenz auf den ursprünglichen XAP-Ablesesatz je OBIS zur Prüfung durch solvit."
    }
  },
  "required": [
    "id",
    "ablesedatum",
    "herkunft",
    "messwerte",
    "plausibilitaetsstatus",
    "version"
  ],
  "description": "Nur fachlich freigegebene Werte. Unplausible Eingänge vor Entscheidung fehlen auch in GET-Antworten. Versionen zählen Beobachtungen seit Einführung der API; die erste Beobachtung ist Version 1. Historische Herkunft ist ohne Beleg unbekannt."
}
ZaehlerstandSeite
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "daten": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Zaehlerstand"
      }
    },
    "naechster_cursor": {
      "type": "string",
      "nullable": true
    },
    "mengen_und_rueckstellwerte": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Zaehlerstand"
      },
      "description": "Messwertsätze mit wenigstens einem Verbrauchs- oder Rückstellkanal; nicht als fortlaufende Zählerstände behandeln. Auch bei gemischten Geräten bleiben alle Kanäle erhalten, ihre Einordnung steht in wertarten. Ohne belegten Zeitraum wird kein Verbrauchszeitraum erfunden."
    }
  },
  "required": [
    "daten",
    "mengen_und_rueckstellwerte"
  ]
}
ZaehlerstandKorrektur
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "uebermittlung_id": {
      "type": "string",
      "description": "Vom Absender vergebene UUID in kanonischer Kleinschreibung; unverändert und vollständig gespeichert.",
      "format": "uuid"
    },
    "messwerte": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Messwert"
      },
      "minItems": 1,
      "maxItems": 10
    },
    "begruendung": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2000
    }
  },
  "required": [
    "uebermittlung_id",
    "messwerte",
    "begruendung"
  ],
  "description": "id bezeichnet die freigegebene Version aus GET /zaehlerstaende/werte/{id}, nicht die Eingangsquittung. Idempotency-Key entspricht uebermittlung_id; If-Match übernimmt ETag dieses Einzelabrufs. Eine neue Version entsteht erst nach fachlicher Freigabe. Alte Versionen bleiben abrufbar. MSB-/Altdatenwerte benötigen solvit-Prüfung; Abrechnungs- und Plausibilitätsprüfungen bleiben wirksam."
}
Webhook
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "zustellung_id": {
      "type": "string",
      "description": "Interne, von der API vergebene Kennung.",
      "format": "uuid"
    },
    "typ": {
      "type": "string",
      "enum": [
        "status.geaendert"
      ]
    },
    "erstellt_am": {
      "type": "string",
      "description": "Zeitpunkt nach RFC 3339 mit Zeitzone.",
      "format": "date-time"
    },
    "ereignis": {
      "type": "object",
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/Ereignis"
        }
      ]
    },
    "status": {
      "$ref": "#/components/schemas/Vertragsstatus"
    }
  },
  "required": [
    "zustellung_id",
    "typ",
    "erstellt_am",
    "ereignis",
    "status"
  ]
}