Empfänger finden
TL;DR – Empfänger und Adressierung stehen in FIT-Connect nie unabhängig voneinander:
„die zuständige Stelle finden" und „wie adressiere ich einen Antrag an sie" ist dieselbe Suche.
Beide SDKs bündeln das im Directory — Java in sdk.directory(), .NET in client.Directory.
Java liefert direkt einen fertig adressierten Participant; .NET liefert Route-Objekte, deren
DestinationId in den Builder wandert.
Das Verzeichnis auf einen Blick
- Java
- .NET (C#)
sdk.directory() ist der eine Einstiegspunkt für „wer ist zuständig, und wie adressiere ich
ihn" — vier Suchen, alle unauthentifiziert:
| Methode | Ergebnis | Wofür |
|---|---|---|
forService(leikaKey, name, ars) | ein fertiger Participant | Regelfall: Verwaltungsleistung in einem Gebiet — direkt an OutgoingSubmission.to(...) weiterreichbar |
allForService(leikaKey, name, ars) | List<Participant> | mehrere zuständige Stellen zulassen/anzeigen |
forProcessMessage(processModelId, messageId, region) | ein fertiger Participant | Prozesskommunikation statt Verwaltungsleistung |
activeEncryptionKeyOf(destinationId) | EncryptionKey | nur der öffentliche Schlüssel, ohne vollen Participant (siehe Submission senden) |
routing() | Routing-Zugriff | Rohdaten des Routingdienstes — Name, Adresse, Kontaktperson, Bearbeitungsdauer u. a. |
Die ersten vier geben Ihnen bereits alles, was ein Antrag zur Adressierung braucht — kein
separater Schritt „erst destinationId ermitteln, dann Participant bauen" nötig. Nur wer mehr
als die reine Adressierung braucht (Kontaktdaten, Bearbeitungsfristen, …), greift zusätzlich auf
routing() zu — siehe weiter unten.
client.Directory (IDirectoryService) ist der eine Einstiegspunkt für „wer ist zuständig“ —
vier Suchen, alle unauthentifiziert:
| Methode | Ergebnis | Wofür |
|---|---|---|
FindByService(leikaKey, AreaFilter) | IReadOnlyList<Route> | Regelfall: Verwaltungsleistung in einem Gebiet; Route.DestinationId geht in WithDestinationId(...) |
FindByProcessMessage(processModelId, messageId, region) | MultipleDestinations? | Prozesskommunikation statt Verwaltungsleistung — liefert vollständige Destination-Objekte |
FindAreas(filter) | AreaList? | Gebiets-IDs über Namen oder PLZ ermitteln |
GetActiveEncryptionKey(destinationId) | ApiJwk | nur der öffentliche Schlüssel, z. B. für Vorverschlüsselung (siehe Antrag senden) |
FindByService und FindByProcessMessage liefern bewusst verschiedene Typen (Route vs.
Destination), weil sie verschiedene APIs befragen. Die Adressierung (WithServiceType) setzen
Sie anschließend im Builder — Leistung und Ziel gehören dort zusammen.
Begriffsklärung
| Begriff | Bedeutung |
|---|---|
| LeiKa-Schlüssel | Identifiziert eine Verwaltungsleistung, z. B. 99400048079000. Siehe FIM-Portal. |
| ARS | Amtlicher Regionalschlüssel, 12-stellig. Nachfolger des AGS. Siehe Details/ARS. |
| AGS | Amtlicher Gemeindeschlüssel, 8-stellig. |
| AreaId | Interne Routing-ID. Ermitteln über Stadtname, PLZ etc. |
Sie brauchen pro Suche:
- den LeiKa-Schlüssel der Leistung
- genau ein Gebietskriterium:
ars,agsoderareaId
Empfänger in einem Schritt finden
- Java
- .NET (C#)
Der Regelfall: LeiKa-Schlüssel plus Gebietskriterium hinein, fertig adressierter Participant
heraus — sofort einsetzbar beim Senden:
Participant amt = sdk.directory().forService(
"99400048079000", // LeiKa-Schlüssel
"Anmeldung", // Name für die Adressierung
"147130000000"); // ARS, hier: Leipzig
OutgoingSubmission submission = OutgoingSubmission.to(amt)
.setData(SubmissionData.json(jsonData, schemaUri))
.build();
Gibt es mehr als eine zuständige Stelle, wirft forService(...) eine IllegalStateException —
in dem Fall alle Kandidaten holen und selbst auswählen (lassen):
List<Participant> kandidaten = sdk.directory().allForService(
"99400048079000", "Anmeldung", "147130000000");
Haben Sie bereits ein PublicService-Objekt vorliegen (z. B. aus den öffentlichen Daten einer
Destination), spart die Überladung forService(PublicService, ars) das erneute Zerlegen in
LeiKa-Schlüssel und Name.
IReadOnlyList<Route> routes = await client.Directory.FindByService(
"99400048079000", // LeiKa-Schlüssel
AreaFilter.Ars("147130000000")); // genau ein Gebietsfilter, hier: ARS Leipzig
Guid destinationId = routes.Single().DestinationId; // bei mehreren Treffern: eigene Auswahl nötig
OutgoingSubmission submission = OutgoingSubmissionBuilder.Builder()
.WithDestinationId(destinationId)
.WithServiceType("99400048079000", "Anmeldung")
.WithMetadataVersion(new Version(1, 5, 0))
.WithJsonData(jsonData, schemaUri)
.Build();
AreaFilter.Ags(…), AreaFilter.Ars(…) oder AreaFilter.Area(…) — nie zwei gleichzeitig. Der
Typ erzwingt das, statt es zur Laufzeit mit einer FitConnectRoutingException zu quittieren.
Prozessnachrichten adressieren
Statt einer Verwaltungsleistung können Sie auch einen Schritt eines Prozesses adressieren — z. B. eine Rückfrage innerhalb eines laufenden Vorgangs, für die es keinen eigenen LeiKa-Schlüssel gibt (prozessbasierte Kommunikation, API v3):
- Java
- .NET (C#)
Participant fach = sdk.directory().forProcessMessage(
processModelId, messageId, "DE147130000000"); // region: ARS mit "DE"-Präfix
List<Participant> kandidaten =
sdk.directory().allForProcessMessage(processModelId, messageId, region);
Details zum Adressierungs-Objekt: Spickzettel (Java) · Addressing.
MultipleDestinations? result = await client.Directory.FindByProcessMessage(
processModelId, messageId, "DE147130000000"); // region: ARS mit "DE"-Präfix
Guid destinationId = result!.Destinations.Single().DestinationId;
OutgoingSubmission submission = OutgoingSubmissionBuilder.Builder()
.WithDestinationId(destinationId)
.WithProcessMessage(processModelId, messageId) // statt WithServiceType
.WithMetadataVersion(new Version(1, 5, 0))
.WithJsonData(jsonData, schemaUri)
.Build();
Kommen mehrere Kandidaten zurück, entscheidet meist der Prozess selbst, welcher gemeint ist —
üblicherweise hat der Absender einer früheren Nachricht auf dem Case das bereits im
process-Datensatz angegeben.
Wenn Sie mehr als die ID brauchen: rohe Routing-Daten
forService(...) liefert nur, was zum Adressieren nötig ist. Brauchen Sie zusätzlich
Destinationsname, Adresse, Kontaktperson oder Bearbeitungsdauer, greifen Sie auf die rohen
Antworten des Routingdienstes zu:
- Java
- .NET (C#)
Routing routing = sdk.directory().routing();
DestinationSearch search = DestinationSearch.withArs(
"99400048079000", // LeiKa-Schlüssel
"147130000000"); // Beispiel: ARS für Leipzig
List<Route> routes = routing.routes(search);
for (Route r : routes) {
System.out.printf("→ destinationId=%s, destinationName=%s%n",
r.destinationId(), r.destinationName());
}
IReadOnlyList<Route> routes = await client.Directory.FindByService(
"99400048079000",
AreaFilter.Ars("147130000000"), // Beispiel: ARS für Leipzig
offset: 0,
limit: 25);
foreach (Route r in routes)
{
Console.WriteLine($"→ destinationId={r.DestinationId}, destinationName={r.DestinationName}");
}
Route trägt weniger Felder als in JavaRoute liefert aktuell DestinationId, DestinationSignature und DestinationName — anders als
das Java-Route-Objekt (das zusätzlich Adresse, Kontaktpersonen, Fristen u. a. trägt). Für Details
zum Zustellpunkt rufen Sie zusätzlich client.DestinationApiService.GetDestination(r.DestinationId)
auf.
Es können mehrere zuständige Zustellpunkte für dieselbe Kombination aus Leistungsschlüssel und Gebiet existieren – z. B. wenn mehrere Zustelldienste parallel betrieben werden. Wählen Sie in dem Fall den passenden Eintrag anhand zusätzlicher Metadaten.
Variante: AreaId statt ARS/AGS verwenden
Wenn Ihnen weder ARS noch AGS vorliegt, sondern z. B. nur ein Stadtname oder eine PLZ,
ermitteln Sie zunächst die areaId:
- Java
- .NET (C#)
AreaResult areas = sdk.directory().routing().areas(
List.of("Leip*", "04229"), // Wildcards möglich
/* offset */ 0, /* limit */ 5);
for (Area a : areas.areas()) {
System.out.printf("→ %s (areaId=%s)%n", a.name(), a.id());
}
forService/allForService erwarten ars, nicht areaIdNur die rohe routing().routes(...)-Suche akzeptiert wahlweise ARS, AGS oder AreaId
(DestinationSearch.withAreaId(...)). Der direkte Weg über forService(...) ist auf ARS
festgelegt. Haben Sie nur eine areaId, nutzen Sie die rohe Suche und bauen den Participant
anschließend selbst:
DestinationSearch search = DestinationSearch.withAreaId(
"99400048079000",
"48566"); // Beispiel: areaId für Leipzig
List<Route> routes = sdk.directory().routing().routes(search);
Participant amt = Participant.of(
routes.get(0).destinationId(),
Addressing.toService("99400048079000", "Anmeldung"));
AreaList? areas = await client.Directory.FindAreas(["Leip*", "04229"], offset: 0, limit: 5);
foreach (Area a in areas!.Areas)
{
Console.WriteLine($"→ {a.Name} (areaId={a.Id})");
}
IReadOnlyList<Route> routes = await client.Directory.FindByService(
"99400048079000",
AreaFilter.Area("48566")); // AreaId statt ARS/AGS
LeiKa-Schlüssel und Regionalschlüssel sind nicht über die FIT-Connect-Routing-API abrufbar. Quellen für die initiale Recherche:
- fimportal.de – Katalog der Leistungs-IDs
- opengovtech.de/ars – Lookup für Regionalschlüssel
Was tun mit dem Ergebnis?
Java, direkter Weg (forService/forProcessMessage): Das Ergebnis ist bereits ein fertiger
Participant — direkt an OutgoingSubmission.to(...) übergeben, kein weiterer Bauschritt nötig.
Java, rohe Routing-Daten: Den ermittelten destinationId übergeben Sie beim Bau einer
Submission an Participant.of(destinationId, addressing), mit addressing aus
Addressing.toService(...) bzw. Addressing.toProcessMessage(...).
.NET: Die DestinationId der gewählten Route übergeben Sie beim Bau der Submission an
WithDestinationId(...), die Leistung an WithServiceType(...) (bzw. WithProcessMessage(...)).
Mehrere Treffer können Sie ggf. Bürger:innen zur Auswahl anzeigen, falls die Routing-Logik in Ihrer Anwendung das zulässt.
Häufige Stolperfallen
- Unnötig auf die rohen Routing-Daten zurückgreifen (Java): Wer nur adressieren will, braucht
routing().routes(...)+ manuellesParticipant.of(...)nicht —forService(...)erledigt beides in einem Aufruf. - Kein Treffer: Häufig liegt das an einem zu eng gewählten Gebiet. ARS oder AGS müssen exakt stimmen (auch führende Nullen!). Versuchen Sie testweise eine breitere Gebietssuche.
- „Bekannte"
destinationIdschlägt fehl: Zustellpunkte können in Status inactive oder decommissioned überführt werden. Das Routing liefert nur Zustellpunkte im Status active zurück. - Falscher Leistungsschlüssel: Tippfehler im LeiKa-Schlüssel führen zu leerem Resultset. Validieren Sie über fimportal.de.
- STAGE-Umgebung: In STAGE gibt es keine dedizierte Routing-API – sie verweist auf die PROD-Routing-Endpoints. Destinations müssen Sie in STAGE direkt kennen.