API-Verträge

Demo

Vertragsschicht zwischen neuem Frontend und bestehendem PHP-7.4-Backend. Basis /api/v1. Nur Beschreibung — keine Implementierung, keine Datenbankstruktur, keine echten IDs.

Querschnittsregeln
  • Session ausschliesslich über HttpOnly-Cookie des PHP-Backends; kein Token im Storage oder in der URL.
  • Jede schreibende Anfrage sendet den CSRF-Header X-Benson-Csrf mit dem Wert aus GET /session.
  • Idempotenz über Header Idempotency-Key (UUID v4), Serverfenster 24 h, gleiche Antwort bei Wiederholung.
  • Optimistische Sperre über If-Match/version — Konflikt ergibt 409 statt stillem Überschreiben.
  • Autorisierung findet serverseitig statt; Capabilities steuern im Frontend nur die Sichtbarkeit.
  • Antworten mit personenbezogenen Daten: Cache-Control: no-store.
  • Fehlermeldungen bleiben generisch, keine Stacktraces und keine internen Pfade.
  • Rate Limit je Session und Route; Überschreitung ergibt 429 mit Retry-After.
Einheitliche Hüllstruktur

Erfolg

{ "data": <Nutzlast>, "meta": { "requestId": "req_demo_0001", "seite": 1, "proSeite": 25, "gesamt": 128 }, "error": null }

Fehler

{ "data": null, "meta": { "requestId": "req_demo_0002" }, "error": { "code": "VALIDIERUNG", "message": "Eingabe unvollstaendig", "fields": { "telefon": "ungueltiges Format" } } }

23 von 23 Verträgen

GET/api/v1/session
Session
Aktuelle Identität und Sitzungsstatus

Liefert Identität, Mandant, Arbeitsplatz und frischen CSRF-Wert nach Anmeldung oder Reload.

Request

keine Parameter

Response

  • benutzerId : stringopakes Kürzel, z. B. usr_demo_014
  • anzeigename : string
  • mandantId : string
  • rollen : string[]
  • csrfToken : stringnur im Body, nie in der URL
  • ablaufIso : string (ISO 8601)
  • passwortWechselNoetig : boolean

Fehlerzustände

401 NICHT_ANGEMELDET
429 ZU_VIELE_ANFRAGEN
500 SERVERFEHLER
Capability
session.read
CSRF
nicht erforderlich
Idempotenz
entfällt (lesend)
Audit
kein Eintrag, nur technische Zugriffsmetrik
Datenschutzklasse
personenbezogen
Timeout / Retry
5000 ms — 1 Wiederholung bei Netzfehler, kein Retry bei 401
POST/api/v1/session/logout
Session
Abmelden

Beendet die serverseitige Sitzung und invalidiert das Cookie.

Request

keine Parameter

Response

  • abgemeldet : boolean

Fehlerzustände

401 NICHT_ANGEMELDET
500 SERVERFEHLER
Capability
session.read
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
natürlich idempotent, wiederholter Aufruf liefert erneut abgemeldet=true
Audit
Pflicht: Benutzer, Zeit, Quell-IP-Hash
Datenschutzklasse
intern
Timeout / Retry
5000 ms — keine automatische Wiederholung
GET/api/v1/capabilities
Capabilities
Berechtigungen und Navigationsbaum

Liefert die erlaubten Capabilities und die daraus abgeleitete Navigation für die Shell.

Request

keine Parameter

Response

  • capabilities : string[]Muster modul.objekt.aktion
  • navigation : { schluessel, titel, pfad, gruppe }[]
  • merkmalsschalter : Record<string, boolean>

Fehlerzustände

401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
429 ZU_VIELE_ANFRAGEN
500 SERVERFEHLER
Capability
session.read
CSRF
nicht erforderlich
Idempotenz
entfällt (lesend)
Audit
kein Eintrag
Datenschutzklasse
intern
Timeout / Retry
5000 ms — 1 Wiederholung mit 500 ms Backoff
GET/api/v1/kunden?suche=&status=&seite=&proSeite=
Kunden & Leads
Kunden und Leads suchen

Serverseitig gefilterte, seitenweise Trefferliste für Backoffice und CRM.

Request

  • suche : stringmax. 120 Zeichen, serverseitig escaped
  • status : enumneu | inArbeit | abgeschlossen | gesperrt
  • seite : int>= 1
  • proSeite : int10 | 25 | 50, Standard 25

Response

  • eintraege[].kundeId : string
  • eintraege[].name : string
  • eintraege[].telefonMaskiert : stringz. B. +49 30 •••• 42
  • eintraege[].dncGesperrt : boolean
  • meta.gesamt : int

