Ü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:readzum Lesen,cases:writezum 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 aufmusterhandler— 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 gelesenelastModifiedDatedes 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).
| Parameter | Default | Bedeutung |
|---|---|---|
page | 0 | 0-basierter Seitenindex |
size | 50 | Seitengröße, maximal 200 |
sort | LAST_MODIFICATION_DATE | LAST_MODIFICATION_DATE, CREATION_DATE oder RELEASE_DATE (Groß-/Kleinschreibung egal; unbekannte Werte fallen still auf CREATION_DATE zurück) |
direction | DESC | ASC oder DESC (unbekannte Werte fallen still auf DESC zurück) |
changedSince | — | ISO-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:
| Feld | Inhalt |
|---|---|
id | Fall-UUID — der Schlüssel für alle weiteren Aufrufe |
status | aktueller CaseStatus |
opened | Anlagezeitpunkt |
updated | Änderungszeitpunkt, abgeleitet aus der Fallhistorie (Fallback: lastModifiedDate) |
released, closedDate | Freigabe- bzw. Abschlusszeitpunkt, sonst null |
lastName, company | Nachname bzw. Firma der geschädigten Partei (leerer String, wenn nicht gesetzt) |
vehicleLicensePlate | Kennzeichen des beschädigten Fahrzeugs |
damageType, repairWanted, accidentDate, accidentNational | Schadenart, Reparaturwunsch, Unfalldatum, Auslandschaden |
location | Standort des Falls (locationCode, name, partner, type, …) |
tags | nicht versteckte Fall-Tags |
processingType | HANDLER_PROCESSING oder OWN_PROCESSING |
caseHandler | Standortcode des zuständigen Fallabwicklers; null, wenn keiner zugewiesen ist |
Zwei Eigenheiten, die du kennen solltest:
sort=RELEASE_DATEverkleinert 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 alstotalItems.
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:
GET /cases/{caseId}— vollständiges Objekt holen.- Nur die Felder ändern, die du besitzt.
- Objekt unverändert im Rest zurückschicken.
Regeln, die der Server durchsetzt:
| Regel | Verstoß |
|---|---|
lastModifiedDate muss exakt dem Serverstand entsprechen | 400 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ück | 400 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 abweichen | 400 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 API | 400 VALIDATION |
| Fall muss editierbar sein | 409 CONFLICT |
| Zielstandort und Bearbeitungsart müssen für die Grant-Person erlaubt sein | 403 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.
GETantwortet 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".PUTist eine Vollersetzung, kein Patch. Jedes Feld des Bodys wird geschrieben; ausgelassene Felder werden geleert. Lies also erst, ändere, schreib zurück.- Die
caseIdaus dem Pfad gewinnt; eine abweichendecaseIdim Body ergibt 400VALIDATION. - Der Aufruf stempelt
lastSyncedam Fall und schreibt die alten/neuen Werte in die Fallhistorie — und bewegt damitlastModifiedDate. - 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-Mandant | Regulierungswerte |
|---|---|
| Fallabwickler | lesen + schreiben für alle Fälle, die an ihn routen — der Normalfall, denn die Regulierung liegt beim Fallabwickler |
| Werkstatt | lesen + 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üro | lesen + schreiben für Fälle, denen das Büro als Gutachter zugewiesen ist |
Was kann schiefgehen?
Vollständiger Katalog: Fehlercodes.
| HTTP | code | Wann | Was tun |
|---|---|---|---|
| 400 | VALIDATION | page negativ, size außerhalb 1–200, ungültige UUID, fehlende locationId, id-/caseId-Konflikt, Status- oder Schadenart-Änderung im Fall-Update | Request korrigieren; Statuswechsel über PUT /cases/{caseId}/status |
| 400 | (kein code) | nicht-numerisches page/size oder unparsbares changedSince — das fängt Spring vor dem Controller ab | Parameterformat prüfen (2026-07-20T06:00:00) |
| 400 | ALREADY_MODIFIED | lastModifiedDate fehlt oder ist veraltet | Fall neu lesen, Änderung erneut anwenden, erneut schreiben |
| 403 | ACCESS_DENIED | Scope fehlt; oder Zielstandort/Bearbeitungsart für die Grant-Person nicht erlaubt | Key-Scopes prüfen; Standort im eigenen Mandanten belassen |
| 404 | NOT_FOUND | Fall unbekannt, fremder Mandant, außerhalb der Sichtbarkeit — oder eine geänderte locationId außerhalb des Mandanten; bei GET …/evaluation-values auch: noch keine Werte erfasst | ID prüfen; bei Regulierungswerten als „leer" behandeln |
| 409 | CONFLICT | Fall ist CLOSED, CANCELLED, HIDDEN oder WAITING_FOR_CUSTOMER | Status prüfen; ggf. erst wiedereröffnen |
| 429 | RATE_LIMITED / API_RATE_LIMITED | Rate-Limit erreicht | Retry-After abwarten |
| 500 | INTERNAL_ERROR | unerwarteter Serverfehler | errorId aus der Antwort an den Support geben |