Skip to content

AI translation

This page was translated from German with AI. If in doubt, the German original applies.

Calls and synchronization

This page describes the operating rules under which a DIWASS integration has to run: how often you are allowed to call, how you fetch changes, what you do in case of errors and how you keep your local store up to date.

The rules come from two directions, and both are binding:

  • The European Commission sets rules for call operations against DIWASS. Violations can lead to API access being blocked.
  • The latency of the chain ZEDAL → DIWASS → authority → ZEDAL determines which timeouts and waiting times are realistic.

This is not a list of recommendations

The Commission monitors compliance with the sequentiality principle and, in the event of continued violations, reserves the right to temporarily or permanently block the API calls of a user. The reasoning in the original text: such calls jeopardize the functioning of the system, and blocking may also be necessary as a defensive measure against cybersecurity threats.

Calls must be sequential

The central rule: each call is only started once the previous one has completed — that is, once DIWASS has responded. Simultaneous calls are to be avoided.

Question Answer
Is there a rate limit (requests per minute)? No. DIWASS does not set a rate limit per API client.
Is there another limit? Yes. DIWASS limits the number of users calling simultaneously per API client.
What happens in case of a violation? First the Commission gets in touch and asks for the behavior to be changed; in the event of continued violations, temporary or permanent blocking.

For your architecture this means:

  • No fan-out across documents. The obvious idea “I’ll push ten movements in parallel, that’s ten times faster” is exactly what is prohibited here.
  • One call queue per user. Serialize the calls in your system instead of relying on serialization by chance. A worker pool with parallelism > 1 on the same user is a violation, even if it works out most of the time.
  • No aggressive polling. The absence of a rate limit is not an invitation. The Commission explicitly requires retrievals to be carried out “only when necessary and without duplicates”.

API client and Client ID

An “API client” is a technical configuration under which the Commission groups several users; it is the entry point through which these users make calls to DIWASS. Every user who reaches DIWASS via the API is assigned to exactly one API client.

  • The identifier of an API client is neutral (examples from the specification: WSR01, WSR85) and does not allow any conclusions about identities; names are to be avoided.
  • The users within one API client are expected to use the API sequentially — so the sequentiality rule applies not only per user, but at the level of the API client.

The Client ID must be configurable — not hard-wired

The Commission monitors the load and can move users between API clients to safeguard the performance of DIWASS. The specification therefore explicitly requires local systems and software to be able to adjust the Client ID attribute on the API calls.

What happens when it comes to that: the Commission announces the new API client and the start date; the standard period for the switchover is 2 to 5 days. Once the period has expired, the user is removed from the old API client and added to the new one.

Symptom of a hard-wired ID: the integration breaks on the day of the switchover — not gradually, but completely, because the user no longer exists in the old client. Therefore keep the Client ID in the configuration, not in the code, and make it changeable without a deployment.

Failed calls

The specification distinguishes between two error classes with different handling. A blanket retry wrapper around all errors is therefore wrong.

Cause of error Required behavior
Technical problem (network, timeout, system malfunction) Do not retry immediately. The local system must allow a reasonable amount of time between attempts.
Invalid data elements (example from the specification: duplicate notification number) Do not repeat until the submitting user has corrected the underlying errors.

Practical implementation:

  • Never retry data errors automatically. A validation error does not get better through repetition — it becomes a sequentiality violation. Such cases belong in a rework queue with manual correction, not in a retry loop. Which error code belongs to which class is listed in the Error codes.
  • For technical errors, check the time budget first. A large share of apparent server errors on DIWASS paths are pure timeouts — see Time budgets.
  • Backoff with an upper limit, not endless. The text requires “a reasonable amount of time between attempts”; an exponential backoff meets this, a retry storm does not.

Fetching changes

The Commission prescribes a two-step pattern and explicitly rejects full exports: first retrieve the list of events since the last synchronization, then fetch only the documents that have actually changed, one by one.

flowchart LR
    A["1. Event list<br/>since last synchronization"] --> B["2. Filter:<br/>what is new / changed?"]
    B --> C["3. Fetch only these documents<br/>one by one"]
    C --> D["4. Update the<br/>local store"]
    D -.->|next cycle| A

No full export

The specification requires calls to be optimized in number and size, and calls for a mechanism that fetches exclusively what is new and changed — not the entire store on every synchronization. A full export per cycle is at the same time a violation of the duplicate rule (“only when necessary and without duplicates”).

Step 1 — Event list

On the ZEDAL path this is RetrievalApi.LookupEventList. The request narrows down via a time window and optionally via a single document identifier:

Field Meaning
date_from, date_to Time window of the synchronization. Set date_from to the time of the last successful synchronization.
notification_identifier Only events for a specific notification
movement_identifier Only events for a specific movement
annex7_identifier Only events for a specific Annex VII document

The response (notification_event_list.waste_event) carries an identifier, event code and timestamp for each event:

Field Content
identifier.event_identifier Event identifier
code Event code from the ModificationEventCode code list
description optional free text
event_date Time of the event

This means the list itself already tells you what kind of change took place on which document — a separate detail retrieval per event is not necessary on this path.

The ModificationEventCode code list follows a recognizable pattern on which you can base your routing:

Prefix Meaning Examples
SUBMIT_ / NEW_ Something was created for the first time SUBMIT_NOTIFICATION, NEW_DECISION, NEW_REQUEST_INFORMATION, NEW_ARTICLE15
UPDATE_ Something existing was changed UPDATE_NOTIFICATION, UPDATE_DECISION, UPDATE_MOVEMENT_FACILITY_RECEIPT
SIGNAL_ A signal was set SIGNAL_PROPERLY_CARRIED_OUT, SIGNAL_PROPERLY_COMPLETED, SIGNAL_CANCEL_NOTIFICATION, SIGNAL_CANCEL_MOVEMENT
ANNOUNCE_MOVEMENT, NEW_MOVEMENT_*, UPDATE_MOVEMENT_* Movement lifecycle NEW_MOVEMENT_CARRIER_TRANSPORT, NEW_MOVEMENT_FACILITY_COMPLETION, NEW_MOVEMENT_TAKEBACK
GLW_ Annex VII lifecycle (green-listed waste) GLW_ANNOUNCE, GLW_NEW_CONSIGNEE_RECEIPT, GLW_CANCEL

code is a token, not a proto enum

In the proto, the event codes are given as documentation on a StringField (ModificationEventCode), not as a generated enum. So do not treat unknown codes as errors: if a code is added, your synchronization must keep running and put the case into rework instead of aborting.

Step 2 — Fetch selectively

From the event code you derive what you fetch — and what you specifically do not fetch. That is where the real savings lie.

Target object Operation
Notification CompanyDocumentApi.GetNotification (authority side: AuthorityDocumentApi.GetDocument)
Movement CompanyDocumentApi.GetMovement
Annex VII document CompanyDocumentApi.GetAnnex7
Movements for a notification RetrievalApi.GetAllMovements
Annex VII documents in a period RetrievalApi.GetAllAnnex7
Change details of notifications RetrievalApi.LookupSyncNotification
Attachment AttachmentApi.GetAttachment

The Get… operations of the CompanyDocumentApi are not a retrieval by number

Despite their names, GetNotification, GetMovement and GetAnnex7 take the XML payload of the document as input, not its identifier. They update a document that you already have — they do not fetch one that you do not have yet.

To get a document into your local store by its business number, use LoadFromZedalByNotificationId(…) or LoadFromZedalByMovementId(…) on the matching handler in the SDK. And if the document came from outside, it must first have been taken over via the inbox import — otherwise it is simply not in your store yet.

The example from the EU specification illustrates the distinction: if an event “competent authority of destination has issued a decision” arrives for notification A and an event “notifier has created a movement” arrives for notification B, then only A is fetched again as a notification. For B, nothing has changed in the notification document — there the movement would be the right target, not the notification.

Attachments are never included

Neither the notification, the movement nor the Annex VII retrieval includes attachment contents; only attachment identifiers are returned. The content is a separate call. Likewise, the notification retrieval lists movements only as a list of identifiers — so plan for at least three levels: event → document → attachment.

How often?

The Implementing Regulation sets a lower limit, not an upper limit: under Article 20(2), local systems and software are to synchronize the entered data after every operation in accordance with Annex II, Parts A–E, and in any case at least every 12 hours.

A viable operating plan follows from this:

  • Event-driven after your own operations. Every push of your own must be followed by a synchronization, because the actual response only arrives asynchronously (see Lifecycle → Sending procedure).
  • Periodic as a safety net underneath. A cycle well below 12 hours, with date_from = time of the last successful synchronization.
  • Let windows overlap. Set date_from a little before the last synchronization so that you do not miss anything in edge cases — idempotency on your side is cheaper than a lost authority decision.
  • Only the last period, not the history. The specification is explicit here: the list of the previous day’s operations, not all those ever entered in DIWASS.

Keeping the local store up to date

Keeping DIWASS identifiers locally

The specification requires external systems to adapt their data model to that of the Implementing Regulation and to maintain the relationship between the local object and the DIWASS object. For every operator, every notification and every other document held in both places, the associated DIWASS identifier must be stored locally and used in communication with the central system — both business identifiers such as the notification number and DIWASS’s own keys such as the operator ID.

Without this mapping, incremental synchronization is not possible: you receive events for identifiers that you cannot resolve.

External changes take time

100 to 150 seconds, not immediately

An update from outside — a request for additional information from an authority, a decision, a response to a request for change — does not appear synchronously in the ZEDAL store. 100 to 150 seconds were measured between the authority’s push to DIWASS and its appearance in ZEDAL.

Within this window, reloading the document returns the previous state: empty intent list, no authority block. That looks like “nothing happened”, but means “not there yet”.

