DATEYE

Fehler, Konventionen & Referenz

Health & Status

EndpunktAuthZweckResponse
GET /api/v1/healthBearerKonnektivität + Key-Gültigkeit prüfen (aktualisiert last_used_at){ "status": "ok" }
GET /api/v1/statusöffentlichService-Health inkl. DB-Ping, für Monitoring{ "status": "ok", "version": "…", "timestamp": "…" }
GET /api/v1/openapi.jsonöffentlichMaschinenlesbare Spec (OpenAPI 3.0.3), Access-Control-Allow-Origin: *OpenAPI JSON

Fehlercodes (vollständig)

Alle Fehler verwenden { "success": false, "error": { "code", "message" } }; Validierungsfehler ergänzen error.fields: [{ "field": "measurements[0].axialLength", "code": "too_big" }] (Pfad + Zod-Issue-Code, niemals empfangene Werte — Datenschutz).

CodeHTTPWo
INVALID_API_KEY401alle authentifizierten Endpunkte
VALIDATION_ERROR400import, imports/list, practice/mappings, setup/db-key
INVALID_REQUEST400JSON nicht parsierbar
INVALID_CODE400setup/redeem
INVALID_HOSTNAME400setup/redeem
ALREADY_REDEEMED409setup/redeem
CODE_EXPIRED410setup/redeem
KEY_ALREADY_SET409setup/db-key POST (einmalig)
KEY_NOT_FOUND404setup/db-key GET
CONSENT_MISSING403import Modus follow_up
CONSENT_WITHDRAWN403import Modus follow_up
TOKEN_FOREIGN403import Modus token (fremde Praxis)
NOT_FOUND404import (Patient/Praxis nicht gefunden)
RATE_LIMIT_EXCEEDED429alle (mit Retry-After)
INTERNAL_ERROR500alle

Typische Abläufe (Sequenzen)

Onboarding

Portal: Setup-Code generieren (MK-XXXX-XXXX, expires_at, code_hash speichern)
Client → POST /setup/redeem { code }           → { apiKey, host, encryptionKey }
Client → GET  /health            (Bearer)      → { status: ok }
Client → POST /setup/db-key { encryptedDbKey } → { success: true }   (optional, einmalig)

Erster Import → Verknüpfung → Folge-Import

Client → POST /import { mode: first_visit, devicePseudonym, hints, measurements[] }
         → { unmatched: N, skip_pseudonyms, … }        (landet in Pending-Queue)
Portal:  Mitarbeiter verknüpft Pseudonym → Patient      (Mapping + persistentToken angelegt)
Client → GET  /practice/mappings?since=…
         → { mappings: [ { devicePseudonym, mkClientId, revoked:false } ], nextSince }
Client → POST /import { mode: token, persistentToken, measurements[] }
         → { assigned: N, persistent_token }             (direkt zugeordnet)

Widerruf/Ablehnung

Portal:  Patient gelöscht / Einwilligung widerrufen / Pseudonym abgelehnt
Client → GET /practice/mappings?since=…   → Tombstone { revoked:true }  ⇒ Senden stoppen
Client → GET /rejected-clients            → { rejected:[…] }            ⇒ überspringen

Was das Backend bereitstellen muss (Checkliste)

Damit der unveränderte DATEYE-Client gegen das eigene Backend funktioniert:

  • Tenant-Modell: ein „Account” (Praxis) pro Setup-Code / API-Key; alle Daten account_id-scoped.
  • Setup-Codes: Generierung im Portal, Speicherung als SHA-256-Hash, expires_at, Status active/redeemed/invalidated, POST /setup/redeem inkl. IP-Rate-Limit.
  • API-Keys: Generierung beim Redeem (<prefix>_live_ + 64 Hex), Speicherung als SHA-256-Hash + Anzeigepräfix, revoked_at, Bearer-Auth-Middleware (SHA-256-Lookup).
  • encryptionKey: per-Account 32-Byte-Schlüssel generieren, verschlüsselt at rest speichern, via redeem/config ausliefern; setup/db-key implementieren (opaker Blob, einmalig).
  • Pseudonym-Store: devicePseudonym (32 Hex, account_id-scoped) als Patientenschlüssel; Mapping pseudonym → clientId mit mapped_at / Tombstone-Status.
  • POST /import: die 4 Modi, .strict()-Validierung, Batch 1–50, Dedup auf dem Natural Key, Counter-Response inkl. skip_pseudonyms, Einwilligungsprüfung bei follow_up.
  • Pending-Queue + Portal-Verknüpfung (manuell), persistentToken-Ausstellung.
  • GET /practice/mappings (inkrementell, Tombstones, deterministischer Cursor), GET /rejected-clients, GET /imports/list.
  • GET /health (auth), GET /status (öffentlich), Fehler-Envelope + Codes, 120/60s-Rate-Limit, Retry-After.
  • Datenschutz: niemals PII in Fehlermeldungen ausgeben; das Pseudonym-Modell strikt einhalten (keine PII über die API).

Versionierung & Stabilität

Alle Endpunkte tragen das Präfix /api/v1/. Innerhalb von v1 sind Änderungen additiv und rückwärtskompatibel: Neue optionale Felder können in Antworten erscheinen, neue optionale Request-Felder können akzeptiert werden, und neue Fehlercodes können hinzukommen. Der Client soll unbekannte Felder ignorieren. Breaking Changes (entfernte Felder, geänderte Semantik, inkompatible Request-Formate) werden ausschließlich unter einem neuen Major-Version-Präfix eingeführt (/api/v2) und vorab angekündigt. Deprecations werden im Changelog kommuniziert und, wo möglich, vor der Entfernung per Response-Header signalisiert.

Sicherheit & Datenschutz

  • Nur TLS: Alle Endpunkte sind ausschließlich per HTTPS erreichbar; unverschlüsseltes HTTP wird abgelehnt.
  • Privacy by Design: Der Client überträgt niemals Klartext-PII — nur Pseudonyme (stabile HMAC-Hashes), grobe Hinweise (Initialen, Geburtsmonat/-jahr) und biometrische Messwerte. Der Server kann einen Patienten aus den empfangenen Daten nicht re-identifizieren. Dieses Design entspricht dem Grundsatz der Datensparsamkeit nach DSGVO und ist besonders relevant für Gesundheitsdaten gemäß Art. 9 DSGVO.
  • API-Key-Speicherung: Keys werden ausschließlich als SHA-256-Hash gespeichert; der Klartext wird einmalig bei der Ausstellung angezeigt und ist danach nicht mehr abrufbar.
  • Fehlermeldungen: Validierungsfehler enthalten nur Feldpfade und Fehlercodes — niemals die empfangenen Werte. PII wird in Fehlerantworten grundsätzlich nicht zurückgegeben.

Die vollständige OpenAPI-3.0.3-Definition wird unter GET /api/v1/openapi.json ausgeliefert und ist direkt in Swagger, Postman oder beliebige Codegen-Tools importierbar.

Zur interaktiven API-Referenz