Fehlerzustände

401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
429 ZU_VIELE_ANFRAGEN
500 SERVERFEHLER
Capability
crm.kunde.read
CSRF
nicht erforderlich
Idempotenz
entfällt (lesend)
Audit
Suchzugriffe auf personenbezogene Daten protokollieren (Benutzer, Filter, Trefferzahl)
Datenschutzklasse
personenbezogen
Timeout / Retry
8000 ms — 1 Wiederholung nur bei Timeout
GET/api/v1/kunden/{kundeId}
Kunden & Leads
Kundenakte lesen

Stammdaten, DNC-Status, Historie und Notizen einer Akte.

Request

  • kundeId : string*opake ID, keine laufende Nummer

Response

  • kunde : Kundenobjekt
  • dnc : { gesperrt, quelle, gueltigBis }
  • historie : Ereignis[]
  • version : stringETag für optimistische Sperre

Fehlerzustände

401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
429 ZU_VIELE_ANFRAGEN
500 SERVERFEHLER
Capability
crm.kunde.read
CSRF
nicht erforderlich
Idempotenz
entfällt (lesend)
Audit
Pflicht: Akteneinsicht mit Benutzer, Kunde, Zeit
Datenschutzklasse
personenbezogen
Timeout / Retry
8000 ms — 1 Wiederholung nur bei Timeout
PATCH/api/v1/kunden/{kundeId}
Kunden & Leads
Kundenstammdaten ändern

Teilaktualisierung der Stammdaten; DNC- und Sperrlogik bleibt im Backend.

Request

  • version : string*aus GET, sonst 409
  • felder : Partial<Kunde>*Whitelist serverseitig

Response

  • kunde : Kundenobjekt
  • version : string

Fehlerzustände

400 UNGUELTIGE_ANFRAGE
401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
409 KONFLIKT
422 VALIDIERUNG
500 SERVERFEHLER
Capability
crm.kunde.update
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
Idempotency-Key + version; Wiederholung liefert denselben Stand
Audit
Pflicht: Feld-Diff ohne Klartext besonders schutzwürdiger Werte
Datenschutzklasse
personenbezogen
Timeout / Retry
10000 ms — kein automatischer Retry nach 4xx; bei Netzfehler Wiederholung mit gleichem Idempotency-Key
POST/api/v1/rueckrufe
Rueckrufe
Rückruf terminieren

Legt einen Rückruftermin an; Zuweisung und Zeitfenster prüft das Backend.

Request

  • kundeId : string*
  • faelligIso : string*Zukunft, max. 180 Tage
  • notiz : stringmax. 500 Zeichen, HTML wird verworfen
  • persoenlich : boolean

Response

  • rueckrufId : string
  • status : enum offen | erledigt | verfallen

Fehlerzustände

400 UNGUELTIGE_ANFRAGE
401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
409 KONFLIKT
422 VALIDIERUNG
500 SERVERFEHLER
Capability
crm.rueckruf.create
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
Idempotency-Key verhindert Doppelanlage bei Doppelklick
Audit
Pflicht
Datenschutzklasse
personenbezogen
Timeout / Retry
10000 ms — 1 Wiederholung mit gleichem Idempotency-Key
GET/api/v1/produkte?kampagneId=
Produkte & Warenkorb
Verfügbare Produkte je Kampagne

Liefert freigegebene Produkte samt Preisen; Preisfindung bleibt Backendhoheit.

Request

  • kampagneId : string*

Response

  • produkte[].produktId : string
  • produkte[].bezeichnung : string
  • produkte[].preisCent : int
  • produkte[].steuersatz : decimal

Fehlerzustände

401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
429 ZU_VIELE_ANFRAGEN
500 SERVERFEHLER
Capability
crm.produkt.read
CSRF
nicht erforderlich
Idempotenz
entfällt (lesend)
Audit
kein Eintrag
Datenschutzklasse
intern
Timeout / Retry
8000 ms — 2 Wiederholungen mit Backoff (idempotent)
POST/api/v1/warenkorb/berechnung
Produkte & Warenkorb
Warenkorb bewerten

Server berechnet Summen, Rabatte und Steuer; das Frontend rechnet nie selbst verbindlich.

Request

  • kundeId : string*
  • positionen[].produktId : string*
  • positionen[].menge : int*1..99

Response

  • positionen : BerechnetePosition[]
  • summeNettoCent : int
  • summeBruttoCent : int
  • berechnungId : stringwird beim Auftrag referenziert

Fehlerzustände

