Skip to content

AI translation

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

REST / JSON

The REST API is the access path for all languages without an official SDK. It is available in production — but does not cover the full gRPC feature set. What is missing is described under Limits of the REST path; read that before you commit to REST as your only access path.

Open API reference

The reference at openapi.live.zedal.de is generated directly from the running system and is therefore always up to date: 131 operations in 21 services, arranged in 7 groups (Core, DIWASS, BXA-DIWASS, BXA-BMU, Signer, Notifier, Utility). The OpenAPI document itself (OpenAPI 3.0.3) is available at openapi.live.zedal.de/openapi.yaml and can be loaded directly into code generators such as openapi-generator or NSwag.

Call format

The paths follow the fully qualified gRPC method name:

<HTTP verb> https://openapi.<environment>.zedal.de/<service.FullName>/<method>

Example:

POST /infotech.zedal.bxa.diwass.v1.api.CompanyDocumentApi/CreateSubmission HTTP/1.1
Host: openapi.test.zedal.de
Authorization: Bearer <access-token>
provider_id: 106
Content-Type: application/json

{ "…": "…" }

provider_id is mandatory on every call

The provider_id header belongs on every request — including the login, which does not carry a token yet. If it is missing, the gateway responds with HTTP 501 and a body like {"code":12,"message":"Bad gRPC response. HTTP status code: 404"}. This message mentions neither the header nor the word “provider” and reliably points you in the wrong direction.

You get the same response with a valid provider ID that belongs to a different environment: the IDs differ per environment, and DiscoveryApi/ListProvider returns the cross-environment directory on every host — so an ID listed there is not automatically valid on the host you are currently addressing.

Rule of thumb for troubleshooting: 501 almost always means provider_id, not “operation not implemented”.

The HTTP verb is not always POST. It comes from the google.api.http annotation of the respective proto method; in practice, POST, GET, PATCH, DELETE and PUT occur. Read operations are mostly GET, with the parameters in the query string. Which verb applies to an operation is stated in the reference — do not guess it from the method name.

Resource paths, not resource design

The paths are deliberately RPC-style rather than resource-oriented. There is no /notifications/{id} — the operation is named the way the gRPC method is named. Each REST operation therefore corresponds to exactly one gRPC method. The reverse does not hold: there are gRPC methods without a REST route (see Limits of the REST path).

Authentication

Three stages, identical for REST and gRPC:

sequenceDiagram
    participant App as Your application
    participant Z as ZEDAL

    App->>Z: ListProvider (without token, without provider_id)
    Z-->>App: Provider directory
    App->>Z: IssueRefreshToken (credentials + app token)
    Z-->>App: Refresh token
    App->>Z: RequestAccess (Bearer refresh token)
    Z-->>App: Access token + NEW refresh token
    Note over App,Z: This invalidates the old refresh token
    App->>Z: Business call (Bearer access token)
Step Operation Authorization provider_id
0 — Determine the provider DiscoveryApi/ListProvider — —
1 — Login AuthApi/IssueRefreshToken — required
2 — Get access AuthApi/RequestAccess Bearer refresh token required
3 — Business call every other operation Bearer access token required

DiscoveryApi/ListProvider is the only operation that requires neither a token nor provider_id. It is therefore the natural starting point: query the directory first, then log in with the matching provider ID.

Two credential types are available for login: UsernameCredentials for user-bound applications and ClientIdCredentials for automated processes. Both also require the app token you receive at registration.

Token rotation — every RequestAccess invalidates the old refresh token

Step 2 always returns a new refresh token, and the previous one loses its validity. Your application must persist the new token before it makes the next business call. If you keep using the original token, you are locked out after the first refresh and have to log in again.

For processes running in parallel, this means: one component refreshes, all others use the result. Two simultaneous RequestAccess calls with the same refresh token invalidate each other.

Some operations additionally require a scope (for example usermgt for the administrative operations of the AuthApi). Scopes are tied to the app token and the subscriber assignment and are carried as a claim in the access token — they are not requested per call.

Errors

gRPC status codes are mapped to HTTP status codes; the response body carries the error details. For error handling, distinguish between two levels:

  • Transport level — HTTP status and gRPC status. Tells you whether the call arrived and was permitted.
  • Business level — rejections from DIWASS with their own error codes such as WASTE_CLASSIFICATION_INVALID_CODE. Such an error can be contained in an HTTP 200 response, because the call was technically successful. See Error codes.

Limits of the REST path

Not every gRPC method has a REST route. The routes are created from the google.api.http annotations of the protos — whatever is not annotated there does not exist via REST. Via REST you can store transport documents (DocumentApi/ImportTransportDocument) and list inventory, inbox and templates (QueryApi/ListTransportDocuments, ListInboxDocuments, ListTransportTemplates); an example is in the BMU guide. Missing is a group that is central to integrations:

Missing via REST Consequence
all other Import… operations of the DocumentApi, including ImportTransportDocumentFromInbox and ImportDocumentToFileAreaFromInbox incoming processes cannot be taken over, documents cannot be imported into files
SendDocument, DeliverDocument no explicit sending to a recipient, no granting of permissions
GetTransferDetails, FindFilesToDocument no status of a transfer, no search for files related to a document
CreateNewFile, GrantAccessToFile, MergeFiles file operations of the FileApi
QueryApi/ListFileAreaDocuments, ListFiles documents in the file area and files cannot be listed

A purely REST-based integration cannot take over incoming documents

The inbox import is the delivery path for changes that come from outside — requests for additional information from authorities, decisions, responses to requests for change. Without it, these versions do not appear in your document inventory, no matter how often you reload.

Via REST you can list the inbox (QueryApi/ListInboxDocuments, DocumentApi/ListInbox) and clean it up (SetInboxDocumentsRead, RemoveDocumentsFromInbox), but not take entries over: ImportTransportDocumentFromInbox and ImportDocumentToFileAreaFromInbox have no REST route. An integration without a gRPC part is therefore functionally incomplete: it can create and submit documents and store them as transport documents, but cannot take the responses to them over into the inventory.

For the full scope: the .NET SDK or gRPC directly. More on the delivery path under Calls and synchronization.

Streaming

The REST/JSON path is unary — one request, one response. Server streaming is not available via REST, not even as Server-Sent Events.

If you need streaming, you have two options: native gRPC, or gRPC-Web via the same openapi.* host — there, server streaming responses are delivered incrementally.

Headers

Incoming HTTP headers are passed through to the backend as gRPC metadata, minus the hop-by-hop and framing headers (connection, host, content-length, content-type, te, transfer-encoding, upgrade, proxy-connection, user-agent, accept*, grpc-encoding, grpc-accept-encoding, grpc-timeout).

There is no allowlist and no prefix scheme: if you set a custom header, expect it to reach the backend. This is the intended way to correlate across service boundaries.

Two headers are relevant for every call:

Header When If missing
Authorization: Bearer <token> from step 2 of authentication 401
provider_id: <id> always, including at login 501 with gRPC code 12

Code generation

The OpenAPI document is complete and self-contained — client code generators handle it without any rework:

openapi-generator generate \
  -i https://openapi.live.zedal.de/openapi.yaml \
  -g python \
  -o ./zedal-client

For production builds, pin a downloaded copy of the document instead of generating directly against the URL — otherwise your generated client changes with the next platform release.

Next