Consequence for the code: never treat a missing authority block as a final result. A test that checks immediately after the authority’s push will reliably fail for the wrong reason.

The inbox import is the delivery path

Without the inbox import, the new version does not appear

The inbox import is not a cleanup step, but the path by which incoming document versions reach the document store. If you skip it, you will not see the authority’s response — no matter how long you wait and how often you reload the document.

The area itself and its role in the data model are described under Storage areas. The import consists of two steps — see and take over:

Step Operation Purpose
see DocumentApi.ListInbox List incoming entries. Without condition parameters, all unread entries without a time limit
see DocumentApi.ApplyInboxFilter Runs the automatic filter rules configured in the ZEDAL portal
take over DocumentApi.ImportTransportDocumentFromInbox Take an incoming entry over into the transport paper area (Versandpapier-Bereich)
take over DocumentApi.ImportDocumentToFileAreaFromInbox Take an incoming entry over into the file area (Nachweis-Bereich)
clean up DocumentApi.SetInboxDocumentsRead Mark incoming entries as read
clean up DocumentApi.RemoveDocumentsFromInbox Remove incoming entries from the inbox

The matching step happens on the server: the import itself searches for the matching existing document and stores a new file version on it; if it does not find one, a new document is created. You do not need any client-side lookup logic for this — and you should not rebuild it. The source entry remains in the inbox on import; removing it is a separate call.

In the .NET SDK this is bundled: client.DocumentHandler.CreateInboxImporter() returns an IInboxImporter with ImportAsTransportAsync(...), ImportToFileAreaAsync(...) and ListPendingAsync(...) — including a type filter via document_type, page size and an InboxImportReport whose Failed list names the entries that could not be taken over. The default is conservative: only successfully imported entries are removed from the inbox; failed ones remain and show up in the report. For unattended runs, in which an entry with defective content would otherwise block the page indefinitely, there is removeOnFailure: true.

Taking over from the inbox is only available via gRPC, not via REST/JSON

ImportTransportDocumentFromInbox and ImportDocumentToFileAreaFromInbox are not included in the running OpenAPI specification — via REST you can access the inbox for reading (ListInbox), filtering (ApplyInboxFilter) and cleaning up (SetInboxDocumentsRead, RemoveDocumentsFromInbox) — but not for taking entries over.

If you are planning a pure REST integration and depend on incoming document versions, you have to run the take-over step via gRPC or the SDK. Check this early — it is an architecture decision, not a detail.

A robust synchronization cycle interleaves both: retrieve the event list, import the inbox, then read the affected documents. If you only poll without importing, you are polling against a state that never changes.

Time budgets

The time budgets and the measured durations are listed with the calls they belong to: Lifecycle → Sending procedure. Here, only the consequences for call operations:

  • The SDK’s client default is 30 seconds and is too short for DIWASS paths. Set the deadline per call via the ZedalCallContext instead of raising the default globally — the budgets differ considerably per stage.
  • Because the calls have to run sequentially, these budgets add up. A multi-step movement chain is not a matter of seconds; plan the processing as a background process, not in the request cycle of a web UI.
  • A budget that is too tight produces errors that look like server problems but are purely a matter of latency — and a retry on them is then a sequentiality violation on a call that is still running on the server.

No draft status via the API

The first call is the submission

What would be a draft in the user interface is already submitted via the API: a notification created via the API goes directly into the Submitted status, a movement directly into Announced. There is no preliminary stage for trying things out — all validations apply on the first call, and an incorrectly submitted document is submitted.

This ties in with the error rule above: at this point, a data error is not “a failed attempt”, but potentially a case that has to be corrected or canceled. Therefore build your pre-validation before sending, not afterwards — the Field rules describe what DIWASS insists on beyond the schema.

For draft data you need your own storage on your side. A document may only be sent towards DIWASS once it is complete.

What gets you into trouble

Short list of the patterns that either get an integration blocked or produce wrong results:

Pattern Consequence
Parallel calls for the same user / API client Violation of the sequentiality principle; if continued, temporary or permanent blocking
Full export on every synchronization instead of event list + delta Violation of the requirement to optimize the number and size of calls
Immediate retry after a technical error Violation of the waiting time requirement; usually aggravates the cause
Automatic retry after a data error Prohibited without correction; also creates duplicates
Hard-wired Client ID Integration breaks when the Commission moves the user (only 2–5 days’ notice)
Synchronization less often than every 12 hours Falls short of the requirement of Article 20(2) of the Implementing Regulation
Assessing the state from the document after 5 seconds External updates take 100–150 s; you are reading the previous state
Polling without inbox import The new version never appears in the document store
30-second default for DIWASS calls Timeouts that look like server errors
Local model without DIWASS identifiers Events cannot be resolved to local objects

What’s next

  • Lifecycle — two-phase sending, stage sequences, time budgets
  • Storage areas — inbox, file area, transport paper area
  • Error codes — technical error or data error?
  • Field rules — pre-validation before the first call