400 UNGUELTIGE_ANFRAGE
401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
409 KONFLIKT
422 VALIDIERUNG
500 SERVERFEHLER
Capability
crm.warenkorb.calc
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
reine Berechnung, wiederholbar; Ergebnis über berechnungId referenzierbar
Audit
kein Eintrag
Datenschutzklasse
intern
Timeout / Retry
8000 ms — 1 Wiederholung bei Timeout
POST/api/v1/auftraege
Auftraege
Auftrag einreichen

Legt einen Auftrag aus einer serverseitig erzeugten Berechnung an.

Request

  • kundeId : string*
  • berechnungId : string*max. 15 Minuten alt
  • dispositionCode : string*
  • einwilligung : { art, zeitpunktIso }*

Response

  • auftragId : string
  • status : enum eingereicht | pruefung | bestaetigt | storniert
  • version : string

Fehlerzustände

400 UNGUELTIGE_ANFRAGE
401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
409 KONFLIKT
422 VALIDIERUNG
500 SERVERFEHLER
Capability
auftrag.create
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
Idempotency-Key zwingend; Doppel-Submit liefert denselben auftragId
Audit
Pflicht: Einreichung, Benutzer, Kampagne, Betrag
Datenschutzklasse
personenbezogen
Timeout / Retry
15000 ms — nur Netzfehler, immer mit identischem Idempotency-Key
PATCH/api/v1/auftraege/{auftragId}/status
Auftraege
Auftragsstatus setzen

Backoffice bestätigt, bemängelt oder storniert einen Auftrag.

Request

  • version : string*
  • neuerStatus : enum*nur erlaubte Übergänge, sonst 409
  • begruendung : stringPflicht bei Storno

Response

  • status : string
  • version : string

Fehlerzustände

400 UNGUELTIGE_ANFRAGE
401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
409 KONFLIKT
422 VALIDIERUNG
500 SERVERFEHLER
Capability
auftrag.verify
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
Idempotency-Key + Statusübergangsprüfung
Audit
Pflicht: alter und neuer Status, Begründung, Benutzer
Datenschutzklasse
personenbezogen
Timeout / Retry
10000 ms — kein automatischer Retry
GET/api/v1/kampagnen
Kampagnen & Listen
Kampagnen und Listen

Kampagnenübersicht mit Listenzuordnung und Aktivstatus.

Request

keine Parameter

Response

  • kampagnen[].kampagneId : string
  • kampagnen[].bezeichnung : string
  • kampagnen[].aktiv : boolean
  • kampagnen[].listen : { listeId, bezeichnung, offen }[]

Fehlerzustände

401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
429 ZU_VIELE_ANFRAGEN
500 SERVERFEHLER
Capability
kampagne.read
CSRF
nicht erforderlich
Idempotenz
entfällt (lesend)
Audit
kein Eintrag
Datenschutzklasse
intern
Timeout / Retry
8000 ms — 2 Wiederholungen mit Backoff
POST/api/v1/listen/{listeId}/import
Kampagnen & Listen
Leadliste importieren

Nimmt eine CSV entgegen; Prüfung, DNC-Abgleich und Dublettenlogik laufen serverseitig.

Request

  • datei : multipart/form-data*nur text/csv, max. 10 MB, Inhaltsprüfung
  • trennzeichen : enum , | ;

Response

  • importId : string
  • status : enum angenommen | verarbeitung | fertig | fehlerhaft
  • zeilenGesamt : int
  • zeilenAbgelehnt : int

Fehlerzustände

400 UNGUELTIGE_ANFRAGE
401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
409 KONFLIKT
422 VALIDIERUNG
500 SERVERFEHLER
429 ZU_VIELE_ANFRAGEN
Capability
kampagne.liste.import
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
Idempotency-Key + Prüfsumme der Datei verhindert Doppelimport
Audit
Pflicht: Dateiname, Prüfsumme, Zeilenbilanz, Benutzer
Datenschutzklasse
personenbezogen
Timeout / Retry
30000 ms — kein automatischer Retry, Status über GET /listen/importe/{importId}
GET/api/v1/mitarbeiter?aktiv=
Mitarbeiter & Zugriffe
Mitarbeiter und Zugriffe

Übersicht über Konten, Rollen und Sperrzustände.

Request

  • aktiv : boolean

Response

  • mitarbeiter[].benutzerId : string
  • mitarbeiter[].anzeigename : string
  • mitarbeiter[].rollen : string[]
  • mitarbeiter[].gesperrt : boolean

Fehlerzustände

