Erste Schritte¶
Diese Seite bringt dich vom Null-Stand bis zum ersten erfolgreichen Aufruf gegen ZEDAL — einmal über REST/JSON und einmal über das .NET SDK. Beide Wege führen auf dieselben Backend-Methoden und benutzen dasselbe Auth-Modell; du brauchst nur einen davon.
| Weg | Wann | Abschnitt |
|---|---|---|
| REST / JSON | Jede Sprache mit HTTP-Client. Kein Codegen, kein SDK. | Erster Aufruf über REST |
| .NET SDK | C#-Anwendungen. Kapselt Token-Refresh, Dokument-Lebenszyklen, Retries. | Erster Aufruf über das .NET SDK |
Wer natives gRPC generieren will, findet die Grundlagen unter API-Referenz → gRPC; der Auth-Ablauf auf dieser Seite gilt dort unverändert.
Voraussetzungen und Zugang¶
Für einen Aufruf brauchst du vier Dinge:
| Angabe | Was es ist | Woher |
|---|---|---|
| Provider | Numerische provider_id der ZEDAL-Instanz, auf der dein Account liegt. Sie gilt für genau eine Umgebung. |
aus der Zugangs-Mail; die Liste liefert DiscoveryApi/ListProvider, siehe Schritt 0 |
| Subscriber + Account + Passwort | Vertrags-Mandant und Benutzerkonto darin. | ZEDAL-Support |
| App-Token | JWT, der deine Anwendung identifiziert und ihr Berechtigungs-Profil festlegt. Er bildet die Rechtetabelle aus dem Portal ab — was der App-Token nicht freischaltet, kann der Account über die API nicht tun. | ZEDAL-Support |
| Umgebung | Test / Preview / Production, siehe Umgebungen. | deine Entscheidung |
App-Tokens und Account-Daten werden nicht öffentlich verteilt. Schreib eine Mail an erp-tools@zedal.de. Damit die Anfrage in einem Durchgang beantwortet werden kann, nenne darin:
- Integrierendes Unternehmen — Name und Anschrift.
- Anwendung — Name und Version. Beide gehen als
clientName/clientVersionin jeden Aufruf ein und tauchen so in den serverseitigen Logs auf; wähle Werte, die du dauerhaft behalten willst. - Ziel-Umgebung — Test, Preview oder Production. Für den Einstieg: Test.
- Provider — falls schon bekannt, der Provider-Kurzname oder die
provider_id; sonst „bitte zuweisen”. - Fachlicher Umfang — welche Rollen und Vorgänge du bedienen willst (z. B. DiWaSS-Notifizierung und Begleitformulare aus Unternehmenssicht, oder Behördensicht). Daraus wird das Berechtigungs-Profil des App-Tokens abgeleitet.
- Art des Logins — interaktiver Benutzer-Login (
UsernameCredentials) oder Prozess ohne Benutzer. Für Prozesse ohne Benutzer folgen API-Keys; du legst sie dann im ZEDAL-Portal unter „Mein ZEDAL“ → „API-Keys“ für den Account an. - Ansprechpartner — technisch und fachlich.
Du bekommst zurück: App-Token, Subscriber, Account, Initialpasswort und die provider_id.
Umgebungen¶
| Umgebung | Zweck | Nativ gRPC | REST / JSON und gRPC-Web |
|---|---|---|---|
| Production | Produktivbetrieb | api.live.zedal.de |
openapi.live.zedal.de |
| Preview | Staging- und Beta-Systeme | api.preview.zedal.de |
openapi.preview.zedal.de |
| Test | Test- und Integrationssysteme | api.test.zedal.de |
openapi.test.zedal.de |
Entwickle gegen Test. Diese Umgebung ist für Integrationen vorgesehen und wird als solche betrieben. Preview ist der Vorschau-Stand vor einem Produktiv-Release; dort testest du sinnvoll erst, wenn deine Integration gegen Test läuft.
Jede Umgebung bedient nur ihre eigenen Provider. Eine provider_id, die auf Test funktioniert, wird auf Production nicht bedient und umgekehrt — auch dann nicht, wenn DiscoveryApi/ListProvider sie auf beiden Hosts auflistet: Die Discovery-Liste ist ein umgebungsübergreifendes Verzeichnis, kein Routing-Versprechen. Maßgeblich ist die provider_id, die dir für diese Umgebung genannt wurde.
Das Auth-Modell¶
Der Ablauf ist dreistufig und für REST, gRPC und SDK identisch.
sequenceDiagram
participant App as Deine Anwendung
participant Z as ZEDAL
App->>Z: AuthApi/IssueRefreshToken<br/>appToken + Credentials
Z-->>App: refreshToken
Note over App: refreshToken sicher ablegen
App->>Z: AuthApi/RequestAccess<br/>Authorization: Bearer refreshToken
Z-->>App: accessToken + newRefreshToken
Note over App,Z: Das vorgelegte refreshToken ist ab hier ungültig.<br/>newRefreshToken persistieren, bevor es weitergeht.
App->>Z: Fachlicher Aufruf<br/>Authorization: Bearer accessToken
Z-->>App: Ergebnis
App->>Z: AuthApi/RevokeRefreshToken (Logout)
| Stufe | Operation | Authentifizierung | Ergebnis |
|---|---|---|---|
| 1 — Login | AuthApi/IssueRefreshToken |
keine (appToken + Credentials im Body) |
refreshToken |
| 2 — Access holen | AuthApi/RequestAccess |
Authorization: Bearer <refreshToken> |
accessToken und newRefreshToken |
| 3 — Fachaufruf | jede weitere Operation | Authorization: Bearer <accessToken> |
— |
| Logout | AuthApi/RevokeRefreshToken |
Authorization: Bearer <refreshToken> |
Session beendet |
Der Login trägt neben dem App-Token die usernameCredentials mit subscriberId, accountId und password. Mit den API-Keys, die folgen, brauchen Prozesse ohne Benutzer den Login nicht mehr: Sie tauschen den Key direkt gegen ein Access-Token.
Gültigkeit¶
| Token | Erneuerung | Woher du die Gültigkeit erfährst |
|---|---|---|
| Access-Token | über RequestAccess mit dem aktuellen Refresh-Token — kein erneuter Login |
exp-Claim im Token |
| Refresh-Token | jede Verwendung liefert ein neues Token | exp-Claim im Token |
Keine Lebensdauer im Client fest annehmen
Beide Token sind JWTs und tragen ihre Ablaufzeit im exp-Claim. Die konkrete Dauer wird
serverseitig beim Ausstellen gesetzt und kann sich ändern — lies sie aus dem Token, statt sie
im Client zu hinterlegen. Eine hart kodierte Annahme führt entweder zu abgelaufenen Aufrufen oder
zu unnötigen Refreshes.
Im .NET SDK steht die Ablaufzeit in Jwt.ValidTo. Das SDK erneuert das Access-Token von selbst,
sobald weniger als fünf Minuten Restlaufzeit bleiben.
Solange dein Prozess das Refresh-Token vor dessen Ablauf verwendet, bleibt die Session bestehen. Die ursprünglichen Credentials brauchst du danach nicht mehr und sollst sie in der Anwendung nicht dauerhaft speichern.
Token-Rotation — hier scheitern die meisten Integrationen¶
Jeder RequestAccess entwertet das vorgelegte Refresh-Token
Stufe 2 gibt immer ein neues Refresh-Token zurück (newRefreshToken), und das eingereichte verliert im selben Moment seine Gültigkeit. Persistiere das neue Token, bevor du den nächsten Fachaufruf machst.
Symptom, wenn du es nicht tust: Der erste Durchlauf funktioniert, der zweite scheitert an Stufe 2 mit HTTP 401 und {"code":16,"message":"The token '…' is invalid."}. Von außen sieht das aus wie ein abgelaufenes Token — tatsächlich ist es ein überschriebenes.
Parallele Prozesse sperren sich gegenseitig aus
Zwei Prozesse, die dasselbe gespeicherte Refresh-Token benutzen, invalidieren sich beim Refresh wechselseitig: Der zweite RequestAccess legt ein Token vor, das der erste bereits verbraucht hat. Beide fallen früher oder später auf HTTP 401, und ein Neustart hilft nicht — das gespeicherte Token ist tot, es hilft nur ein neuer Login mit Credentials.
Zwei tragfähige Muster:
- Ein Refresher. Genau eine Stelle hält die Session und erneuert; alle anderen Prozesse bekommen von ihr das aktuelle Access-Token. Innerhalb eines Prozesses reicht ein Lock um den Refresh.
- Eine Session pro Prozess. Jeder Prozess loggt sich einmal eigenständig ein und führt sein eigenes Refresh-Token in eigenem Storage. Sessions laufen unabhängig voneinander.
Was nicht funktioniert: dasselbe Refresh-Token in mehreren Prozessen oder auf mehreren Maschinen.
Einzelne Operationen verlangen zusätzlich einen Scope — etwa usermgt für die Verwaltungsoperationen der AuthApi. Scopes hängen am App-Token und an der Subscriber-Zuordnung und stecken als Claim im Access-Token; sie werden nicht pro Aufruf angefordert.
Prozesse ohne Benutzer: API-Key¶
API-Keys folgen
API-Keys stehen noch nicht bereit. Dieser Abschnitt beschreibt sie vorab; Zedal.Sdk unterstützt sie ab einem Release nach 1.3.0. Wann API-Keys für deine Umgebung bereitstehen, erfährst du beim ZEDAL-Support (erp-tools@zedal.de).
Ein Prozess ohne Benutzer — Integration, ERP-Anbindung, Skript — braucht weder Login noch Rotation. Er tauscht einen API-Key zusammen mit dem App-Token in AuthApi/RequestAccess direkt gegen ein Access-Token. Ein Refresh-Token entsteht dabei nicht.
sequenceDiagram
participant App as Deine Anwendung
participant Z as ZEDAL
App->>Z: AuthApi/RequestAccess<br/>apiKeyCredentials: apiKey + appToken
Z-->>App: accessToken + accessTokenExpiresOn + notGrantedScopes
Note over App: Kein Refresh-Token. Der Key ändert sich nicht.
App->>Z: Fachlicher Aufruf<br/>Authorization: Bearer accessToken
Z-->>App: Ergebnis
Note over App,Z: Kurz vor Ablauf des Access-Tokens denselben Key erneut tauschen.
| Login mit Credentials | API-Key | |
|---|---|---|
| Access-Token holen | IssueRefreshToken, dann RequestAccess mit dem Refresh-Token |
RequestAccess mit apiKeyCredentials, ohne Authorization-Header |
| Erneuern | RequestAccess mit dem jeweils neuesten Refresh-Token |
RequestAccess erneut mit demselben Key |
| Dauerhaft speichern | das rotierende Refresh-Token | den Key |
| Scopes im Access-Token | aus App-Token und Subscriber-Zuordnung | nur die, die Key und App-Token haben |
| Beenden | RevokeRefreshToken |
nichts; den Key selbst widerrufst du im Portal |
API-Keys legst du im ZEDAL-Portal unter „Mein ZEDAL“ → „API-Keys“ an, für den dort angemeldeten Account; über die API lassen sie sich nicht anlegen. Zum Anlegen fragt das Portal noch einmal das Passwort des Accounts ab, denn der Key gilt über die Sitzung hinaus. Beim Anlegen wählst du die Scopes, die der Key gewähren darf, und optional ein Ablaufdatum. Zur Wahl stehen core, dataexport und wastemanager, und nichts ist implizit: Ein Key ohne Scope weist nur den Account aus. Widerrufen wird ebenfalls im Portal. Danach gibt es mit dem Key kein neues Access-Token mehr; bereits ausgegebene können bis zu ihrem Ablauf gültig bleiben.
Ein API-Key ist kein JWT, sondern eine Zeichenkette der Form zedal_ak_<provider_id>_<key_id>_<geheimnis>. Am festen Präfix erkennen Secret-Scanner einen Key, der versehentlich in ein Repository oder ein Log gerät. Die provider_id darin ist die des Providers, der den Key ausgegeben hat, also der Wert für den Header provider_id. Account, Scopes und Ablauf stehen nicht im Key; die kennt nur ZEDAL. Das Portal zeigt den Key genau einmal. Bewahre ihn auf wie ein Passwort.
Jeder Account legt Keys nur für sich an, auch der Hauptaccount. Der Hauptaccount sieht im Portal zusätzlich die Keys seiner Benutzer und kann sie widerrufen. Ein Key handelt mit den Rechten seines Accounts. Soll er nicht am Zugang einer Person hängen, leg ihn an einem eigenen Account für die Integration an.
Die Antwort von RequestAccess nennt in accessTokenExpiresOn, wann das Access-Token abläuft, und in notGrantedScopes die Scopes des App-Tokens, die ein Key gewähren könnte, dieser aber nicht. Die gewährten Scopes stehen im scope-Claim des Access-Tokens.
Den Key nicht vor jedem Aufruf tauschen
Der Server begrenzt, wie oft ein Key in einer Zeitspanne getauscht werden kann. Darüber antwortet RequestAccess mit HTTP 429 (gRPC-Status RESOURCE_EXHAUSTED). Verwende das Access-Token, bis accessTokenExpiresOn naht, und tausche erst dann.
Ein unbekannter, widerrufener oder abgelaufener Key ergibt HTTP 401 (UNAUTHENTICATED), ebenso ein gesperrter Account.
Erster Aufruf über REST¶
Basis-URL ist der openapi.*-Host der gewählten Umgebung, der Pfad ist der vollqualifizierte gRPC-Methodenname:
Die vollständige Liste aller 131 Operationen steht in der API-Referenz; Details zum Aufrufformat unter REST / JSON.
Der Header provider_id ist bei jedem Aufruf Pflicht
Alle Provider einer Umgebung teilen sich denselben API-Host. Ohne provider_id kann die Plattform den Aufruf keiner Instanz zuordnen — auch nicht beim Login.
Symptom: HTTP 501 mit dem Körper {"code":12,"message":"Bad gRPC response. HTTP status code: 404","details":[]}. Der Text legt einen falschen Pfad nahe; tatsächlich lässt sich der Aufruf nicht zuordnen. Zwei Ursachen kommen infrage: Der Header fehlt — oder er nennt einen Provider, den diese Umgebung nicht bedient.
Einzige Ausnahme ist DiscoveryApi/ListProvider — die Operation ist providerübergreifend und unauthentifiziert.
Die folgenden Schritte laufen mit curl und jq gegen die Test-Umgebung. Setz zuerst die Basiswerte:
export ZEDAL_HOST=https://openapi.test.zedal.de
export ZEDAL_APP_TOKEN='<app token from the access email>'
export ZEDAL_PASSWORD='<account password>'
Schritt 0: Provider ermitteln¶
Der einzige Aufruf, der ohne Token und ohne provider_id funktioniert. Er ist zugleich der schnellste Konnektivitätstest.
curl -s "$ZEDAL_HOST/infotech.zedal.provider_discovery.v1.api.DiscoveryApi/ListProvider" \
| jq '.provider[] | {id, shortName, type}'
Setz die id, die dir für diese Umgebung genannt wurde:
Die vollständige Antwort führt pro Provider zusätzlich name, vendor, portalUrl, soapUrl und grpcUrl.
Schritt 1: Login — Refresh-Token holen¶
REFRESH=$(curl -s -X POST \
"$ZEDAL_HOST/infotech.zedal.core.v1.api.AuthApi/IssueRefreshToken" \
-H 'Content-Type: application/json' \
-H "provider_id: $ZEDAL_PROVIDER" \
-d '{
"appToken": "'"$ZEDAL_APP_TOKEN"'",
"usernameCredentials": {
"subscriberId": "mysubscriber",
"accountId": "myaccount",
"password": "'"$ZEDAL_PASSWORD"'"
}
}' \
| jq -r .refreshToken)
Mit einem API-Key, sobald es ihn gibt, überspringt ein Prozess ohne Benutzer diesen Schritt und Schritt 2, siehe Variante: Access-Token mit API-Key.
Schritt 2: Access-Token holen¶
RESPONSE=$(curl -s -X POST \
"$ZEDAL_HOST/infotech.zedal.core.v1.api.AuthApi/RequestAccess" \
-H 'Content-Type: application/json' \
-H "provider_id: $ZEDAL_PROVIDER" \
-H "Authorization: Bearer $REFRESH" \
-d "{\"refreshToken\": \"$REFRESH\"}")
ACCESS=$(echo "$RESPONSE" | jq -r .accessToken)
REFRESH=$(echo "$RESPONSE" | jq -r .newRefreshToken) # mandatory: the old one is now dead
Das Refresh-Token steht sowohl im Authorization-Header als auch im Body-Feld refreshToken. Beide werden erwartet; ausgewertet und rotiert wird das Feld aus dem Body.
Die letzte Zeile ist der Punkt, an dem eine Integration steht oder fällt: In einer echten Anwendung gehört dorthin ein Schreibvorgang in denselben Speicher, aus dem der nächste Prozessstart das Token liest — nicht eine Shell-Variable.
Variante: Access-Token mit API-Key¶
Folgt mit den API-Keys
Diese Variante steht zur Verfügung, sobald die API-Keys bereitstehen.
Mit einem API-Key ersetzt ein einziger Aufruf die Schritte 1 und 2. RequestAccess bekommt Key und App-Token im Body; einen Authorization-Header braucht es nicht.
export ZEDAL_API_KEY='<API-Key aus dem Portal, zedal_ak_…>'
ACCESS=$(curl -s -X POST \
"$ZEDAL_HOST/infotech.zedal.core.v1.api.AuthApi/RequestAccess" \
-H 'Content-Type: application/json' \
-H "provider_id: $ZEDAL_PROVIDER" \
-d '{
"apiKeyCredentials": {
"apiKey": "'"$ZEDAL_API_KEY"'",
"appToken": "'"$ZEDAL_APP_TOKEN"'"
}
}' \
| jq -r .accessToken)
ZEDAL_PROVIDER ist hier die Zahl hinter zedal_ak_ im Key. Die Antwort enthält kein Refresh-Token; zu speichern gibt es nichts außer dem Key. Weiter geht es mit Schritt 3.
Schritt 3: Fachlicher Aufruf¶
DocumentApi/ListInbox listet die Dokument-IDs im Posteingang. Ein GET mit den Filtern in der Query-String — das Verb steht pro Operation in der Referenz und ist nicht immer POST.
curl -s -G "$ZEDAL_HOST/infotech.zedal.core.v1.api.DocumentApi/ListInbox" \
-H "provider_id: $ZEDAL_PROVIDER" \
-H "Authorization: Bearer $ACCESS" \
--data-urlencode 'from=2026-01-01T00:00:00Z' \
--data-urlencode 'to=2026-12-31T23:59:59Z' \
--data-urlencode 'unread.value=true'
Ohne Filter listet die Operation alle ungelesenen Dokumente ohne Zeitgrenze. Zeitstempel sind RFC 3339 in UTC. Verschachtelte Felder werden in der Query-String mit Punkt geschrieben (unread.value), so wie in der Referenz angegeben.
Fehler lesen¶
Der Antwortkörper hat immer die Form {"code": <grpc-status>, "message": "…", "details": []}. Der HTTP-Status leitet sich aus dem gRPC-Status ab.
| HTTP | Körper | Ursache |
|---|---|---|
501 |
{"code":12,"message":"Bad gRPC response. HTTP status code: 404"} |
provider_id-Header fehlt, oder der genannte Provider wird von dieser Umgebung nicht bedient |
400 |
{"code":3,"message":"MISSING_CREDENTIALS"} |
im Login-Body fehlen die usernameCredentials |
401 |
{"code":16,"message":"The token '…' is invalid."} |
Token abgelaufen, rotiert, widerrufen — oder gar kein JWT |
401 |
{"code":16,"message":"The given authentication token is invalid."} |
Authorization-Header fehlt ganz |
Das ist die Transportebene. Fachliche Ablehnungen aus DiWaSS haben eigene Fehlercodes und können in einer HTTP 200-Antwort stecken, weil der Aufruf technisch erfolgreich war — siehe Fehlercodes.
Aufrufe wiederauffindbar machen
Das .NET SDK schickt bei jedem Aufruf die Header clientName und clientVersion mit; sie landen in den serverseitigen Logs. Über REST setzt du sie als HTTP-Header. Nimm dieselben Werte, die du bei der Registrierung angegeben hast; das verkürzt jede Supportanfrage erheblich.
Erster Aufruf über das .NET SDK¶
Installation¶
Target ist netstandard2.0; die Versionierung folgt Semantic Versioning (1.3.0). Das Paket zieht die generierten gRPC-Typen transitiv mit.
Client anlegen¶
using Zedal.Sdk;
using Zedal.Sdk.Impl; // TargetSystem
using Zedal.Sdk.Jwt; // AppToken, RefreshToken
var client = ZedalClientFactory.Default.CreateClient(
system: TargetSystem.Test, // Production | Preview | Test
clientName: "MyIntegrationApp", // appears server-side as the caller
clientVersion: "1.0.0");
Adressen setzt das SDK aus TargetSystem selbst; provider_id hängt es an jeden Aufruf an, sobald die Session steht. Beides musst du nicht selbst verwalten.
Transport — meist nichts zu tun
Der Parameter transport steht per Default auf ZedalTransport.Auto: Beim Aufbau der Verbindung wird einmal geprüft, ob der native Host HTTP/2 spricht. Nur bei erwiesener HTTP/1.x-Beschränkung schaltet das SDK auf ZedalTransport.GrpcWeb über den openapi.*-Host um; bleibt die Prüfung ohne Antwort (offline, Timeout), bleibt es bei nativem gRPC. Die Prüfung ist ein blockierender Netzwerkaufruf im Konstruktor.
In Umgebungen, in denen HTTP/2 sicher blockiert ist — alte Corporate-Proxies, .NET Framework —, kannst du den Umweg sparen und transport: ZedalTransport.GrpcWeb direkt setzen. Läuft der Verkehr über einen Proxy, gib ihn über den Parameter proxyUrl oder die Umgebungsvariable INFOTECH_GRPC_PROXY_URL an.
Rotations-Callback registrieren — vor dem Login¶
SetAccessTokenChanged wird bei jeder Token-Rotation aufgerufen: nach CreateSession, nach ContinueSession und bei jedem automatischen Refresh.
client.SetAccessTokenChanged(tokens =>
SecureStorage.Save("zedal-refresh-token", tokens.refreshToken.Jwt.EncodedToken));
tokens ist ein Tupel (AccessToken accessToken, RefreshToken refreshToken). Die kodierte JWT-Zeichenkette liegt unter Jwt.EncodedToken; alternativ liefert ToByteArray() dieselbe Zeichenkette als UTF-8-Bytes.
SecureStorage steht in den Beispielen stellvertretend für deinen geschützten Speicher — DPAPI, Keychain, ein Secret-Store. Das Refresh-Token erlaubt, beliebig neue Access-Tokens auszustellen; behandle es wie ein Passwort.
Ohne diesen Callback überlebt die Session keinen Neustart
Das SDK rotiert das Refresh-Token bei jedem Refresh. Wenn du nur das Token aus dem Login speicherst, ist es beim nächsten Programmstart längst entwertet — ContinueSession scheitert dann mit RpcException, Status Unauthenticated, und du brauchst wieder die Credentials.
Registriere den Callback vor CreateSession und vor ContinueSession: Beide rotieren selbst schon, und ein danach registrierter Callback verpasst genau diese erste Rotation.
Login und erster Aufruf¶
await client.CreateSession(
providerId: int.Parse(Environment.GetEnvironmentVariable("ZEDAL_PROVIDER")),
subscriberId: "mysubscriber",
accountId: "myaccount",
password: Environment.GetEnvironmentVariable("ZEDAL_PASSWORD"),
appToken: new AppToken(Environment.GetEnvironmentVariable("ZEDAL_APP_TOKEN")));
var docIds = await client.CoreApi.Documents.ListInbox(
from: DateTime.UtcNow.AddDays(-30),
to: DateTime.UtcNow,
unread: true);
foreach (var docId in docIds)
{
Console.WriteLine(docId.Id);
}
Das ist derselbe Aufruf wie Schritt 3 im REST-Beispiel. Zwei Details:
fromundtomüssen UTC sein. EinDateTimemitKindungleichUtclässt die Protobuf-Konvertierung mitArgumentExceptionscheitern, bevor überhaupt ein Aufruf rausgeht.- Access-Tokens erneuert das SDK selbsttätig, sobald weniger als fünf Minuten Restlaufzeit bleiben. Du musst
RequestAccessnicht selbst aufrufen.
Neben der Variante mit getrennten Feldern gibt es CreateSession(userId, appToken) mit dem kombinierten Muster <subscriber>-<account>@<provider>:<password>. Der Login mit API-Key, CreateSession(apiKey, appToken), folgt mit den API-Keys. Details in der .NET-SDK-Doku.
Neustart: Session fortsetzen¶
var client = ZedalClientFactory.Default.CreateClient(
system: TargetSystem.Test,
clientName: "MyIntegrationApp",
clientVersion: "1.0.0");
// Callback first — ContinueSession already rotates.
client.SetAccessTokenChanged(tokens =>
SecureStorage.Save("zedal-refresh-token", tokens.refreshToken.Jwt.EncodedToken));
var refreshToken = new RefreshToken(SecureStorage.Load("zedal-refresh-token"));
await client.ContinueSession(refreshToken);
Kein Credential-Login, keine Passwörter im Speicher. ContinueSession liest die provider_id aus dem Token; du musst sie nicht erneut angeben.
Logout¶
Beendet die Session serverseitig und macht das Refresh-Token unbrauchbar. Danach ist nur noch ein Login mit Credentials möglich — lösch das gespeicherte Token bei dieser Gelegenheit mit.
Timeouts¶
Der Client-Default-Timeout liegt bei 30 Sekunden. Für Aufrufe, die serverseitig an DiWaSS weitergereicht werden, ist das zu knapp; setz die Zeitgrenze pro Aufruf:
await client.DiwassApi.AttachmentApi.GetAttachment(attachmentIdentifier,
ZedalCallContext.Default.WithTimeout(600));
ZedalCallContext steuert Deadline, CancellationToken und zusätzliche Header. WithoutDeadline() schaltet die Zeitgrenze ganz ab — sinnvoll nur zusammen mit einem CancellationToken. Eine gerissene Deadline kommt als RpcException mit StatusCode.DeadlineExceeded zurück und sieht aus wie ein Serverfehler, ist aber keiner. Konkrete Richtwerte je Vorgang stehen bei den DiWaSS-Anleitungen.
Wie es weitergeht¶
- DiWaSS-Anleitungen — grenzüberschreitende Abfallverbringung: Notifizierung, Begleitformulare, Behördensicht
- REST / JSON — Aufrufform, Header-Durchreichung, Streaming-Grenzen, Codegenerierung
- Feldregeln — Formate und Wertebereiche, die das Schema nicht ausdrückt
- Fehlercodes — was eine fachliche Ablehnung bedeutet und was zu tun ist
- Codelisten — zulässige Werte für kontrollierte Felder