Fehler, Konventionen & Referenz
Health & Status
| Endpunkt | Auth | Zweck | Response |
|---|---|---|---|
GET /api/v1/health | Bearer | Konnektivität + Key-Gültigkeit prüfen (aktualisiert last_used_at) | { "status": "ok" } |
GET /api/v1/status | öffentlich | Service-Health inkl. DB-Ping, für Monitoring | { "status": "ok", "version": "…", "timestamp": "…" } |
GET /api/v1/openapi.json | öffentlich | Maschinenlesbare 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).
| Code | HTTP | Wo |
|---|---|---|
INVALID_API_KEY | 401 | alle authentifizierten Endpunkte |
VALIDATION_ERROR | 400 | import, imports/list, practice/mappings, setup/db-key … |
INVALID_REQUEST | 400 | JSON nicht parsierbar |
INVALID_CODE | 400 | setup/redeem |
INVALID_HOSTNAME | 400 | setup/redeem |
ALREADY_REDEEMED | 409 | setup/redeem |
CODE_EXPIRED | 410 | setup/redeem |
KEY_ALREADY_SET | 409 | setup/db-key POST (einmalig) |
KEY_NOT_FOUND | 404 | setup/db-key GET |
CONSENT_MISSING | 403 | import Modus follow_up |
CONSENT_WITHDRAWN | 403 | import Modus follow_up |
TOKEN_FOREIGN | 403 | import Modus token (fremde Praxis) |
NOT_FOUND | 404 | import (Patient/Praxis nicht gefunden) |
RATE_LIMIT_EXCEEDED | 429 | alle (mit Retry-After) |
INTERNAL_ERROR | 500 | alle |
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, Statusactive/redeemed/invalidated,POST /setup/redeeminkl. 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, viaredeem/configausliefern;setup/db-keyimplementieren (opaker Blob, einmalig). - Pseudonym-Store:
devicePseudonym(32 Hex,account_id-scoped) als Patientenschlüssel; Mappingpseudonym → clientIdmitmapped_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 beifollow_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.