Zum Hauptinhalt springen

Übersicht und Bearbeitung

Der Kernablauf jeder Integration: Fälle des Mandanten auflisten oder als Delta abholen, einen Fall vollständig lesen, Felder zurückschreiben und die Regulierungswerte pflegen. Alles unter /api/external/v1.

Ziel

Du hältst dein Fremdsystem mit der Plattform synchron: neue und geänderte Fälle abholen, deine eigenen Daten (Aktenzeichen, Beträge, Adressen) zurückschreiben — ohne dir dabei Änderungen anderer zu überschreiben.

Voraussetzungen

  • Scopes: cases:read zum Lesen, cases:write zum Schreiben. Sie implizieren einander nicht.
  • Sichtbarkeit: doppelt begrenzt — auf die Standorte der Person hinter dem Grant und auf den Mandanten des Keys. Ein Fall gehört zum Mandanten, wenn sein Standort im Teilbaum liegt (Werkstatt-Sicht, z. B. Key auf musterhaus), sein Fallabwickler (caseHandler) dort liegt (Fallabwickler-Sicht, z. B. Key auf musterhandler — der sieht die ihm zugewiesenen Fälle, egal in welcher Werkstatt sie liegen; Fälle in Eigenbearbeitung nie) oder sein Gutachterbüro (expertOfficeCode) dort liegt (Gutachter-Sicht). Alles andere ist 404, nie 403.
  • Status: Schreiben nur auf editierbaren Fällen (WORK_IN_PROGRESS, RELEASED, HANDED_OVER_APPRAISER); siehe Status-Lebenszyklus.
  • Optimistic Lock: Für PUT /cases/{caseId} brauchst du das zuletzt gelesene lastModifiedDate des Falls.

1. Fälle auflisten

GET /api/external/v1/cases (Scope cases:read)

Ein GET, weil es ein Lesezugriff ist — die internen Overview-Aufrufe der Oberfläche (POST mit Filterliste) haben hier kein Gegenstück. Der Mandantenfilter wird nicht übergeben, sondern serverseitig aus dem Key gesetzt: ein Werkstatt-Key (musterhaus) filtert auf den Fall-Standort im Teilbaum, ein Fallabwickler-Key (musterhandler) auf die ihm zugewiesenen Fälle (caseHandler) — Fälle in Eigenbearbeitung tauchen dort nie auf —, ein Gutachter-Key auf die dem Büro zugewiesenen Fälle (expertOfficeCode).

ParameterDefaultBedeutung
page00-basierter Seitenindex
size50Seitengröße, maximal 200
sortLAST_MODIFICATION_DATELAST_MODIFICATION_DATE, CREATION_DATE oder RELEASE_DATE (Groß-/Kleinschreibung egal; unbekannte Werte fallen still auf CREATION_DATE zurück)
directionDESCASC oder DESC (unbekannte Werte fallen still auf DESC zurück)
changedSinceISO-8601-Zeitstempel ohne Zone; schaltet in den Delta-Modus (siehe unten)

Antwort:

{
"items": [ { "id": "5f4d…", "status": "RELEASED", "opened": "2026-07-02T10:12:00", "…": "…" } ],
"totalItems": 138,
"page": 0,
"size": 50,
"totalPages": 3,
"delta": false
}

Jeder Eintrag ist eine kompakte Projektion des Falls:

FeldInhalt
idFall-UUID — der Schlüssel für alle weiteren Aufrufe
statusaktueller CaseStatus
openedAnlagezeitpunkt
updatedÄnderungszeitpunkt, abgeleitet aus der Fallhistorie (Fallback: lastModifiedDate)
released, closedDateFreigabe- bzw. Abschlusszeitpunkt, sonst null
lastName, companyNachname bzw. Firma der geschädigten Partei (leerer String, wenn nicht gesetzt)
vehicleLicensePlateKennzeichen des beschädigten Fahrzeugs
damageType, repairWanted, accidentDate, accidentNationalSchadenart, Reparaturwunsch, Unfalldatum, Auslandschaden
locationStandort des Falls (locationCode, name, partner, type, …)
tagsnicht versteckte Fall-Tags
processingTypeHANDLER_PROCESSING oder OWN_PROCESSING
caseHandlerStandortcode des zuständigen Fallabwicklers; null, wenn keiner zugewiesen ist

Zwei Eigenheiten, die du kennen solltest:

  • sort=RELEASE_DATE verkleinert die Treffermenge: Fälle ohne Freigabedatum fallen heraus. totalItems ändert sich dadurch mit.
  • Zeilen, die weder über Standort noch über Fallabwickler noch über Gutachterbüro zum Mandanten gehören, werden verworfen (fail closed). Die Summe der gelieferten items über alle Seiten kann deshalb minimal kleiner sein als totalItems.

2. Delta-Abholung (changedSince)

GET /api/external/v1/cases?changedSince=2026-07-20T06:00:00

