AI translation
This page was translated from German with AI. If in doubt, the German original applies.
Notification with the SDK¶
Setup and, for each stage, the call via the NotificationDocumentHandler.
The background in business terms is described in the guide Notification (company role).
Setup¶
// Client + Session — for details see Getting started
var client = ZedalClientFactory.Default.CreateClient(
TargetSystem.Test, "MyIntegrationApp", "1.0.0");
await client.CreateSession(
userId: "mysubscriber-myaccount@1:mypassword",
appToken: new AppToken("<APP_TOKEN_JWT>"));
In the SDK, the notification lifecycle is managed via a notification handler. The handler encapsulates the document including versioning, validation and push logic:
One handler per notification. Repeated editing of the same notification (e.g. a reply to a request for additional information) is done via LoadFromZedalByNotificationId(notificationId) on a new handler.
The mirror-image variant for the authority side is called CreateAuthorityNotificationHandler() and returns an AuthorityNotificationDocumentHandler — see Authority.
Stage 1 — CreateNotification¶
The nesting of the document¶
The field types are protobuf wrappers (StringField, BoolField, Int32Field, DoubleField, TimestampField), not .NET primitives. The SDK comes with short constructors for them:
Building it in code¶
// 1) Operator lookups via EORI — returns the DIWASS-internal operator IDs
long notifierId = await client.DiwassApi.OperatorApi.FindOperatorForEori("DE0001234567");
long consigneeId = await client.DiwassApi.OperatorApi.FindOperatorForEori("FR0009876543");
// 2) Assemble the submission
var submission = new SubmissionDocumentType
{
Base = new BaseSubmissionDocumentType
{
TakeBackIndication = Bf(false),
IllegalActivityIndication = Bf(false),
Submission = new SubmissionType
{
// Parties — one UUID, one contact and the DIWASS operator ID per party
Notifier = new WasteOperatorType
{
PartyUuid = Sf(notifierPartyUuid),
Contact = notifierContact, // ContactType
OperatorInternalId = I64(notifierId)
},
Consignee = new WasteOperatorType
{
PartyUuid = Sf(consigneePartyUuid),
Contact = consigneeContact,
OperatorInternalId = I64(consigneeId)
},
IndividualShipmentIndicator = Bf(false),
DestinedForRecoveryDisposal = Sf(FacilityTypeCode.Disposal.ToWire()),
PreConsentIndicator = Bf(false),
// Intended number of shipments — later limits the
// number of movement documents, see movement guide
ShipmentQuantity = I32(100),
TotalWasteMeasurement = new NotificationMassVolumeMeasureType
{
MassMeasure = new MassMeasureType { Tonnes = Df(50000.0) }
},
ShipmentPeriod = new ShipmentPeriodType
{
FirstStartDate = DateUtc(2026, 10, 1),
LastStartDate = DateUtc(2027, 9, 30)
},
Packaging = new PackagingType
{
PackageTypeCode = { Sf(PackageTypeCode.Box.ToWire()) }
},
WasteClassification = new WasteClassificationType
{
// Code WITH group spaces and ListId token — either one is
// grounds for rejection if it is wrong. See code lists.
WasteTypeCode =
{
new WasteCodeType
{
Code = Sf("01 03 06"),
ListId = Sf(WasteCodeListId.Low.ToWire()) // "LoW"
}
}
},
// Further mandatory fields, not shown here: WasteProducer[],
// Carrier[], RecoveryDisposalFacility[], DisposalRecoveryOperations,
// ExportState, ImportState, TransitState[]
}
}
};
// 3) Save + send to DIWASS
await handler.CreateSubmission(submission);
await handler.SaveToZedal();
await handler.SendToDiwass();
string notificationId = handler.DiwassNotificationId;
// Format: <CA code><YY><sequence number>i, example DE0126005589i
Complete value lists and rules
The values the individual fields may take are listed in the code lists — including the spellings on which submissions regularly fail. Which fields DIWASS enforces beyond the schema and which cross-references are checked is described in the field rules.
Both are part of building a submission. The schema alone is not enough: there are fields that are annotated as optional and are still rejected if they are missing.
The prefix of the notification number is the authority code
The number is structured as <CA code><YY><serial number>i. The prefix is the code of the competent authority of dispatch — if it does not match the responsible authority, the submission is rejected.
And: For a rejected submission, DIWASS returns the string UNKNOWN as the notification number. If you take over the return value without checking it, you store this value as the case number and lose the mapping. Before persisting, check whether the submission succeeded at all.
SaveToZedal persists the document as ZeWaSS XML in ZEDAL (it can be reloaded at any time, but has not been sent yet). SendToDiwass then triggers the asynchronous sending to DIWASS. Neither method has a return value; you can see the persisted state in handler.ZedalDocumentId and handler.TargetFileId, and the sending status in handler.LastTransferLogId.
As soon as the platform has stored the DIWASS response as a new version of the document, the notification is in status SUBMITTED — the authorities concerned then become active (acknowledgment, request for additional information, decision). The exact mechanics of fetching the response are described in Lifecycle → Sending procedure.
Waiting for the transmission confirmation
If you want to handle the push synchronously, call the following directly after SendToDiwass():
// Time budget depends on the payload: without attachment around 50 s, WITH attachment over 90 s.
// The client default of 30 s is not enough in either case.
await handler.WaitForDiwassTransfer(
timeout: TimeSpan.FromMinutes(3),
pollInterval: TimeSpan.FromSeconds(5));
DiwassTransferFailedException on rejection by DIWASS and TimeoutException on timeout. Reference values for other cases are given in the lifecycle.
Validation before the push — and why it matters especially here
After each stage call, structured errors are available in handler.ValidationErrors, with
handler.HasValidationErrors as a quick check. Check both before you send.
This is more important here than with other APIs: via the interface there is no draft status. A notification created via the API is submitted immediately; there is no preliminary stage for trying things out. What you send is with the authorities.
The meaning of the returned codes is described in the error codes.
Stage 3 — Responding to a request for additional information¶
Variant A: SetReplyToRequestInformation¶
var handler = client.DocumentHandler.CreateNotificationHandler();
await handler.LoadFromZedalByNotificationId(notificationId);
// Extract the ID of the open RequestInformation from the notification state
string requestInformationId = ExtractLatestRequestInformationId(handler.Notification);
await handler.SetReplyToRequestInformation(new RequestInformationDocumentType
{
// RequestInformationId, reply text, attachment references …
});
await handler.SaveToZedal();
await handler.SendToDiwass();
Variant B: SetSubmission (update)¶
var handler = client.DocumentHandler.CreateNotificationHandler();
await handler.LoadFromZedalByNotificationId(notificationId);
var updatedSubmission = /* adjusted copy of the original submission */;
updatedSubmission.Base.NotificationId = notificationId; // reference to the existing notification
await handler.SetSubmission(updatedSubmission);
await handler.SaveToZedal();
await handler.SendToDiwass();
Update only while a request for additional information is open
SetSubmission on a notification that has already been submitted is only permitted if a RequestInformation is currently open. Without an open request, the call is rejected on the server side. If you need to make changes after consent, use stage 5 (SetRequestForChangeAfterConsent) instead.
Stage 5 — SetRequestForChangeAfterConsent (optional)¶
If changes become necessary after consent has been given (e.g. adjusting the approved quantity under Article 7 of Regulation 2024/1157), the notifier submits a request for change:
var handler = client.DocumentHandler.CreateNotificationHandler();
await handler.LoadFromZedalByNotificationId(notificationId);
await handler.SetRequestForChangeAfterConsent(new RequestForChangeAfterConsentDocumentType
{
// Justification, specific changes requested, attachment references if needed
});
await handler.SaveToZedal();
await handler.SendToDiwass();
Stage 8 — SubmitNotificationSignal (optional)¶
As long as the notification is not already in the final state CANCELLED, the notifier can cancel it (Article 6(2)). Cancellation and other notifier signals (e.g. withdrawal before consent) are sent via SubmitNotificationSignal:
var handler = client.DocumentHandler.CreateNotificationHandler();
await handler.LoadFromZedalByNotificationId(notificationId);
await handler.SubmitNotificationSignal(new NotificationSignalDocumentType
{
// Signal type (e.g. NotificationCancelled), justification
});
await handler.SaveToZedal();
await handler.SendToDiwass();
Helper functions in the sample code¶
Recurring tasks such as the EORI lookup or extracting the RequestInformationId from the latest server response are grouped as private helper methods in the official sample project. They are not part of the public SDK API and should be rebuilt in your own codebase as needed.
private static async Task<long> LookupOperatorIdByEori(IZedalClient client, string eori)
=> await client.DiwassApi.OperatorApi.FindOperatorForEori(eori);
private static string ExtractLatestRequestInformationId(NotificationDocument notification)
{
// Find the most recent open RequestInformation in the current notification state
// … for the implementation, see the sample
}