{"openapi":"3.1.0","info":{"title":"Reditor – Lizenz-API","version":"1.0.0","description":"API für die Windows-Anwendung. Alle Antworten haben das Format `{ ok, data }` bzw. `{ ok: false, error: { code, message, details? } }`.\n\n**Authentifizierung:** `POST /licenses/activate` benötigt keine Authentifizierung – der Lizenzschlüssel im Body ist das Geheimnis. Die Antwort enthält ein Aktivierungs-Credential (`<activationId>.<secret>`), das der Client sicher speichert (z. B. Electron safeStorage/DPAPI) und für alle weiteren Aufrufe als `Authorization: Bearer <credential>` sendet. Der Einmal-Token wird danach nie wieder übertragen.\n\n**Lizenznachweis:** `proof` ist eine kompakte JWS (alg EdDSA/Ed25519). Der Client prüft sie lokal mit den Schlüsseln aus `GET /public/license-keys`, vergleicht `dev` mit SHA-256(installationId) (base64url) und akzeptiert sie bis `exp` (Offline-Toleranz, Standard 7 Tage). Eine serverseitige Sperrung erkennt ein Offline-Client erst bei der nächsten Synchronisierung bzw. spätestens mit Ablauf des Nachweises.\n\nLizenzschlüssel werden nie in URLs übertragen. Rate-Limits antworten mit HTTP 429 und `Retry-After`."},"servers":[{"url":"/api/v1"}],"components":{"securitySchemes":{"activationCredential":{"type":"http","scheme":"bearer","bearerFormat":"<activationId>.<secret>","description":"Aus POST /licenses/activate."}},"schemas":{"Error":{"type":"object","required":["ok","error"],"properties":{"ok":{"const":false},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","INVALID_JSON","INVALID_ACTIVATION","INVALID_TOKEN","TOKEN_ALREADY_REDEEMED","TOKEN_DISABLED","TOKEN_EXPIRED","LICENSE_EXPIRED","LICENSE_REVOKED","LICENSE_SUSPENDED","DEVICE_LIMIT_REACHED","CONFLICT","NOT_FOUND","PAYLOAD_TOO_LARGE","RATE_LIMITED","INTERNAL_ERROR"]},"message":{"type":"string"},"details":{}}}}},"License":{"type":"object","properties":{"licenseId":{"type":"string","format":"uuid"},"activationId":{"type":"string","format":"uuid"},"plan":{"type":"string","examples":["PRO_YEARLY"]},"planName":{"type":"string"},"status":{"type":"string","enum":["ACTIVE","EXPIRED","REVOKED","SUSPENDED"]},"features":{"type":"array","items":{"type":"string"}},"maxDevices":{"type":"integer"},"activeDevices":{"type":"integer"},"licenseExpiresAt":{"type":["string","null"],"format":"date-time"}}},"ActivateRequest":{"type":"object","required":["token","installationId"],"additionalProperties":false,"properties":{"token":{"type":"string","description":"Lizenzschlüssel, Format SRP-XXXX-…","minLength":20,"maxLength":64},"installationId":{"type":"string","pattern":"^[A-Za-z0-9_-]{16,128}$","description":"Zufällige, pseudonyme ID der Installation (kein Hardware-Fingerprint)."},"deviceName":{"type":"string","maxLength":80},"clientVersion":{"type":"string","examples":["1.0.0"]}}},"ActivateResponse":{"type":"object","properties":{"createdLicense":{"type":"boolean"},"activation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"credential":{"type":"string","description":"Nur einmal sichtbar – sicher speichern."}}},"license":{"$ref":"#/components/schemas/License"},"proof":{"type":"string","description":"Signierter Lizenznachweis (JWS, EdDSA)."},"proofExpiresAt":{"type":"string","format":"date-time"},"refreshAfter":{"type":"string","format":"date-time"},"offlineDays":{"type":"integer"}}},"RefreshRequest":{"type":"object","required":["installationId"],"additionalProperties":false,"properties":{"installationId":{"type":"string"},"clientVersion":{"type":"string"}}},"RefreshResponse":{"type":"object","properties":{"license":{"$ref":"#/components/schemas/License"},"proof":{"type":"string"},"proofExpiresAt":{"type":"string","format":"date-time"},"refreshAfter":{"type":"string","format":"date-time"},"offlineDays":{"type":"integer"}}},"Features":{"type":"object","properties":{"source":{"type":"string","enum":["free","license"]},"licenseStatus":{"type":"string"},"plan":{"type":"string"},"planName":{"type":"string"},"features":{"type":"array","items":{"type":"string"}},"catalog":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"category":{"type":"string"},"enabled":{"type":"boolean"},"minClientVersion":{"type":["string","null"]}}}}}},"PublicKeys":{"type":"object","properties":{"algorithm":{"type":"string"},"currentKid":{"type":"string"},"offlineDays":{"type":"integer"},"keys":{"type":"array","items":{"type":"object","properties":{"kid":{"type":"string"},"status":{"type":"string","enum":["current","previous"]},"publicKeyPem":{"type":"string"},"jwk":{"type":"object"}}}},"jwks":{"type":"object"}}},"Release":{"type":"object","properties":{"id":{"type":"string"},"version":{"type":"string"},"title":{"type":"string"},"changelog":{"type":"string"},"compatibility":{"type":"string","enum":["FREE_AND_PRO","PRO_ONLY","FREE_ONLY"]},"fileName":{"type":["string","null"]},"sizeBytes":{"type":["integer","null"]},"sha256":{"type":["string","null"]},"publishedAt":{"type":["string","null"],"format":"date-time"},"downloadUrl":{"type":["string","null"]}}}}},"paths":{"/licenses/activate":{"post":{"summary":"Lizenzschlüssel einlösen und Gerät aktivieren","description":"Erste Einlösung erzeugt eine Lizenz. Ein bereits eingelöster Schlüssel erzeugt NIE eine weitere Lizenz, kann aber im Rahmen des Gerätelimits weitere Geräte derselben Lizenz aktivieren. Dasselbe Gerät erneut aktivieren rotiert nur das Credential.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivateRequest"}}}},"responses":{"201":{"description":"Aktiviert","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/ActivateResponse"}}}}}},"400":{"description":"VALIDATION_ERROR / INVALID_JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"LICENSE_EXPIRED / LICENSE_REVOKED / LICENSE_SUSPENDED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"INVALID_TOKEN","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"DEVICE_LIMIT_REACHED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"TOKEN_EXPIRED / TOKEN_DISABLED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/licenses/refresh":{"post":{"summary":"Neuen Lizenznachweis abrufen (Synchronisierung)","security":[{"activationCredential":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}}},"responses":{"200":{"description":"Erfolg","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/RefreshResponse"}}}}}},"401":{"description":"INVALID_ACTIVATION – Credential ungültig oder Aktivierung zurückgesetzt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"LICENSE_EXPIRED / LICENSE_REVOKED / LICENSE_SUSPENDED – details.license enthält den Status; Client muss auf Free zurückfallen","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/licenses/usage":{"post":{"summary":"Prepaid: Aufnahmezeit buchen","description":"Meldet die tatsächlich aufgenommenen Sekunden einer Aufnahme. usageKey (z. B. die Aufnahme-ID) macht die Meldung idempotent; das Guthaben wird nie negativ. Lizenzen ohne Guthaben (unbegrenzt) buchen nichts. Antwort enthält einen neuen Nachweis mit der Restzeit (Claim rec, null = unbegrenzt).","security":[{"activationCredential":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["installationId","usageKey","seconds"],"properties":{"installationId":{"type":"string"},"usageKey":{"type":"string","pattern":"^[A-Za-z0-9_.-]{8,64}$"},"seconds":{"type":"integer","minimum":1,"maximum":86400},"clientVersion":{"type":"string"}}}}}},"responses":{"200":{"description":"{ ok: true, data: { booked, license, proof, proofExpiresAt, refreshAfter, offlineDays } }"},"401":{"description":"INVALID_ACTIVATION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"LICENSE_EXPIRED / LICENSE_REVOKED / LICENSE_SUSPENDED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/licenses/deactivate":{"post":{"summary":"Dieses Gerät abmelden (Geräteplatz freigeben)","security":[{"activationCredential":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["installationId"],"properties":{"installationId":{"type":"string"}}}}}},"responses":{"200":{"description":"{ ok: true, data: { deactivated: true } }"},"401":{"description":"INVALID_ACTIVATION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/licenses/status":{"get":{"summary":"Lizenzstatus dieser Aktivierung","security":[{"activationCredential":[]}],"responses":{"200":{"description":"{ license, activation }"},"401":{"description":"INVALID_ACTIVATION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/licenses/features":{"get":{"summary":"Gültige Entitlements","description":"Ohne Authorization-Header: Features des Free-Plans (keine Registrierung nötig). Mit Credential: Features der Lizenz bzw. Free bei inaktiver Lizenz.","security":[{},{"activationCredential":[]}],"responses":{"200":{"description":"Erfolg","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/Features"}}}}}},"401":{"description":"INVALID_ACTIVATION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/license-keys":{"get":{"summary":"Öffentliche Signaturschlüssel (aktuell + vorherige für Rotation)","responses":{"200":{"description":"Erfolg","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/PublicKeys"}}}}}}}}},"/releases/latest":{"get":{"summary":"Neueste veröffentlichte Version","responses":{"200":{"description":"Erfolg","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/Release"}}}}}},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}