401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
429 ZU_VIELE_ANFRAGEN
500 SERVERFEHLER
Capability
admin.mitarbeiter.read
CSRF
nicht erforderlich
Idempotenz
entfällt (lesend)
Audit
Pflicht: Zugriff auf Personalstammdaten
Datenschutzklasse
personenbezogen
Timeout / Retry
8000 ms — 1 Wiederholung bei Timeout
PUT/api/v1/mitarbeiter/{benutzerId}/rollen
Mitarbeiter & Zugriffe
Rollen zuweisen

Setzt die Rollenmenge eines Kontos; Rechte selbst bleiben serverseitig definiert.

Request

  • version : string*
  • rollen : string[]*nur bekannte Rollen, Selbst-Entzug von admin verboten

Response

  • rollen : string[]
  • version : string

Fehlerzustände

400 UNGUELTIGE_ANFRAGE
401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
409 KONFLIKT
422 VALIDIERUNG
500 SERVERFEHLER
Capability
admin.rolle.assign
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
vollständige Ersetzung ist natürlich idempotent, zusätzlich Idempotency-Key
Audit
Pflicht: Rollen vorher/nachher, ausführender Benutzer
Datenschutzklasse
personenbezogen
Timeout / Retry
10000 ms — kein automatischer Retry
GET/api/v1/telefonie/agenten?kampagneId=
Telefonie & Supervisor
Live-Agentenstatus

Aktueller Status aller sichtbaren Agenten; Quelle ist der Backend-VICIdial-Adapter, nie das Frontend.

Request

  • kampagneId : string

Response

  • agenten[].agentId : string
  • agenten[].status : enum bereit | gespraech | nachbearbeitung | pause | offline
  • agenten[].seitSekunden : int
  • agenten[].nummerMaskiert : stringnie vollständige Rufnummer
  • standIso : stringfür Veraltet-Erkennung im Wallboard

Fehlerzustände

401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
429 ZU_VIELE_ANFRAGEN
500 SERVERFEHLER
Capability
monitor.agent.read
CSRF
nicht erforderlich
Idempotenz
entfällt (lesend)
Audit
kein Eintrag
Datenschutzklasse
personenbezogen
Timeout / Retry
4000 ms — Polling alle 5 s; kein Retry-Sturm, Backoff auf 15 s nach 3 Fehlern
POST/api/v1/telefonie/agenten/{agentId}/aktion
Telefonie & Supervisor
Supervisor-Aktion ausführen

Mithören, Flüstern, Aufschalten oder Pause setzen — ausschliesslich über den Backend-Adapter.

Request

  • aktion : enum mithoeren | fluestern | aufschalten | pause | abmelden*
  • begruendung : string*min. 5 Zeichen

Response

  • aktionId : string
  • ergebnis : enum gestartet | abgelehnt

Fehlerzustände

400 UNGUELTIGE_ANFRAGE
401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
409 KONFLIKT
422 VALIDIERUNG
500 SERVERFEHLER
429 ZU_VIELE_ANFRAGEN
Capability
monitor.supervisor.act
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
Idempotency-Key; gleiche Aktion innerhalb 10 s liefert dieselbe aktionId
Audit
Pflicht: Aktion, Ziel-Agent, Begründung, Zeit — revisionssicher
Datenschutzklasse
besonders schutzwuerdig
Timeout / Retry
8000 ms — kein automatischer Retry
GET/api/v1/monitor/kennzahlen?zeitraum=heute
Monitoring
Monitoring-Kennzahlen

Aggregierte Kennzahlen für Wallboard und Dashboard.

Request

  • zeitraum : enum heute | woche | monat

Response

  • gespraeche : int
  • abschluesse : int
  • umsatzCent : int
  • warteschlangen : { name, wartend, laengsteWartezeitSek }[]
  • standIso : string

Fehlerzustände

401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
429 ZU_VIELE_ANFRAGEN
500 SERVERFEHLER
Capability
monitor.kpi.read
CSRF
nicht erforderlich
Idempotenz
entfällt (lesend)
Audit
kein Eintrag
Datenschutzklasse
intern
Timeout / Retry
6000 ms — Polling 10 s, Backoff bei Fehlern, letzter Stand bleibt sichtbar
GET/api/v1/portal/auftraege?status=&seite=
Portal
Portalaufträge des Mandanten

Nur Aufträge des angemeldeten Mandanten; keine internen Kennzahlen oder Agentendaten.

Request

  • status : enum offen | inBearbeitung | abgeschlossen
  • seite : int

Response

  • auftraege[].auftragId : string
  • auftraege[].bezeichnung : string
  • auftraege[].status : string
  • auftraege[].aktualisiertIso : string