Mit changedSince liefert der Endpunkt unpaginiert alle Fälle des Mandanten, deren lastModifiedDate am oder nach dem Zeitstempel liegt — älteste Änderung zuerst. page, size, sort und direction werden dann ignoriert; in der Antwort ist delta = true, page/size/totalPages sind null und totalItems ist die Zeilenzahl dieses Batches.

Die Grenze ist inklusiv. Verwende deshalb als neuen Cursor den größten updated-Wert des Batches und akzeptiere, dass der jüngste Fall im nächsten Lauf noch einmal auftaucht — dein Verarbeitungsschritt sollte idempotent sein. Ein zu weit vorgeschobener Cursor verliert dagegen Änderungen dauerhaft.

Weil das Ergebnis unpaginiert ist: halte den Takt eng (Minuten bis wenige Stunden), sonst wächst der Batch unbegrenzt. Für den Erstabgleich nimm den Seitenmodus mit sort=CREATION_DATE&direction=ASC.

Die vollständige Schleife inklusive Cursor-Handling steht im Integrator-Kochbuch.

3. Einen Fall lesen

GET /api/external/v1/cases/{caseId} (Scope cases:read)

Liefert das komplette CaseDTO — dieselbe Struktur, die auch die Oberfläche bekommt: Kopfdaten (id, status, locationId, caseHandler, fileSign, customerReference, damageType, processingType), die Adressblöcke injuredParty, opponentOwnerAddress, opponentDriverAddress, witnessAddress, die Fahrzeug- und Versicherungsfelder (vehicleLicensePlate, vehicleId, vehicleInsuranceId, opponentInsuranceId, opponentClaimNumber, legalInsuranceId, …), die Unfalldaten (accidentDate, accidentPlace, accidentCourse, policeAlerted, …), die Gutachterfelder (expertOffice, expertOfficeCode, expertEmployee, expertReference) sowie additionalInfos, tags, releaseDate, closedDate und die Audit-Felder createdBy, creationDate, lastModifiedBy, lastModifiedDate.

lastModifiedDate ist deine Lock-Baseline — hebe sie zusammen mit dem Fall auf.

