Aufrufe und Synchronisation¶
Diese Seite beschreibt die Betriebsregeln, unter denen eine DiWaSS-Integration laufen muss: wie oft du rufen darfst, wie du Änderungen abholst, was du bei Fehlern tust und wie du deinen lokalen Bestand aktuell hältst.
Die Regeln kommen aus zwei Richtungen, und beide sind verbindlich:
- Die Europäische Kommission setzt Regeln für den Aufrufbetrieb gegen DiWaSS. Verstöße können zur Sperrung des API-Zugangs führen.
- Die Laufzeit der Kette ZEDAL → DiWaSS → Behörde → ZEDAL bestimmt, welche Timeouts und Wartezeiten realistisch sind.
Das ist keine Empfehlungsliste
Die Kommission überwacht die Einhaltung des Sequenzialitätsprinzips und behält sich bei fortgesetztem Verstoß die temporäre oder dauerhafte Sperrung der API-Aufrufe eines Nutzers vor. Begründung im Originaltext: Solche Aufrufe gefährden das Funktionieren des Systems, und die Sperrung kann auch als Abwehrmaßnahme gegen Cybersicherheitsbedrohungen erforderlich sein.
Aufrufe müssen sequenziell erfolgen¶
Die zentrale Regel: Jeder Aufruf wird erst gestartet, wenn der vorherige abgeschlossen ist — also wenn DiWaSS geantwortet hat. Gleichzeitige Aufrufe sind zu vermeiden.
| Frage | Antwort |
|---|---|
| Gibt es ein Rate-Limit (Requests pro Minute)? | Nein. DiWaSS setzt kein Rate-Limit pro API-Client. |
| Gibt es eine andere Begrenzung? | Ja. DiWaSS begrenzt die Zahl der gleichzeitig aufrufenden Nutzer pro API-Client. |
| Was passiert bei Verstoß? | Erst Kontaktaufnahme durch die Kommission mit der Aufforderung zur Verhaltensänderung, bei fortgesetztem Verstoß temporäre oder dauerhafte Sperrung. |
Für deine Architektur heißt das:
- Kein Fan-out über Dokumente. Der naheliegende Gedanke „ich pushe zehn Movements parallel, das geht zehnmal schneller” ist genau das, was hier untersagt ist.
- Eine Aufrufschlange pro Nutzer. Serialisiere die Aufrufe in deinem System, statt sich auf die Serialisierung durch Zufall zu verlassen. Ein Worker-Pool mit Parallelität > 1 auf demselben Nutzer ist ein Verstoß, auch wenn er meistens gutgeht.
- Kein aggressives Polling. Die Abwesenheit eines Rate-Limits ist keine Einladung. Die Kommission verlangt ausdrücklich, Abrufe „nur wenn erforderlich und ohne Duplikate” durchzuführen.
API-Client und Client-ID¶
Ein „API client” ist eine technische Konfiguration, unter der die Kommission mehrere Nutzer gruppiert; er ist der Eintrittspunkt, über den diese Nutzer Aufrufe an DiWaSS stellen. Jeder Nutzer, der DiWaSS über die API erreicht, ist genau einem API-Client zugeordnet.
- Der Identifier eines API-Clients ist neutral (Beispiele aus der Spezifikation:
WSR01,WSR85) und lässt keine Rückschlüsse auf Identitäten zu; Namen sind zu vermeiden. - Die Nutzer innerhalb eines API-Clients sollen die API sequenziell nutzen — die Sequenzialitätsregel gilt also nicht nur pro Nutzer, sondern auf der Ebene des API-Clients.
Die Client-ID muss konfigurierbar sein — nicht hart verdrahtet
Die Kommission überwacht die Auslastung und kann Nutzer zwischen API-Clients umhängen, um die Performance von DiWaSS zu sichern. Die Spezifikation verlangt deshalb ausdrücklich, dass lokale Systeme und Software das Attribut Client ID auf den API-Aufrufen anpassen können.
Ablauf im Ernstfall: Die Kommission teilt den neuen API-Client und das Startdatum mit; als Standardfrist für die Umstellung sind 2 bis 5 Tage vorgesehen. Nach Ablauf der Frist wird der Nutzer aus dem alten API-Client entfernt und dem neuen hinzugefügt.
Symptom bei hart verdrahteter ID: Die Integration bricht am Umstellungstag ab — nicht schleichend, sondern vollständig, weil der Nutzer im alten Client nicht mehr existiert. Halte die Client-ID daher in der Konfiguration, nicht im Code, und lass sie ohne Deployment änderbar sein.
Fehlgeschlagene Aufrufe¶
Die Spezifikation unterscheidet zwei Fehlerklassen mit unterschiedlicher Behandlung. Ein pauschaler Retry-Wrapper über alle Fehler ist damit falsch.
| Fehlerursache | Vorgeschriebenes Verhalten |
|---|---|
| Technisches Problem (Netz, Zeitüberschreitung, Systemstörung) | Nicht sofort erneut versuchen. Das lokale System muss eine angemessene Zeit zwischen den Versuchen zulassen. |
| Ungültige Datenelemente (Beispiel aus der Spezifikation: doppelte Notifizierungsnummer) | Nicht wiederholen, ohne dass der einreichende Nutzer die zugrunde liegenden Fehler behoben hat. |
Praktische Umsetzung:
- Datenfehler nie automatisch wiederholen. Ein Validierungsfehler wird durch Wiederholung nicht besser — er wird zu einem Sequenzialitätsverstoß. Solche Fälle gehören in eine Nacharbeits-Warteschlange mit menschlicher Korrektur, nicht in einen Retry-Loop. Welcher Fehlercode welcher Klasse angehört, steht in den Fehlercodes.
- Bei technischen Fehlern zuerst das Zeitbudget prüfen. Ein großer Teil der scheinbaren Serverfehler auf DiWaSS-Pfaden sind reine Laufzeitüberschreitungen — siehe Zeitbudgets.
- Backoff mit Obergrenze, nicht endlos. Der Text verlangt „angemessene Zeit zwischen den Versuchen”; ein exponentieller Backoff erfüllt das, ein Retry-Sturm nicht.
Änderungen abholen¶
Die Kommission gibt ein zweistufiges Muster vor und lehnt Vollabzüge ausdrücklich ab: Zuerst die Liste der Ereignisse seit der letzten Synchronisation abrufen, dann nur die tatsächlich geänderten Dokumente einzeln nachladen.
flowchart LR
A["1. Ereignisliste<br/>seit letztem Abgleich"] --> B["2. Filtern:<br/>was ist neu / geändert?"]
B --> C["3. Nur diese Dokumente<br/>einzeln nachladen"]
C --> D["4. Lokalen Bestand<br/>aktualisieren"]
D -.->|nächster Zyklus| A
Kein Vollabzug
Die Spezifikation verlangt, Aufrufe in Zahl und Größe zu optimieren, und fordert einen Mechanismus, der ausschließlich Neues und Geändertes holt — nicht bei jeder Synchronisation den gesamten Bestand. Ein Vollabzug pro Zyklus ist zugleich ein Verstoß gegen die Duplikatsregel („nur wenn erforderlich und ohne Duplikate”).
Schritt 1 — Ereignisliste¶
Auf dem ZEDAL-Pfad ist das RetrievalApi.LookupEventList. Der Request grenzt über ein Zeitfenster ein und optional über eine einzelne Dokumentkennung:
| Feld | Bedeutung |
|---|---|
date_from, date_to |
Zeitfenster des Abgleichs. Setze date_from auf den Zeitpunkt des letzten erfolgreichen Abgleichs. |
notification_identifier |
Nur Ereignisse zu einer bestimmten Notifizierung |
movement_identifier |
Nur Ereignisse zu einem bestimmten Movement |
annex7_identifier |
Nur Ereignisse zu einem bestimmten Annex-VII-Dokument |
Die Antwort (notification_event_list.waste_event) trägt pro Ereignis Kennung, Ereignis-Code und Zeitstempel:
| Feld | Inhalt |
|---|---|
identifier.event_identifier |
Ereignis-Kennung |
code |
Ereignis-Code aus der Codeliste ModificationEventCode |
description |
optionaler Freitext |
event_date |
Zeitpunkt des Ereignisses |
Damit steckt bereits in der Liste selbst, welche Art von Änderung an welchem Dokument stattgefunden hat — ein separater Detailabruf pro Ereignis ist auf diesem Weg nicht nötig.
Die Codeliste ModificationEventCode folgt einem erkennbaren Muster, an dem du dein Routing aufhängen kannst:
| Präfix | Bedeutung | Beispiele |
|---|---|---|
SUBMIT_ / NEW_ |
Etwas ist erstmals entstanden | SUBMIT_NOTIFICATION, NEW_DECISION, NEW_REQUEST_INFORMATION, NEW_ARTICLE15 |
UPDATE_ |
Etwas Bestehendes wurde geändert | UPDATE_NOTIFICATION, UPDATE_DECISION, UPDATE_MOVEMENT_FACILITY_RECEIPT |
SIGNAL_ |
Ein Signal wurde gesetzt | SIGNAL_PROPERLY_CARRIED_OUT, SIGNAL_PROPERLY_COMPLETED, SIGNAL_CANCEL_NOTIFICATION, SIGNAL_CANCEL_MOVEMENT |
ANNOUNCE_MOVEMENT, NEW_MOVEMENT_*, UPDATE_MOVEMENT_* |
Movement-Lebenszyklus | NEW_MOVEMENT_CARRIER_TRANSPORT, NEW_MOVEMENT_FACILITY_COMPLETION, NEW_MOVEMENT_TAKEBACK |
GLW_ |
Annex-VII-Lebenszyklus (Grüne-Liste-Abfälle) | GLW_ANNOUNCE, GLW_NEW_CONSIGNEE_RECEIPT, GLW_CANCEL |
code ist ein Token, keine Proto-Enum
Die Ereignis-Codes stehen im Proto als Dokumentation an einem StringField (ModificationEventCode), nicht als generierte Enum. Behandle unbekannte Codes deshalb nicht als Fehler: Kommt ein Code hinzu, muss dein Abgleich weiterlaufen und den Fall in die Nacharbeit legen, statt abzubrechen.
Schritt 2 — gezielt nachladen¶
Aus dem Ereignis-Code leitest du ab, was du nachlädst — und was du gerade nicht nachlädst. Das ist der eigentliche Sparhebel.
| Zielobjekt | Operation |
|---|---|
| Notifizierung | CompanyDocumentApi.GetNotification (Behördenseite: AuthorityDocumentApi.GetDocument) |
| Movement | CompanyDocumentApi.GetMovement |
| Annex-VII-Dokument | CompanyDocumentApi.GetAnnex7 |
| Movements zu einer Notifizierung | RetrievalApi.GetAllMovements |
| Annex-VII-Dokumente im Zeitraum | RetrievalApi.GetAllAnnex7 |
| Änderungsdetails von Notifizierungen | RetrievalApi.LookupSyncNotification |
| Anhang | AttachmentApi.GetAttachment |
Die Get…-Operationen der CompanyDocumentApi sind kein Abruf über die Nummer
Trotz des Namens nehmen GetNotification, GetMovement und GetAnnex7 den XML-Payload des
Dokuments als Eingabe, nicht dessen Kennung. Sie aktualisieren ein Dokument, das du schon
hast — sie holen keines, das du noch nicht hast.
Um ein Dokument über seine fachliche Nummer in den lokalen Bestand zu bekommen, nutze im SDK
LoadFromZedalByNotificationId(…) bzw. LoadFromZedalByMovementId(…) auf dem passenden
Handler. Und wenn das Dokument von außen kam, muss es zuvor über den
Inbox-Import übernommen worden sein — sonst liegt es
schlicht noch nicht in deinem Bestand.
Das Beispiel aus der EU-Spezifikation illustriert die Trennschärfe: Kommt für Notifizierung A ein Ereignis „Bestimmungsbehörde hat eine Entscheidung abgegeben” und für Notifizierung B ein Ereignis „Notifizierender hat ein Movement erzeugt”, dann wird nur A als Notifizierung nachgeladen. Bei B hat sich am Notifizierungsdokument nichts geändert — dort wäre das Movement das richtige Ziel, nicht die Notifizierung.
Anhänge kommen nie mitgeliefert
Weder der Notifizierungs- noch der Movement- noch der Annex-VII-Abruf liefert Anhangsinhalte mit; zurück kommen nur Anhang-Kennungen. Der Inhalt ist ein eigener Aufruf. Genauso werden im Notifizierungsabruf Movements nur als Liste von Kennungen geführt — plane also mindestens drei Ebenen ein: Ereignis → Dokument → Anhang.
Wie oft?¶
Die Durchführungsverordnung setzt eine Untergrenze, keine Obergrenze: Nach Artikel 20 Absatz 2 sollen lokale Systeme und Software die eingebrachten Daten nach jedem Vorgang nach Anhang II Teile A–E synchronisieren und in jedem Fall mindestens alle 12 Stunden.
Daraus ein tragfähiger Betriebsplan:
- Ereignisgetrieben nach eigenen Vorgängen. Nach jedem eigenen Push gehört ein Abgleich dazu, weil die fachliche Antwort erst asynchron eintrifft (siehe Lebenszyklus → Versandverfahren).
- Periodisch als Netz darunter. Ein Zyklus deutlich unter 12 Stunden, mit
date_from= Zeitpunkt des letzten erfolgreichen Abgleichs. - Fenster überlappen lassen. Setze
date_frometwas vor den letzten Abgleich, damit dir bei Grenzfällen nichts entgeht — Idempotenz auf deiner Seite ist billiger als eine verlorene Behördenentscheidung. - Nur die letzte Periode, nicht die Historie. Die Spezifikation ist hier ausdrücklich: die Liste der Vorgänge des Vortags, nicht alle jemals in DiWaSS eingebrachten.
Lokalen Bestand aktuell halten¶
DiWaSS-Kennungen lokal führen¶
Die Spezifikation verlangt, dass externe Systeme ihr Datenmodell an das der Durchführungsverordnung anpassen und die Beziehung zwischen lokalem Objekt und DiWaSS-Objekt halten. Für jeden Operator, jede Notifizierung und jedes weitere doppelt geführte Dokument muss die zugehörige DiWaSS-Kennung lokal gespeichert und in der Kommunikation mit dem Zentralsystem verwendet werden — sowohl fachliche Kennungen wie die Notifizierungsnummer als auch DiWaSS-eigene Schlüssel wie die Operator-ID.
Ohne diese Zuordnung ist ein inkrementeller Abgleich nicht möglich: Du bekommst Ereignisse zu Kennungen, die du nicht auflösen kannst.
Fremde Änderungen brauchen Zeit¶
Zwei bis drei Minuten, nicht sofort
Ein Update von außen — Nachforderung einer Behörde, Entscheidung, Antwort auf einen Änderungsantrag — erscheint nicht synchron im ZEDAL-Bestand. Rechne mit zwei bis drei Minuten zwischen dem Push der Behörde an DiWaSS und dem Auftauchen in ZEDAL.
In diesem Fenster liefert ein erneutes Laden des Dokuments den Ausgangsstand: leere Intent-Liste, kein Behörden-Block. Das sieht aus wie „nichts passiert”, ist aber „noch nicht da”.
Konsequenz für den Code: Bewerte einen fehlenden Behörden-Block nie als endgültigen Befund. Eine Zustandsprüfung unmittelbar nach dem Behörden-Push schlägt zuverlässig falsch fehl.
Der Inbox-Import ist der Zustellweg¶
Ohne Inbox-Import erscheint die neue Version nicht
Der Inbox-Import ist kein Aufräumschritt, sondern der Weg, auf dem eingehende Dokumentversionen in den Dokumentenbestand gelangen. Wer ihn auslässt, sieht die Behördenantwort nicht — unabhängig davon, wie lange er wartet und wie oft er das Dokument neu lädt.
Der Bereich selbst und seine Rolle im Datenmodell sind unter Speicherbereiche beschrieben. Der Import besteht aus zwei Schritten — sehen und übernehmen:
| Schritt | Operation | Zweck |
|---|---|---|
| sehen | DocumentApi.ListInbox |
Eingänge auflisten. Ohne Bedingungsparameter alle ungelesenen Einträge ohne Zeitgrenze |
| sehen | DocumentApi.ApplyInboxFilter |
Führt die im ZEDAL-Portal konfigurierten automatischen Filterregeln aus |
| übernehmen | DocumentApi.ImportTransportDocumentFromInbox |
Eingang in den Versandpapier-Bereich übernehmen |
| übernehmen | DocumentApi.ImportDocumentToFileAreaFromInbox |
Eingang in den Nachweis-Bereich übernehmen |
| aufräumen | DocumentApi.SetInboxDocumentsRead |
Eingänge als gelesen markieren |
| aufräumen | DocumentApi.RemoveDocumentsFromInbox |
Eingänge aus der Inbox entfernen |
Der Zuordnungsschritt liegt auf dem Server: Der Import sucht selbst das passende bestehende Dokument und legt eine neue Dateiversion darauf ab; findet er keines, entsteht ein neues Dokument. Eine clientseitige Lookup-Logik brauchst du dafür nicht — und solltest du nicht nachbauen. Der Quell-Eintrag bleibt beim Import in der Inbox stehen; das Entfernen ist ein eigener Aufruf.
Im .NET-SDK ist das gebündelt: client.DocumentHandler.CreateInboxImporter() liefert einen IInboxImporter mit ImportAsTransportAsync(...), ImportToFileAreaAsync(...) und ListPendingAsync(...) — inklusive Typ-Filter über document_type, Seitengröße und einem InboxImportReport, dessen Failed-Liste die nicht übernehmbaren Einträge nennt. Der Default ist konservativ: Nur erfolgreich importierte Einträge werden aus der Inbox entfernt, fehlgeschlagene bleiben stehen und tauchen im Report auf. Für unbeaufsichtigte Läufe, in denen ein fachlich defekter Eintrag sonst dauerhaft die Seite blockiert, gibt es removeOnFailure: true.
Die Übernahme aus der Inbox gibt es nur über gRPC, nicht über REST/JSON
ImportTransportDocumentFromInbox und ImportDocumentToFileAreaFromInbox sind in der laufenden OpenAPI-Spezifikation nicht enthalten — über REST erreichst du die Inbox lesend (ListInbox), filternd (ApplyInboxFilter) und aufräumend (SetInboxDocumentsRead, RemoveDocumentsFromInbox) — aber nicht übernehmend.
Wer eine reine REST-Integration plant und auf eingehende Dokumentversionen angewiesen ist, muss den Übernahmeschritt über gRPC oder das SDK fahren. Prüfe das früh — es ist eine Architekturentscheidung, keine Detailfrage.
Ein belastbarer Abgleichzyklus verschränkt beides: Ereignisliste abrufen, Inbox importieren, dann die betroffenen Dokumente lesen. Wer nur pollt, ohne zu importieren, pollt gegen einen Stand, der sich nie ändert.
Zeitbudgets¶
Die Zeitbudgets und Laufzeiten stehen bei den Aufrufen, zu denen sie gehören: Lebenszyklus → Versandverfahren. Hier nur die Konsequenzen für den Aufrufbetrieb:
- Der Client-Default des SDK liegt bei 30 Sekunden und ist für DiWaSS-Pfade zu knapp. Setze die Deadline pro Aufruf über den
ZedalCallContext, statt den Default global hochzudrehen — die Budgets unterscheiden sich pro Stage erheblich. - Weil die Aufrufe sequenziell laufen müssen, addieren sich diese Budgets. Eine mehrstufige Movement-Kette ist keine Sache von Sekunden; plane die Verarbeitung als Hintergrundprozess, nicht im Request-Zyklus einer Weboberfläche.
- Ein zu knappes Budget erzeugt Fehler, die wie Serverprobleme aussehen, aber reine Laufzeit sind — und ein Retry darauf ist dann ein Sequenzialitätsverstoß auf einen Aufruf, der serverseitig noch läuft.
Kein Entwurfsstatus über die API¶
Der erste Aufruf ist die Einreichung
Was über die Oberfläche ein Entwurf wäre, ist über die API bereits eingereicht: Eine per Schnittstelle erzeugte Notifizierung landet direkt im Status Submitted, ein Movement direkt in Announced. Es gibt keine Vorstufe zum Ausprobieren — alle Validierungen greifen beim ersten Aufruf, und ein fehlerhaft eingereichtes Dokument ist eingereicht.
Das verzahnt sich mit der Fehlerregel oben: Ein Datenfehler ist an dieser Stelle nicht „ein fehlgeschlagener Versuch”, sondern potenziell ein Vorgang, der korrigiert oder annulliert werden muss. Baue deine Vorprüfung deshalb vor dem Versand ein, nicht danach — die Feldregeln beschreiben, worauf DiWaSS über das Schema hinaus besteht.
Für Entwurfsdaten brauchst du eine eigene Ablage auf deiner Seite. Ein Dokument darf erst dann in Richtung DiWaSS gehen, wenn es vollständig ist.
Was dich in Schwierigkeiten bringt¶
Kurzliste der Muster, die eine Integration entweder sperren lassen oder falsche Ergebnisse liefern:
| Muster | Folge |
|---|---|
| Parallele Aufrufe für denselben Nutzer / API-Client | Verstoß gegen das Sequenzialitätsprinzip; bei Fortsetzung temporäre oder dauerhafte Sperrung |
| Vollabzug bei jedem Abgleich statt Ereignisliste + Delta | Verstoß gegen die Vorgabe, Zahl und Größe der Aufrufe zu optimieren |
| Sofortiger Retry nach technischem Fehler | Verstoß gegen die Wartezeit-Vorgabe; verschärft die Ursache meist |
| Automatischer Retry nach Datenfehler | Untersagt ohne Korrektur; erzeugt zusätzlich Duplikate |
| Hart verdrahtete Client-ID | Integration bricht ab, wenn die Kommission den Nutzer umhängt (Vorlauf nur 2–5 Tage) |
| Abgleich seltener als alle 12 Stunden | Unterschreitet die Vorgabe aus Artikel 20 Absatz 2 der Durchführungsverordnung |
| Zustand aus dem Dokument nach 5 Sekunden bewerten | Fremd-Updates brauchen zwei bis drei Minuten; du liest den Ausgangsstand |
| Polling ohne Inbox-Import | Die neue Version erscheint nie im Dokumentenbestand |
| 30-Sekunden-Default für DiWaSS-Aufrufe | Timeouts, die wie Serverfehler aussehen |
| Lokales Modell ohne DiWaSS-Kennungen | Ereignisse sind nicht auf lokale Objekte auflösbar |
Was als Nächstes¶
- Lebenszyklus — Zwei-Phasen-Versand, Stage-Reihenfolgen, Zeitbudgets
- Speicherbereiche — Inbox, Nachweis-Bereich, Versandpapier-Bereich
- Fehlercodes — technischer Fehler oder Datenfehler?
- Feldregeln — Vorprüfung vor dem ersten Aufruf