Anhänge verwalten
Ziel
Du tauschst Dokumente mit der Plattform aus: die Anhänge eines Falls auflisten, einzelne Metadaten lesen, Dateien herunterladen, eigene Dateien hochladen und wieder löschen. Typischer Anwendungsfall einer Kanzlei- oder Werkstatt-Anbindung: Gutachten und Rechnungen abholen, eigene Schriftsätze ablegen.
Voraussetzungen
- Scopes:
attachments:read(Liste, Metadaten, Download),attachments:write(Upload, Löschen). Beide sind als erhöhtes Risiko eingestuft — Anhänge enthalten Vollmachten, Gutachten und Rechnungen. - Sichtbarkeit: Der Fall muss im Mandanten des Keys liegen und für die Grant-Person sichtbar
sein. Bei den
/attachments/{attachmentId}-Pfaden löst der Server zuerst den zugehörigen Fall auf und prüft dessen Standort — erst danach siehst du irgendetwas. - Status: Der Upload verlangt einen editierbaren Fall (
WORK_IN_PROGRESS,RELEASED,HANDED_OVER_APPRAISER). Lesen, Download und Löschen sind nicht status-beschränkt. - Größe: maximal 20 MB pro Datei und Request (globales Multipart-Limit).
1. Anhänge eines Falls auflisten
GET /api/external/v1/cases/{caseId}/attachments (Scope attachments:read)
Antwort ist ein JSON-Array mit den Metadaten:
| Feld | Inhalt |
|---|---|
attachmentId | UUID des Anhangs — Schlüssel für Download und Löschen |
caseId | UUID des Falls |
fileName | Originaldateiname |
tag | fachliche Kategorie (AttachmentTag), Default NONE |
signable | true, solange ein generiertes Dokument noch signiert werden kann |
created | Zeitpunkt des Uploads |
signingStatus | auf der externen Fläche immer null — externe Signaturprozesse haben keine externen Endpunkte |
Eine Dateigröße enthält das DTO nicht; der Inhalt kommt über einen separaten Aufruf.
Der tag ist für dich das wichtigste Feld: er sagt, was ein Dokument fachlich ist. Die Werte listet
das Glossar unter „Anhang-Tag"; die Gates
POA_SIGNED, OWN_PROCESSING_POA_SIGNED und EXPERT_OPINION_ORDER_SIGNED entscheiden über
Freigabe und Gutachter-Übergabe (siehe
Fall freigeben und abschließen).
2. Metadaten eines einzelnen Anhangs
GET /api/external/v1/attachments/{attachmentId} (Scope attachments:read)
Dasselbe DTO wie in der Liste, für genau einen Anhang. Praktisch, wenn du dir nur die
attachmentId gemerkt hast.
3. Datei herunterladen
GET /api/external/v1/attachments/{attachmentId}/content (Scope attachments:read)
Liefert die Bytes als erzwungenen Download:
Content-Disposition: attachment; filename="…"(der Dateiname ist gegen Header-Injection bereinigt),X-Content-Type-Options: nosniff,- Content-Type aus der Dateiendung abgeleitet — SVG und HTML werden immer als
application/octet-streamausgeliefert, damit aktive Inhalte nie inline landen.
curl -sS -o gutachten.pdf \
https://dev.devlodge.site/api/external/v1/attachments/$ATTACHMENT_ID/content \
-H "Authorization: Bearer $USP_API_KEY"
Downloads schreiben keinen Historieneintrag und verändern den Fall nicht.
4. Datei hochladen
POST /api/external/v1/cases/{caseId}/attachments (Scope attachments:write)
Content-Type: multipart/form-data, Part-Name: file
curl -sS -X POST https://dev.devlodge.site/api/external/v1/cases/$CASE_ID/attachments \
-H "Authorization: Bearer $USP_API_KEY" \
-F "file=@schriftsatz.pdf"
Antwort: 201 Created mit dem AttachmentDTO des neuen Anhangs (tag = NONE,
signable = false).
Was der Server prüft und tut:
- Fall existiert, liegt im Mandanten, ist für die Grant-Person sichtbar — sonst 404.
- Fall ist editierbar — sonst 409
CONFLICT. - Datei ist nicht leer und ihr Name führt nicht aus dem Anhang-Verzeichnis heraus — sonst
400
VALIDATION. - Keine Formatbeschränkung: es gibt auf dieser Fläche keine Endungs- oder Content-Type-Allowlist (die existiert nur für Uploads aus dem Kundenportal).
- Es entsteht ein Fallhistorie-Eintrag — der Fall gilt danach als geändert, siehe unten. Eine Mail löst ein Upload nicht aus.
Der Upload ist nicht idempotent: ein Retry nach Timeout kann dieselbe Datei ein zweites Mal anlegen. Prüfe im Zweifel erst die Liste, bevor du erneut hochlädst.
5. Anhang löschen
DELETE /api/external/v1/attachments/{attachmentId} (Scope attachments:write)
Antwort 204 No Content. Es ist ein Hard-Delete: Datenbankzeile und Datei auf der Platte sind weg, es gibt keinen Papierkorb und keine Wiederherstellung. Auch signierte Vollmachten und Gutachtenaufträge sind löschbar — damit kann ein Löschvorgang die Freigabe-Voraussetzung eines Falls zerstören.
Es gibt hier kein Status-Gate: auch Anhänge geschlossener Fälle lassen sich löschen. Ein Historieneintrag wird geschrieben.
Achtung: Optimistic Lock
Jeder Upload und jedes Löschen schreibt einen Fallhistorie-Eintrag und schiebt damit das
lastModifiedDate des Falls vor. Ein danach abgesetztes PUT /cases/{caseId} mit dem alten
Zeitstempel scheitert mit 400 ALREADY_MODIFIED.
Zwei saubere Reihenfolgen:
- erst
PUT /cases/{caseId}, danach die Anhänge — oder - erst die Anhänge, danach
GET /cases/{caseId}und mit dem frischen Zeitstempel schreiben.
Details unter Übersicht und Bearbeitung.
Noch nicht verfügbar
| Fähigkeit | Stand |
|---|---|
| Tag eines Anhangs setzen oder ändern | existiert extern nicht — hochgeladene Dateien bleiben auf NONE |
| Vollmacht/RKÜ, Gutachtenauftrag, Reparaturablaufplan erzeugen | existiert extern nicht |
| Dokumente signieren oder PDF-Formulare ausfüllen | existiert extern nicht |
| Dokument per Mail an die Kund_in senden | existiert extern nicht |
| Externen Signaturprozess starten oder abfragen | existiert extern nicht (signingStatus bleibt null) |
Was kann schiefgehen?
| HTTP | code | Wann | Was tun |
|---|---|---|---|
| 400 | VALIDATION | leere Datei oder Dateiname mit Pfad-Traversal | Datei mit Inhalt und normalem Namen hochladen |
| 403 | ACCESS_DENIED | Key ohne attachments:read bzw. attachments:write | Key-Scopes prüfen |
| 404 | NOT_FOUND | Fall oder Anhang unbekannt, fremder Mandant oder außerhalb der Sichtbarkeit — bewusst ununterscheidbar | IDs prüfen; Liste neu laden (der Anhang kann gelöscht worden sein) |
| 409 | CONFLICT | Upload auf einen nicht editierbaren Fall (CLOSED, CANCELLED, WAITING_FOR_CUSTOMER) | Fallstatus prüfen |
| 413 | (kein code) | Datei größer als das 20-MB-Multipart-Limit | Datei verkleinern oder aufteilen |
| 415 | (kein code) | Request nicht als multipart/form-data gesendet | Content-Type und Part-Namen file prüfen |
| 429 | RATE_LIMITED / API_RATE_LIMITED | Rate-Limit erreicht — Downloads zählen wie jeder andere Request | Retry-After abwarten, Downloads drosseln |
| 500 | INTERNAL_ERROR | IO-Fehler beim Speichern, oder die Datei fehlt im Anhang-Verzeichnis | errorId an den Support geben |