Fehlerzustände

401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
429 ZU_VIELE_ANFRAGEN
500 SERVERFEHLER
Capability
portal.auftrag.read
CSRF
nicht erforderlich
Idempotenz
entfällt (lesend)
Audit
Pflicht: Mandant, Benutzer, Filter (Mandantentrennung nachweisbar)
Datenschutzklasse
personenbezogen
Timeout / Retry
8000 ms — 1 Wiederholung bei Timeout
POST/api/v1/portal/dokumente/{dokumentId}/ticket
Portal
Dokument-Download anfordern

Erzeugt ein kurzlebiges Ticket; der Download läuft danach als Stream über das Backend, nie über eine direkte Dateipfad-URL.

Request

  • dokumentId : string*opake ID, Mandantenbindung serverseitig

Response

  • ticket : stringeinmalig, gültig 60 s
  • downloadPfad : string/api/v1/portal/downloads/{ticket}

Fehlerzustände

400 UNGUELTIGE_ANFRAGE
401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
409 KONFLIKT
422 VALIDIERUNG
500 SERVERFEHLER
Capability
portal.dokument.read
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
jedes Ticket ist einmalig; Wiederholung erzeugt neues Ticket
Audit
Pflicht: Dokument, Mandant, Benutzer, Zeit
Datenschutzklasse
personenbezogen
Timeout / Retry
8000 ms — kein automatischer Retry
GET/api/v1/chat/kanaele/{kanalId}/nachrichten?vor=&limit=
Chat
Nachrichten eines Kanals lesen

Seitenweises Nachladen älterer Nachrichten; Kanalmitgliedschaft prüft das Backend.

Request

  • vor : stringCursor, keine laufende ID
  • limit : int1..100, Standard 50

Response

  • nachrichten[].nachrichtId : string
  • nachrichten[].autorId : string
  • nachrichten[].text : stringKlartext, Rendering ohne HTML-Interpretation
  • nachrichten[].anhaenge : { anhangId, art, groesseBytes }[]
  • naechsterCursor : string | null

Fehlerzustände

401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
429 ZU_VIELE_ANFRAGEN
500 SERVERFEHLER
Capability
chat.kanal.read
CSRF
nicht erforderlich
Idempotenz
entfällt (lesend)
Audit
kein Eintrag
Datenschutzklasse
intern
Timeout / Retry
6000 ms — 1 Wiederholung bei Timeout
POST/api/v1/chat/kanaele/{kanalId}/nachrichten
Chat
Nachricht senden

Sendet Text und optional Referenzen auf bereits hochgeladene Anhänge.

Request

  • text : string*1..4000 Zeichen, HTML wird verworfen
  • anhangIds : string[]max. 5, müssen dem Absender gehören

Response

  • nachrichtId : string
  • gesendetIso : string

Fehlerzustände

400 UNGUELTIGE_ANFRAGE
401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
409 KONFLIKT
422 VALIDIERUNG
500 SERVERFEHLER
429 ZU_VIELE_ANFRAGEN
Capability
chat.nachricht.create
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
Idempotency-Key aus Client-Entwurfs-ID verhindert Doppelversand
Audit
kein Inhaltsprotokoll, nur Metadaten bei Moderationsfällen
Datenschutzklasse
intern
Timeout / Retry
8000 ms — 1 Wiederholung mit gleichem Idempotency-Key
POST/api/v1/chat/anhaenge
Chat
Anhang oder Sprachnachricht hochladen

Nimmt Datei oder Audio entgegen; Typprüfung, Grössenlimit und Virenscan serverseitig.

Request

  • datei : multipart/form-data*Whitelist: pdf, png, jpg, webm/opus; max. 25 MB
  • art : enum datei | sprache*
  • dauerSek : intnur bei Sprache, max. 300

Response

  • anhangId : string
  • abrufPfad : stringStream über Backend, kein Dateisystempfad

Fehlerzustände

400 UNGUELTIGE_ANFRAGE
401 NICHT_ANGEMELDET
403 KEINE_BERECHTIGUNG
404 NICHT_GEFUNDEN
409 KONFLIKT
422 VALIDIERUNG
500 SERVERFEHLER
429 ZU_VIELE_ANFRAGEN
Capability
chat.anhang.create
CSRF
erforderlich (X-Benson-Csrf)
Idempotenz
Idempotency-Key + Prüfsumme; identischer Upload liefert dieselbe anhangId
Audit
Pflicht: Uploader, Grösse, Typ, Scanergebnis
Datenschutzklasse
personenbezogen
Timeout / Retry
30000 ms — kein automatischer Retry