{"openapi":"3.1.0","info":{"title":"CookieSnitch API","version":"1","description":"DSGVO-Erstprüfung einer Website. Ablauf: POST /api/scans → GET /api/scans/{id}?wait=60 bis status done/error → report. Gratis-Kontingent je IP (FREE_SCANS, Standard 3 in 30 Tagen), danach 1 Credit je Prüfung (10 = 5 €, 50 = 19 €, 200 = 49 €; Kauf unter /credits). Kurzfassung: /llms.txt"},"servers":[{"url":"/"}],"security":[{},{"key":[]}],"paths":{"/api/scans":{"get":{"operationId":"listScans","summary":"Öffentliche Scans (ohne Bericht), neueste zuerst","responses":{"200":{"description":"Liste","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScanSummary"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"createScan","summary":"Prüfung starten","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","maxLength":2000,"examples":["beispiel.de"]},"public":{"type":"boolean","default":true,"description":"false: nicht gelistet, ID mit 128 Bit Zufall"}}}}}},"responses":{"202":{"description":"Eingereiht","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanCreated"}}}},"400":{"description":"Ungültige Eingabe (URL, private Adresse, public kein Boolean, kaputtes JSON)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Gratis-Kontingent und Credits aufgebraucht","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRequired"}}}},"403":{"description":"Anfrage aus dem Browser einer fremden Seite","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body zu groß","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body ist kein JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Warteschlange voll","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/scans/{id}":{"get":{"operationId":"getScan","summary":"Stand und Bericht eines Scans","parameters":[{"$ref":"#/components/parameters/id"},{"name":"wait","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":60},"description":"Sekunden: antwortet, sobald status done oder error ist, spätestens nach wait Sekunden mit dem aktuellen Stand"}],"responses":{"200":{"description":"Stand; bei status done mit report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Scan"}}}},"400":{"description":"Ungültige Scan-ID oder wait nicht 0–60","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"deleteScan","summary":"Scan löschen (nur mit dem Schlüssel des Erstellers)","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"204":{"description":"Gelöscht"},"400":{"description":"Ungültige Scan-ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Nicht der Ersteller (oder Anfrage von fremder Seite)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Scan läuft noch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/credits":{"get":{"operationId":"getCredits","summary":"Guthaben des Schlüssels und Gratis-Kontingent der IP","responses":{"200":{"description":"Stand","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Credits"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}}},"components":{"securitySchemes":{"key":{"type":"http","scheme":"bearer","description":"Schlüssel cc_… (Kauf unter /credits). Ohne Schlüssel gilt das Gratis-Kontingent der IP."}},"parameters":{"id":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Scan-ID aus POST /api/scans"}},"responses":{"Unauthorized":{"description":"Unbekannter Schlüssel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Scan nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"Status":{"type":"string","enum":["queued","running","done","error"]},"Verdict":{"type":["string","null"],"enum":["rot","gelb","grün",null]},"Counts":{"type":["object","null"],"additionalProperties":{"type":"integer"},"description":"Befunde je Schwere (kritisch, hoch, mittel, niedrig, info)"},"ScanCreated":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string"},"status":{"$ref":"#/components/schemas/Status"}}},"ScanSummary":{"type":"object","properties":{"id":{"type":"string"},"input":{"type":"string"},"status":{"$ref":"#/components/schemas/Status"},"verdict":{"$ref":"#/components/schemas/Verdict"},"scannedAt":{"type":["string","null"]},"fatal":{"type":["boolean","null"]},"counts":{"$ref":"#/components/schemas/Counts"}}},"Scan":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string"},"input":{"type":"string"},"host":{"type":"string"},"status":{"$ref":"#/components/schemas/Status"},"verdict":{"$ref":"#/components/schemas/Verdict"},"fatal":{"type":["boolean","null"],"description":"true: Startseite nicht erreichbar, Versuch bzw. Credit zurückgegeben"},"scannedAt":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"counts":{"$ref":"#/components/schemas/Counts"},"error":{"type":["string","null"],"description":"Fehlermeldung bei status error"},"progress":{"type":"object","properties":{"step":{"type":"integer"},"message":{"type":"string"},"queuePosition":{"type":["integer","null"]}}},"report":{"type":"object","description":"Bericht (nur bei status done): verdict, counts, findings, hosts, cookies, storage, consent, impressum, privacy, tech, pages, fatal"}}},"PaymentRequired":{"type":"object","required":["error","buyUrl","freeRemaining","resetAt"],"properties":{"error":{"type":"string"},"buyUrl":{"type":"string","format":"uri","description":"Seite zum Kauf von Credits (…/credits)"},"freeRemaining":{"type":"integer","const":0},"resetAt":{"type":["string","null"],"format":"date-time","description":"Wann der älteste Gratis-Versuch der IP verfällt"}}},"Credits":{"type":"object","required":["credits","freeRemaining","freeLimit","resetAt"],"properties":{"credits":{"type":["integer","null"],"description":"null ohne Schlüssel"},"freeRemaining":{"type":"integer"},"freeLimit":{"type":"integer"},"resetAt":{"type":["string","null"],"format":"date-time"}}}}}}