Unbekannte UUID, fremder Mandant und fehlende Sichtbarkeit antworten identisch mit 404 NOT_FOUND („Fall nicht gefunden"). Eine syntaktisch ungültige UUID ergibt 400 VALIDATION.

4. Fall aktualisieren

Sicht: alle drei — aber verschieben (die locationId ändern) kann den Fall nur die Werkstatt; Fallabwickler- und Gutachter-Keys schicken den Standort unverändert zurück.

PUT /api/external/v1/cases/{caseId} (Scope cases:write)

Der Body ist das komplette CaseDTO, nicht ein Patch. Das erprobte Muster:

  1. GET /cases/{caseId} — vollständiges Objekt holen.
  2. Nur die Felder ändern, die du besitzt.
  3. Objekt unverändert im Rest zurückschicken.

Regeln, die der Server durchsetzt:

RegelVerstoß
lastModifiedDate muss exakt dem Serverstand entsprechen400 ALREADY_MODIFIED (nicht 409!)
locationId muss gesetzt sein und entweder dem persistierten Standort entsprechen oder im Mandanten des Keys liegen — den Fall verschieben kann nur die Werkstatt (musterhaus, im eigenen Teilbaum); Fallabwickler- und Gutachter-Keys schicken den Werkstatt-Standort unverändert zurück400 VALIDATION bzw. 404 NOT_FOUND
Eine id im Body darf der Pfad-caseId nicht widersprechen (der Pfad gewinnt)400 VALIDATION
status darf nicht vom persistierten Status abweichen400 VALIDATION mit Verweis auf PUT /cases/{caseId}/status
damageType darf nicht vom persistierten Wert abweichen — die Schadenart bestimmt das Fallabwickler-Routing und wird in der Plattform geändert, nicht über die API400 VALIDATION
Fall muss editierbar sein409 CONFLICT
Zielstandort und Bearbeitungsart müssen für die Grant-Person erlaubt sein403 ACCESS_DENIED

Der Statuswechsel ist bewusst ausgesperrt: er hat mit cases:status einen eigenen Scope, und ein still verworfenes status-Feld wäre eine Falle. Schick den Status entweder gar nicht mit oder genau so, wie du ihn gelesen hast.

Die Antwort ist der gespeicherte Fall mit neuem lastModifiedDate — übernimm es sofort als neue Baseline.

Seiteneffekte: Der Server schreibt den Diff in die Fallhistorie und veröffentlicht ein CaseUpdatedEvent. Bei fachlich relevantem Unterschied geht eine CASE_UPDATED-Mail an das Postfach des Fallabwickler-Standorts — unterdrückt bei Eigenbearbeitung, im Entwurfsstatus und je nach Mail-Präferenz des Standorts. Ein Wechsel des Gutachterbüros löst zusätzlich eine Gutachter-Benachrichtigung aus. Schreib also nicht in einer Schleife identische Werte zurück.

Optimistic Lock in der Praxis

lastModifiedDate bewegt sich auch ohne Fall-Update: jeder Historieneintrag — also auch ein Anhang-Upload, ein Kommentar, ein Fall-Tag oder eine Regulierungswert-Änderung — schiebt ihn vor.

Die praktische Konsequenz für deine Integration:

GET /cases/{id} → lastModifiedDate = T0
POST /cases/{id}/attachments → Historie! Serverstand ist jetzt T1
PUT /cases/{id} (mit T0) → 400 ALREADY_MODIFIED

Richtig ist: nach jedem Anhang-, Kommentar- oder Tag-Aufruf den Fall neu lesen (oder zuerst den Fall schreiben und danach die Nebenobjekte). Bei 400 ALREADY_MODIFIED gilt: neu lesen, Änderung erneut anwenden, noch einmal schreiben — nie blind wiederholen.

5. Regulierungswerte

GET /api/external/v1/cases/{caseId}/evaluation-values (Scope cases:read)
PUT /api/external/v1/cases/{caseId}/evaluation-values (Scope cases:write)

Die finanziellen Werte des Falls: issueDate, repairInvoiceNo, repairCostsIssued/repairCostsReceived, rentalInvoiceNo, rentalCostsIssued/rentalCostsReceived, substitutionNoticeDate, advanceRequestDate, depreciationIssued/depreciationReceived, expertFeesIssued/expertFeesReceived, expertInvoiceNo.

  • GET antwortet 404, wenn für den Fall noch keine Werte erfasst sind. Das ist kein Fehler, sondern der Normalfall bei frischen Fällen — behandle es als „leer".
  • PUT ist eine Vollersetzung, kein Patch. Jedes Feld des Bodys wird geschrieben; ausgelassene Felder werden geleert. Lies also erst, ändere, schreib zurück.
  • Die caseId aus dem Pfad gewinnt; eine abweichende caseId im Body ergibt 400 VALIDATION.
  • Der Aufruf stempelt lastSynced am Fall und schreibt die alten/neuen Werte in die Fallhistorie — und bewegt damit lastModifiedDate.
  • Es gibt hier kein Optimistic Locking und kein Status-Gate: auch geschlossene Fälle sind änderbar, und zwei parallele Schreiber überschreiben sich gegenseitig.

Wer darf Regulierungswerte lesen und schreiben?

Es gibt kein eigenes Rollen-Gate — es gelten genau die Sichtbarkeitsregeln des Falls: jede Mandanten-Sicht, die den Fall sieht, kann seine Regulierungswerte mit cases:read lesen und mit cases:write schreiben.

Key-MandantRegulierungswerte
Fallabwicklerlesen + schreiben für alle Fälle, die an ihn routen — der Normalfall, denn die Regulierung liegt beim Fallabwickler
Werkstattlesen + schreiben für Fälle im eigenen Teilbaum; bei Eigenbearbeitung (OWN_PROCESSING) ist das der vorgesehene Weg, denn diese Fälle reguliert die Werkstatt selbst und sie sind für Fallabwickler-Keys unsichtbar
Gutachterbürolesen + schreiben für Fälle, denen das Büro als Gutachter zugewiesen ist

Was kann schiefgehen?

Vollständiger Katalog: Fehlercodes.

HTTPcodeWannWas tun
400VALIDATIONpage negativ, size außerhalb 1–200, ungültige UUID, fehlende locationId, id-/caseId-Konflikt, Status- oder Schadenart-Änderung im Fall-UpdateRequest korrigieren; Statuswechsel über PUT /cases/{caseId}/status
400(kein code)nicht-numerisches page/size oder unparsbares changedSince — das fängt Spring vor dem Controller abParameterformat prüfen (2026-07-20T06:00:00)
400ALREADY_MODIFIEDlastModifiedDate fehlt oder ist veraltetFall neu lesen, Änderung erneut anwenden, erneut schreiben
403ACCESS_DENIEDScope fehlt; oder Zielstandort/Bearbeitungsart für die Grant-Person nicht erlaubtKey-Scopes prüfen; Standort im eigenen Mandanten belassen
404NOT_FOUNDFall unbekannt, fremder Mandant, außerhalb der Sichtbarkeit — oder eine geänderte locationId außerhalb des Mandanten; bei GET …/evaluation-values auch: noch keine Werte erfasstID prüfen; bei Regulierungswerten als „leer" behandeln
409CONFLICTFall ist CLOSED, CANCELLED, HIDDEN oder WAITING_FOR_CUSTOMERStatus prüfen; ggf. erst wiedereröffnen
429RATE_LIMITED / API_RATE_LIMITEDRate-Limit erreichtRetry-After abwarten
500INTERNAL_ERRORunerwarteter ServerfehlererrorId aus der Antwort an den Support geben

Sequenzdiagramm