{"openapi":"3.1.0","info":{"title":"knowmind API","version":"0.2.0","description":"knowmind ist ein persistenter Memory-Layer für KI-Assistenten. Über REST und MCP greifen LLMs, CLI-Werkzeuge und Chat-Clients auf einen tenant-isolierten Wissensgraphen zu.\n\n**Authentifizierung:** Persönliche API-Tokens mit Präfix `kmt_` werden im Header `Authorization: Bearer <token>` mitgegeben. Tokens werden im Dashboard unter `/dashboard/tokens` ausgestellt und mit HMAC-SHA256 plus Server-Pepper und per-Token-Salt gehasht abgelegt. Der Tenant wird aus dem Token abgeleitet — eine Übergabe im Body ist nicht möglich.\n\n**Rate-Limits (pro IP, gleitendes Fenster):**\n- `/api/auth/signin/*`: 5 Anfragen / 60 s — Magic-Link-Versand.\n- `/api/onboarding`: 10 / 60 s — Tenant-Anlage.\n- `/api/checkout`: 10 / 60 s — Stripe-Session-Start.\n- `/api/kuendigung`: 5 / 60 s — öffentlicher Kündigungsbutton (§ 312k BGB).\n- `/api/mcp/v1` mit API-Tarif: 60 / 60 s.\n\nBeim Überschreiten: `429 Too Many Requests` mit `Retry-After`-Header in Sekunden.\n\n**Tarif-Gates (HTTP 402):** Endpunkte, die ein Plan-Feature benötigen, antworten mit `402 Payment Required` + JSON-Body `{ error: 'plan_upgrade_required', currentPlan, requiredPlans, upgradeUrl }`, wenn der Tenant-Plan das Feature nicht enthält. Aktive Gates:\n- BYOK-Speicherung (`POST /api/keys`): Plus und höher.\n- API-Tokens (`POST /api/tokens`): API-Tarif.\n- Audit-Log (`GET /api/audit/export`): Pro und höher.","contact":{"name":"Schübeler Consulting","url":"https://knowmind.de","email":"info@schuebeler-consulting.de"},"license":{"name":"Proprietär — Schübeler Consulting"}},"servers":[{"url":"https://knowmind.de","description":"Produktion (Hetzner DE)"}],"tags":[{"name":"Memory","description":"Hybrid-Recall, Schreiben, Auflisten"},{"name":"Graph","description":"Wissensgraph-Schema und -Statistik"},{"name":"Documents","description":"Dokumenten-Ingest mit asynchroner Indexierung"},{"name":"Tokens","description":"API-Token-Verwaltung (Session-Auth)"},{"name":"Keys","description":"BYOK-Schlüsselverwaltung (Session-Auth)"},{"name":"Tenant","description":"Tenant-Export, DSGVO-Endpunkte"},{"name":"MCP","description":"JSON-RPC 2.0 für Modell-Clients (Claude Code, Claude.ai, LM Studio, Ollama, ChatGPT-Connectoren)"},{"name":"Stats","description":"Plattform-Metriken"},{"name":"Service","description":"Verfügbarkeit und Monitoring"}],"components":{"securitySchemes":{"BearerToken":{"type":"http","scheme":"bearer","bearerFormat":"kmt_<base64url>","description":"Persönlicher API-Token. Im Dashboard unter `/dashboard/tokens` ausstellen."},"SessionCookie":{"type":"apiKey","in":"cookie","name":"authjs.session-token","description":"Auth.js-Session-Cookie (für Browser-/Dashboard-Routen)."}},"schemas":{"RecallRequest":{"type":"object","required":["query"],"properties":{"query":{"type":"string","example":"Wo läuft die OKR-App?"},"k":{"type":"integer","minimum":1,"maximum":25,"default":5},"hops":{"type":"integer","minimum":0,"maximum":3,"default":2}}},"RecallResult":{"type":"object","properties":{"hits":{"type":"array","items":{"type":"object","properties":{"memoryId":{"type":"string","format":"uuid"},"title":{"type":"string"},"snippet":{"type":"string"},"score":{"type":"number","format":"float"},"source":{"type":"string"},"hops":{"type":"integer"}}}},"latencyMs":{"type":"integer"}}},"DocumentIngest":{"type":"object","required":["title","content"],"properties":{"title":{"type":"string","example":"Onboarding-Handbuch Service-Team"},"content":{"type":"string","description":"Volltext (Markdown oder Plain)"},"source":{"type":"string","description":"Quellen-URL oder Pfad","nullable":true},"metadata":{"type":"object","additionalProperties":true}}},"Token":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","write","admin"]}},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":"string","format":"date-time","nullable":true}}},"Error":{"type":"object","properties":{"error":{"type":"string"}}},"JsonRpcRequest":{"type":"object","required":["jsonrpc","method"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]},"method":{"type":"string","example":"tools/call"},"params":{"type":"object"}}},"JsonRpcResponse":{"type":"object","required":["jsonrpc"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]},"result":{},"error":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"data":{}}}}}}},"paths":{"/health":{"get":{"tags":["Service"],"operationId":"healthCheck","summary":"Verfügbarkeits-Check (öffentlich, ohne Auth, ohne Rate-Limit)","description":"Leichtgewichtiger Health-Endpoint für externe Monitoring-Tools (Uptime-Checks). Wird direkt am Edge-Proxy beantwortet — ohne Authentifizierung und ohne Rate-Limit. Antwortet mit HTTP 200 und dem Klartext-Body `ok`, solange die Plattform erreichbar ist. Er prüft die Erreichbarkeit des Edge, NICHT den Zustand einzelner Dienste dahinter (Datenbank, Memory-Service).","security":[],"responses":{"200":{"description":"Plattform erreichbar.","content":{"text/plain":{"schema":{"type":"string","example":"ok"}}}}}}},"/api/memory/recall":{"post":{"tags":["Memory"],"summary":"Hybrid-Recall (BM25 + Vektor + Graph-Hops)","description":"Findet die top-k passenden Memories. Tenant wird aus Session/Token bezogen. Re-Ranking durch BGE-Reranker (e5-large 1024d).","security":[{"BearerToken":[]},{"SessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecallRequest"}}}},"responses":{"200":{"description":"Treffer-Liste","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecallResult"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/documents":{"post":{"tags":["Documents"],"summary":"Dokument zur Indexierung einreichen","description":"Übergibt ein Dokument an die Ingest-Pipeline. Volltext wird in Chunks zerlegt, e5-large-eingebettet, in pgvector + Neo4j abgelegt. Antwort enthält die DocumentId für Status-Polling.","security":[{"BearerToken":[]},{"SessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentIngest"}}}},"responses":{"202":{"description":"Akzeptiert, Indexierung läuft asynchron","content":{"application/json":{"schema":{"type":"object","properties":{"documentId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued"]}}}}}},"402":{"description":"Erinnerungs-Limit des Tarifs erreicht (plan_upgrade_required). Zählbasis: gespeicherte Dokumente/Erinnerungen des Arbeitsbereichs. Body enthält limit, used, requiredPlans und eine Klartext-message.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["plan_upgrade_required"]},"feature":{"type":"string"},"currentPlan":{"type":"string"},"requiredPlans":{"type":"array","items":{"type":"string"}},"limit":{"type":"integer"},"used":{"type":"integer"},"message":{"type":"string"},"upgradeUrl":{"type":"string"}}}}}}}}},"/api/graph/schema":{"get":{"tags":["Graph"],"summary":"Graph-Schema und Edge-Verteilung des Tenants","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"Schema","content":{"application/json":{"schema":{"type":"object","properties":{"nodes":{"type":"integer"},"edges":{"type":"integer"},"edgeTypes":{"type":"array","items":{"type":"object","properties":{"rel_type":{"type":"string"},"count":{"type":"integer"}}}}}}}}}}}},"/api/stats":{"get":{"tags":["Stats"],"summary":"Counter, Time-Series, Health-Indikator","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"Statistik","content":{"application/json":{"schema":{"type":"object","properties":{"memories":{"type":"integer"},"edges":{"type":"integer"},"chunks":{"type":"integer"},"queriesLast24h":{"type":"integer"},"p50Ms":{"type":"integer"},"p99Ms":{"type":"integer"}}}}}}}}},"/api/tokens":{"get":{"tags":["Tokens"],"summary":"Eigene Tokens auflisten","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"Token-Liste","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Token"}}}}}}},"post":{"tags":["Tokens"],"summary":"Neuen Token ausstellen","description":"Der Klartext-Token wird **einmalig** zurückgeliefert und nicht erneut anzeigbar. Bewahren Sie ihn sicher auf.","security":[{"SessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","scopes"],"properties":{"name":{"type":"string","example":"Claude Code Workstation"},"scopes":{"type":"array","items":{"type":"string","enum":["read","write","admin"]}}}}}}},"responses":{"200":{"description":"Token erzeugt (Klartext-Wert nur jetzt sichtbar)","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"token":{"type":"string","example":"kmt_…"}}}}}}}}},"/api/tokens/{id}":{"delete":{"tags":["Tokens"],"summary":"Token widerrufen","security":[{"SessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Widerrufen"}}}},"/api/keys":{"get":{"tags":["Keys"],"summary":"BYOK-Keys auflisten (maskiert)","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"Liste"}}},"post":{"tags":["Keys"],"summary":"BYOK-Key hinzufügen","description":"Speichert einen Provider-Schlüssel verschlüsselt (AES-256-GCM, per-Tenant-KEK). Klartext wird zum Provider-Call entschlüsselt und nirgends geloggt.","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"Key gespeichert"}}}},"/api/keys/{id}/test":{"post":{"tags":["Keys"],"summary":"BYOK-Key gegen den Provider testen","security":[{"SessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Roundtrip-Ergebnis"}}}},"/api/audit/export":{"get":{"tags":["Tenant"],"summary":"Audit-Log als CSV exportieren","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"CSV-Stream"}}}},"/api/tenant/export":{"get":{"tags":["Tenant"],"summary":"DSGVO-Vollexport des Tenants (JSON, alle Memories + Edges + Audit)","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"JSON-Bundle"}}}},"/api/mcp/v1":{"get":{"tags":["MCP"],"summary":"MCP-Tool-Beschreibung (Discovery für Browser)","responses":{"200":{"description":"MCP-Server-Metadaten"}}},"post":{"tags":["MCP"],"summary":"JSON-RPC 2.0 Endpoint für MCP-Clients","description":"Akzeptiert MCP-Methoden `initialize`, `tools/list`, `tools/call`, `prompts/list`, `prompts/get`. Tools (11): `knowmind_recall`, `knowmind_recall_at_time`, `knowmind_list_recent`, `knowmind_list_relations`, `knowmind_stats`, `knowmind_health`, `knowmind_store_memory`, `knowmind_upload_document`, `knowmind_update_fact`, `knowmind_link`, `knowmind_unlink`. Maßgeblich ist die Antwort von `tools/list`.\n\nKlienten:\n- **Claude.ai Desktop** (über Custom-Connector)\n- **Claude Code** (`claude mcp add knowmind --url https://knowmind.de/api/mcp/v1`)\n- **LM Studio** (MCP-Plugin)\n- **Continue / Cursor**","security":[{"BearerToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcRequest"}}}},"responses":{"200":{"description":"JSON-RPC-Antwort","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"401":{"description":"Bearer-Token fehlt oder ungültig"},"429":{"description":"Rate-Limit überschritten — siehe Retry-After-Header"}},"x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://knowmind.de/api/mcp/v1 \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Bearer kmt_...\" \\\n  -d '{\n    \"jsonrpc\": \"2.0\",\n    \"id\": 1,\n    \"method\": \"tools/call\",\n    \"params\": {\n      \"name\": \"knowmind_recall\",\n      \"arguments\": { \"query\": \"Wo läuft die OKR-App?\", \"k\": 5 }\n    }\n  }'"},{"lang":"python","label":"Python","source":"import os, requests\n\nr = requests.post(\n    \"https://knowmind.de/api/mcp/v1\",\n    headers={\"Authorization\": f\"Bearer {os.environ['KNOWMIND_TOKEN']}\"},\n    json={\n        \"jsonrpc\": \"2.0\",\n        \"id\": 1,\n        \"method\": \"tools/call\",\n        \"params\": {\n            \"name\": \"knowmind_recall\",\n            \"arguments\": {\"query\": \"Wo läuft die OKR-App?\", \"k\": 5},\n        },\n    },\n)\nprint(r.json())"},{"lang":"javascript","label":"JavaScript (Node.js)","source":"const r = await fetch(\"https://knowmind.de/api/mcp/v1\", {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/json\",\n    Authorization: `Bearer ${process.env.KNOWMIND_TOKEN}`,\n  },\n  body: JSON.stringify({\n    jsonrpc: \"2.0\",\n    id: 1,\n    method: \"tools/call\",\n    params: {\n      name: \"knowmind_recall\",\n      arguments: { query: \"Wo läuft die OKR-App?\", k: 5 },\n    },\n  }),\n});\nconsole.log(await r.json());"}]}},"/api/onboarding":{"post":{"tags":["Tenant"],"summary":"Self-Service Tenant + Owner anlegen","description":"Wird vom Onboarding-Wizard nach dem ersten Magic-Link-Login aufgerufen. Erzeugt Tenant + setzt den eingeloggten User als Owner. Bei Bezahltarif liefert die Antwort eine Stripe-Checkout-URL.","security":[{"SessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["slug","name","plan"],"properties":{"slug":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{1,62}$"},"name":{"type":"string","maxLength":200},"plan":{"type":"string","enum":["free","plus","pro","team","api"]}}}}}},"responses":{"200":{"description":"Tenant erzeugt","content":{"application/json":{"schema":{"type":"object","properties":{"tenantId":{"type":"string","format":"uuid"},"redirectUrl":{"type":"string"}}}}}},"402":{"description":"Tarif-Pflicht (z. B. bei API-Tarif und Stripe disabled)"},"409":{"description":"Slug bereits vergeben, oder Tenant existiert bereits"},"429":{"description":"Rate-Limit überschritten"}}}},"/api/subscription/portal":{"post":{"tags":["Tenant"],"summary":"Stripe-Customer-Portal-Session erzeugen","description":"Liefert eine URL zum Stripe-Customer-Portal, in dem der Kunde Plan-Wechsel, Zahlungsmittel, Rechnungen und Kündigung selbst verwaltet.","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"Portal-URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}}}}}},"404":{"description":"Kein Stripe-Customer (z. B. Free-Tarif)"},"503":{"description":"Stripe nicht konfiguriert"}}}},"/api/checkout":{"post":{"tags":["Tenant"],"summary":"Stripe-Checkout-Session starten","description":"Eingeloggter User wechselt von Free auf einen Bezahltarif. Liefert die Stripe-Checkout-URL. Nach erfolgreichem Checkout setzt der Webhook `stripe-webhook` den Tenant-Plan und schreibt Customer- und Subscription-ID in die billing-Tabelle.","security":[{"SessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["plan"],"properties":{"plan":{"type":"string","enum":["plus","pro","team","api"]},"billing":{"type":"string","enum":["monthly","yearly"],"default":"monthly"}}}}}},"responses":{"200":{"description":"Checkout-URL"},"429":{"description":"Rate-Limit überschritten"},"503":{"description":"Stripe nicht konfiguriert"}}}},"/api/checkout/webhook":{"post":{"tags":["Tenant"],"summary":"Stripe-Webhook (HMAC-signiert, nicht öffentlich aufrufbar)","description":"Empfängt Stripe-Events. Signatur über `Stripe-Signature`-Header mit `STRIPE_WEBHOOK_SECRET` (HMAC-SHA256). Ungültige Signatur: 400. Verarbeitet aktuell:\n- `checkout.session.completed` → Tenant-Plan aktivieren, Customer/Subscription-ID speichern, Audit-Event `subscription.activated`.\n- `customer.subscription.deleted` → Plan auf Free zurück, Audit-Event `subscription.cancelled`.","responses":{"200":{"description":"Event akzeptiert"},"400":{"description":"Ungültige Signatur"},"503":{"description":"Stripe nicht konfiguriert"}}}}}}