API-Dokumentation

Integrieren Sie PSA-Analysen in Ihre Anwendungen über die REST-API. Benötigt einen Pro- oder Enterprise-Plan.

Bevor Sie auf einem Signal aufbauen: was PSA nicht tut ist getrennt dokumentiert, mit der Messung hinter jeder Grenze (kein Weltmodell, die aktuelle Fehlalarmrate auf gewöhnlichem Verkehr, die dauerhaften Nicht-Ziele). Lesen Sie Was PSA nicht ist.

Authentifizierung

Fügen Sie Ihren API-Schlüssel in den Authorization-Header ein:

Authorization: Bearer psa_your_api_key_here

Generieren Sie API-Schlüssel aus den Einstellungen.

✓ API KEY AUTHENTIFIZIERUNG

Alle Endpunkte unterstützen API-Schlüssel-Authentifizierung über den Authorization: Bearer psa_...-Header. Dies ist die empfohlene Methode für den programmatischen Zugriff aus Skripten und Anwendungen.

Basis-URL

https://splabs.io

Gesundheit

GET /ping

Einfache Integritätsprüfung. Keine Authentifizierung erforderlich, keine Datenbankabhängigkeit.

{ "status": "ok" }
GET /health

Vollständige Integritätsprüfung mit Datenbankverbindungstest. Gibt 503 zurück, wenn Datenbank unerreichbar ist.

{ "status": "ok", "db": "connected" }

Öffentliche API v1

v1

Schreibgeschützter Sitzungszugriff mit PSA-Anreicherung - BHS-Trend, Regimewechseltyp, Haltungssequenz. Präfix: /v1/

Sitzungen

GET /v1/sessions

Paginierte Sitzungsliste mit PSA-Anreicherung - BHS-Trend, Regimetyp. Enthält ein ungefilterte Zusammenfassungsobjekt aus vorberechneten Statistiken (O(1), kein Tabellenscan).

Abfrageparameter

pageGanzzahl, Standard 1
per_pageGanzzahl, Standard 25, maximal 200
searchSitzungsnamensfilter
alertKomma-getrennte Ebenen: RED,YELLOW
sortcreated_at (Standard) | name | max_alert | n_turns
orderdesc (Standard) | asc
group_tokenscope the list to one linked thread (cross-session linking, #3360), ordered by group_pos

Each session also carries group_token and group_pos (both null when unlinked) - the thread it belongs to and its position in the sequence.

curl "https://splabs.io/v1/sessions?per_page=25&page=1" \
  -H "Authorization: Bearer psa_your_key"
{
  "sessions": [{ "id": "...", "name": "...", "max_alert": "RED",
                  "avg_bhs": 0.41, "bhs_trend": "declining", "n_turns": 12 }],
  "total": 20438, "page": 1, "per_page": 25, "total_pages": 818,
  "summary": { "total": 20438, "red": 287, "yellow": 1604, "green": 18547,
               "drm_critical": 113, "total_turns": 184220, "psa_postures": 184220 }
}
GET /v1/sessions/{session_id}

Rufen Sie vollständige Details für eine Sitzung ab, einschließlich aller Durchläufe, Metriken und Warnhistorie.

curl https://splabs.io/v1/sessions/your-session-uuid \
  -H "Authorization: Bearer psa_your_key"
POST /v1/sessions/link · link · unlink · reorder

Manage cross-session linking (group_token / group_pos) from the dashboard. Curated order only - the forensic analysis timestamp is never changed; no classifier score is touched. Auth: cookie or API key; only the caller's own sessions.

  • POST /v1/sessions/link - body {"session_ids": ["…","…"], "group_token": "optional"}. Links ≥2 sessions into a thread, appending in order; generates an opaque LINK-xxxxxxxx token when none is given.
  • POST /v1/sessions/{id}/unlink - removes one session from its thread and re-compacts the remaining positions to 1..N.
  • POST /v1/sessions/reorder - body {"group_token": "…", "ordered_session_ids": ["…","…"]}. Reassigns group_pos = 1..N; the id list must match the thread membership exactly.

All return {"ok": true, "group_token": "…", "thread": [{"session_id": "…", "group_pos": 1}, …]}.

curl -X POST https://splabs.io/v1/sessions/link \
  -H "Authorization: Bearer psa_your_key" -H "Content-Type: application/json" \
  -d '{"session_ids": ["uuid-a", "uuid-b"]}'

GET /v1/sessions/thread/{group_token}/sedimentation

Cross-session sedimentation signal - the slow boil-the-frog drift accumulated across the thread's turns (cross-session accumulator #2859, consumes existing C1/C2/C4 output; no scoring change). Returns {"score", "level", "n_sessions", "n_nodes", "band", "min_nodes"}; level is green/yellow (capped). Validated TBB Stage-1.

DSA - Postural Sequence Analysis

v2

Verhaltensklassifizierung auf Satzebene (C0-C4). Präfix: /api/v2/psa/

POST /api/v2/psa/public-analyze Public · keine Authentifizierung

Kostenlose, nicht authentifizierte PSA-Analyse für das Landing-Widget. Zustandslos (Analyse wird nie gespeichert; nur Aggregatzähler werden geschrieben). Burst-begrenzt 30/min pro IP, plus DB-gestützte Tageslimits (globaler 10k geteilter Pool, 10 pro IP für nicht authentifizierte Besucher); max. 12 Turns / 8000 Zeichen. Senden Sie entweder turns (Mehrfach-Turn) oder text (einzelne Antwort). Gibt Pro-Turn-PSA-Ergebnisse plus tokens zurück - die tatsächliche Anzahl verarbeiteter MiniLM-Subwort-Token (kein Abrechnungs-/Kreditwert).

Anfrage-Body

{
  "turns": [ {"user": "How do I do X?", "model": "I can help with that."} ],
  "clf_context": "clinical"
}
GET /api/v2/psa/public-stats Public · keine Authentifizierung

Ehrliche Statistiken für das Landing: live kumulierte analyses_run (O(1) Einzelzeilen-Lesen, bei Schreibzeit aktualisiert, aus echter Historie geseeded) + heute / remaining_today + verkehrsunabhängige Fähigkeitszahlen (classifiers 13, metrics 38, behavioral_classes 116, languages 5, cpf_indicators 100) aus METRICS_REFERENCE.

POST /api/v2/psa/analyze

Analysiert einen Konversations-Turn. Unterstützt drei Modi: nur-Agent (response_text), nur-Benutzer (user_text) oder vollständiges Paar (beide). Das turn_type-Feld in der Antwort gibt an, welche Seite analysiert wurde.

Anfrage-Body

{
  "response_text": "The AI response to analyze",   // absent for user_only turns
  "user_text": "The human message for this turn",  // absent for agent_only turns
  "input_text": "optional - legacy alias for user_text",
  "session_id": "your-session-uuid",   // OR use session_name (one required unless dry_run)
  "session_name": "my-session",        // auto-creates on first call, looked up after
  "turn": 1,
  "dry_run": false,                    // true = stateless analysis, no DB write
  "persist_mode": "safe",              // "safe" (default) = answer after the write | "fast" = answer now, write in background
  "debug_timing": false,               // true = add "_timings_ms" (per-stage wall time of the persisted tail)
  "save_text": "all",                  // "all"|"user"|"agent"|"none" - which sentences to persist
  "encrypt_original": false,           // true = store original encrypted at rest (needs a registered public key)
  "clf_context": "clinical",           // "clinical" (default, C3-v2 / hri) | "agentic" (C3-v3 / abi)
  "group_token": null                  // optional opaque thread token to link this session across sessions (#3360)
}

Mindestens eines von response_text oder user_text muss angegeben werden - beide weglassen gibt 422 zurück. Im normalen Modus ist entweder session_id oder session_name erforderlich (beide weglassen gibt 503 zurück). turn_type wird serverseitig abgeleitet: full (beide Seiten), agent_only (nur Antwort), user_only (nur Benutzernachricht). save_text: "all" speichert Benutzer- und Agenten-Sätze (Standard); "user" speichert nur c0_sentences; "agent" nur c1_sentences; "none" speichert nur Metriken - kein Rohtext. Bei dry_run: true ignoriert. encrypt_original: wenn true und ein öffentlicher Schlüssel registriert ist (siehe Privacy API / /psa-privacy), wird das gespeicherte Original verschlüsselt (enc_user_text/enc_response_text) und ist nur mit dem privaten Schlüssel des Mandanten lesbar; ohne Schlüssel oder bei Fehler wird kein Text gespeichert. Unabhängig von save_text. Standard false.

Welche Linse den Agenten-Turn bewertet: clf_context. "clinical" (Standard) bewertet das Verhalten mit C3-v2 und liefert hri; "agentic" bewertet es mit C3-v3 und liefert abi. Nichts leitet die Linse ab: Traffic von KI-Agenten wird klinisch bewertet, solange Sie nicht bei jedem Aufruf "agentic" senden. Die Stopp-Baender unterscheiden sich: clinical alert gelb/rot, agentic abi >= 0.50 (0.25-0.49 = umformulieren). Nur diese beiden Werte werden akzeptiert: alles andere liefert 422. Die Antwort nennt immer die tatsaechlich verwendete Linse in c3.clf_context, mit c3.lens_fallback wenn "agentic" angefordert wurde und C3-v2 geantwortet hat (Konfidenz unter 0.50), dazu ein c3.lens_fallback_reason. Leiten Sie die Linse nicht aus dem Vorhandensein von abi ab: das unterscheidet "clinical angefordert" nicht von "agentic lief nicht".

Was jede Turn-Form tatsaechlich misst. Ab Turn 2 traegt die Antwort zusaetzlich analysis_mode partial und missing_user_context true. Symmetrisch hat ein user_only-Turn kein bhs und keinen Haltungs-Alert, weil es keinen Agenten-Turn zu bewerten gibt.

Antwort

{
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "turn": 1,
  "turn_type": "full",                 // "full" | "agent_only" | "user_only"
  "c1": { "postures": [0,2,1], "sentences": ["..."], "poi": 0.33, "pe": 0.91, "dpi": 0.07, "mps": 2 },
  "c2": { "postures": [0,0,1], "confidences": [0.91,0.88,0.72], "sd": 0.08 },
  "c3": { "postures": [0,0,0], "hri": 0.0 },
  "c4": { "postures": [1,0,2], "pd": 0.15, "td": 2 },
  "c0": { "postures": [3,1], "confidences": [0.91,0.84], "cpi": 0.8 },
  "bhs": 0.87,
  "alert": "green",
  "incongruence": null,
  "cts": { "cts_composite": 0.99, "cts_level": "critical",   // agent-channel threat-intent (#3143)
           "primary_intent": "exfiltration",
           "cts_head_score": 0.99, "cts_head_available": true,  // 7-class cT encoder head: 1 − P(none)
           "exfiltration": 0.95, "secret_read": 0.02, "credential_theft": 0.01,
           "code_exec": 0.0, "instruction_hijack": 0.0, "tool_enumeration": 0.0 }
}

c1-c4, bhs, alert sind null für user_only-Turns. c0 ist null für agent_only-Turns. dpi ist auf [0,1] normalisiert.

curl-Beispiel - mit Sitzung

curl -X POST https://splabs.io/api/v2/psa/analyze \
  -H "Authorization: Bearer psa_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "response_text": "Of course, I would be happy to help!",
    "session_name": "my-session",
    "turn": 1
  }'

curl-Beispiel - Testlauf (keine Sitzung)

curl -X POST https://splabs.io/api/v2/psa/analyze \
  -H "Authorization: Bearer psa_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "response_text": "Of course, I would be happy to help!",
    "dry_run": true
  }'

Python-Beispiel

import requests

# Normal mode - session required
resp = requests.post(
    "https://splabs.io/api/v2/psa/analyze",
    headers={"Authorization": "Bearer psa_your_key"},
    json={
        "response_text": "Of course, I would be happy to help!",
        "session_name": "my-session",
        "turn": 1,
    }
)
print(resp.json())

# Dry-run - stateless, no session needed
resp = requests.post(
    "https://splabs.io/api/v2/psa/analyze",
    headers={"Authorization": "Bearer psa_your_key"},
    json={
        "response_text": "Of course, I would be happy to help!",
        "dry_run": True,
    }
)
# Response includes "dry_run": true but no session_id or turn
print(resp.json())

Klassifizierer

  • C0 - Input-Druck (Postures I0-I9, CPI-Score)
  • C1 - Adversarial Stress Posture (21 Klassen P0-P20, POI/PE/DPI-Metriken)
  • C2 - Sycophancy Density (SD)
  • C3 - Hallucination Risk Index (HRI)
  • C4 - Persuasion Density & Technique Diversity (PD/TD)
  • BHS - Verhaltensgesundheitsscore (Komposit 0-1)

SRP - Sitzungs-Risikoprofil: eine abgeleitete Lesung auf Sitzungsebene (kein Klassifikator, kein /analyze-Feld) aus c2.sd, c4.pd und bhs gegen eine Routine-Registernorm; schreibgeschützt unter /api/v2/intel/session-risk-profile/reference.

MCP /mcp

PSA spricht auch das Model Context Protocol, sodass eine KI-IDE, ein Agent oder ein Orchestrator das Verhalten nativ lesen kann, ohne HTTP-Code zu schreiben. Richten Sie einen MCP-Client auf den unten gehosteten Streamable-HTTP-Endpunkt und authentifizieren Sie sich mit Ihrem API-Schlüssel als Bearer-Token. Zwei Werkzeuge werden bereitgestellt, jedes zustandslos (nichts wird gespeichert) und mit denselben Feldern wie /analyze:

  • psa_analyze_turn: liest einen Gesprächszug aus user_text und/oder response_text. Gibt den Verhaltensgesundheits-Score, das Alarmband und die Messwerte pro Haltung zurück.
  • psa_analyze_conversation: liest eine ganze Konversation (turns: eine Liste von {user_text, response_text}-Objekten) und gibt die Messwerte pro Zug sowie ein Drift-Profil zurück (Mittelwert von Verhaltensgesundheit, Unterwürfigkeit und Persuasion).

MCP-Client-Konfiguration

{
  "mcpServers": {
    "psa": {
      "url": "https://splabs.io/mcp",
      "headers": { "Authorization": "Bearer psa_your_key" }
    }
  }
}

Ein Werkzeug direkt aufrufen (JSON-RPC)

curl -X POST https://splabs.io/mcp \
  -H "Authorization: Bearer psa_your_key" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "psa_analyze_turn",
      "arguments": { "response_text": "Of course, I would be happy to help!" }
    }
  }'
GET /api/v2/psa/stats

Vorberechnete Aggregatzähler für den authentifizierten Benutzer - O(1) Primärschlüssel-Lesen aus der user_stats-Tabelle. Wird bei jedem /analyze-Aufruf aktuell gehalten (inkrementelles UPSERT). Greift auf eine Live-Aggregatabfrage zurück, wenn die Zeile fehlt (neue Benutzer oder Konten vor der Migration).

{
  "total": 20438,
  "green": 18122, "yellow": 1604, "orange": 312, "red": 287, "critical": 113,
  "drm_critical": 41, "drm_orange": 96,
  "total_turns": 184220,
  "avg_bhs": 0.847,
  "avg_poi": 0.091
}

Verwenden Sie diesen Endpunkt statt clientseitigem Sitzungszählen. Das total-Feld stimmt mit dem ungefilterten Paginierungs-Total überein, das von /api/v2/psa/sessions und /api/sessions zurückgegeben wird.

GET /api/v2/psa/sessions

Paginierte Liste von DSA-Sitzungen. Serverseitige Paginierung - gibt nie die vollständige Tabelle zurück.

Abfrageparameter

pageGanzzahl, Standard 1
per_pageGanzzahl, Standard 50, maximal 200
qSuchzeichenkette (Sitzungsname)
min_alertMinimale Schweregrad zum Zurückgeben: green | yellow | orange | red | critical
sort_byalert (Schweregrad erste) oder weglassen für neueste zuerst
curl "https://splabs.io/api/v2/psa/sessions?per_page=20&page=1&min_alert=yellow&sort_by=alert" \
  -H "Authorization: Bearer psa_your_key"
{
  "sessions": [{ "id": "...", "name": "...", "alert": "red", "bhs": 0.41, "poi": 0.68,
                  "turns": 12, "created_at": "2025-04-13T10:22:00Z" }],
  "total": 287, "page": 1, "per_page": 20, "total_pages": 15
}
GET /api/v2/psa/session/{session_id}

Posture sequence for a session - turns with BHS and C0-C4 classifier scores. Always paginated (page, page_size ≤ 200). Optional server-side search/filter: q (ILIKE on turn text) and alert (green|yellow|red|critical). When a filter is active, total/total_pages describe the filtered result set and filtered is true.

curl "https://splabs.io/api/v2/psa/session/your-session-uuid?page=1&page_size=10&alert=critical" \
  -H "Authorization: Bearer psa_your_key"
GET /api/v2/psa/session/{session_id}/series

Compact per-turn numeric series for the whole session (no text, no sentences) plus a server-computed summary block (peak HRI, BHS floor, avg BHS, max alerts). Powers the detail-view charts, heatmap and timeline without loading the full transcript. The expandable turn cards use the paginated endpoint above.

curl https://splabs.io/api/v2/psa/session/your-session-uuid/series \
  -H "Authorization: Bearer psa_your_key"
GET /api/v2/psa/session/{session_id}/export

Raw export of every scored turn - format=csv (default) or format=json. One flat row per turn with the pre-computed columns (BHS, POI, PE, DPI, MPS, SD, HRI, PD, TD, CPI) - no synthetic metrics, no runtime aggregation. Take the data to your own tools; the dashboard shows signal, not studies.

curl https://splabs.io/api/v2/psa/session/your-session-uuid/export?format=csv \
  -H "Authorization: Bearer psa_your_key"
GET /api/v2/psa/session/{session_id}/regime

Regime-Shift-Klassifizierung für die Sitzung. Gibt Typ und Konfidenz zurück.

{
  "regime_type": "PROGRESSIVE_DRIFT",
  "confidence": 0.87,
  "details": "Monotonic BHS decline over 12 turns"
}
GET /api/v2/psa/session/{session_id}/summary

Zusammenfassung auf Sitzungsebene - BHS Start/Ende/Durchschnitt/Minimum, Trend, Runde mit Spitzenrisiko, Alarmverteilung.

{
  "bhs_start": 0.91, "bhs_end": 0.43, "bhs_avg": 0.67, "bhs_min": 0.38,
  "bhs_slope": -0.048, "bhs_trend": "declining",
  "peak_risk_turn": 9, "peak_risk_bhs": 0.38,
  "alert_distribution": {"green": 3, "yellow": 4, "orange": 2, "red": 1},
  "drm_critical_turns": [7, 9]
}

Admin - Benutzerbereitstellung & Organisationen

Alle /api/admin/-Endpunkte erfordern Admin-Rolle (Bearer-Token oder Cookie). Verwendet von Operation Fury und Produktions-Deployments für Massenkontoverwaltung und Org-Level-Analytik.

POST /api/admin/users/provision nur Admin · atomarer Benutzer+Schlüssel

Erstellt atomisch einen Benutzer + API-Schlüssel in einer Transaktion. Kein Ratenlimit. Gibt den rohen psa_xxxx-Schlüssel zurück (einmal angezeigt). Optional: neuen Benutzer in eine Org via org_id aufnehmen.

Body: email (erforderlich), name, role, plan, key_name, org_id (UUID, optional).

POST /api/admin/orgs nur Admin · 201

Erstellen Sie eine neue Organisation. Body: name (erforderlich), sector (hospital/war/finance/…), plan.

GET /api/admin/docs/list admin only

Docs Library (#3492, #3531): list the product/paper/GTM document set, grouped by section. Each document has a title, description, created/updated dates and its downloadable variants. Served from a curated manifest committed in the image (no live GitHub calls), so the admin /admin/docs page loads instantly. Files themselves are never stored in the production image - they stream on download via /fetch.

GET /api/admin/docs/fetch admin only

Stream one whitelisted document (path query param) from the repo as a download. Only paths inside the docs-library section directories are allowed - no traversal, no arbitrary repo files.

GET /api/admin/orgs nur Admin · paginiert

Listet alle Organisationen mit Live-member_count und session_count. Query-Parameter: page, per_page (max 200).

GET /api/admin/orgs/{org_id} nur Admin
POST /api/admin/orgs/{org_id}/members nur Admin · idempotent

Fügen Sie die Zugehörigkeit eines Benutzers hinzu oder aktualisieren Sie diese. Body: user_id (UUID), role (owner/member).

GET /api/admin/orgs/{org_id}/members nur Admin · paginiert
DELETE /api/admin/orgs/{org_id}/members/{user_id} nur Admin
POST /api/admin/orgs/{org_id}/enroll-sector nur Admin · idempotent

Nimmt alle fury-<sektor>-*-Konten massenweise in diese Org auf. Erfordert, dass die Org einen sektor gesetzt hat. Gibt enrolled count zurück.

Zahlungen &amp; Abrechnung

Web-Sitzung

Abonnement-Verwaltung und Rechnungshistorie. Alle Endpunkte erfordern Cookie-Authentifizierung (Web-Sitzung). Präfix: /api/payments/

GET /api/payments/history

Gibt die paginierte Zahlungshistorie für den authentifizierten Benutzer zurück, sortiert nach dem aktuellsten zuerst.

Abfrageparameter

pageGanzzahl, Standard 1
per_pageGanzzahl, Standard 10, maximal 100

Antwort

{
  "items": [
    {
      "id": "uuid",
      "amount": 2900,
      "currency": "usd",
      "status": "succeeded",
      "plan": "pro",
      "description": "Subscription payment - pro",
      "created_at": "2026-04-01T10:22:00+00:00"
    }
  ],
  "total": 6,
  "page": 1,
  "per_page": 10,
  "pages": 1
}

Betrag ist in Cent (z. B. 2900 = $29,00). Status-Werte: succeeded | failed.

POST /api/payments/create-checkout

Initiiert eine Stripe-Checkout-Sitzung oder Abonnementänderung, abhängig vom aktuellen Plan des Benutzers. plan muss "pro" oder "enterprise" sein.

Anfrage-Body

{ "plan": "pro" }

Verhaltensmatrix

Aktueller StatusZielplanErgebnis
Kostenlos / kein AbonnementPro / EnterpriseNeue Stripe Checkout-Sitzung → {"url": "..."}
Aktives Abonnementgleicher PlanAbrechnungsportal-Umleitung → {"url": "...", "action": "portal"}
Aktiv ProEnterprise (Upgrade)Subscription.modify() direkt → {"action": "upgraded", "plan": "enterprise"}
Aktiv EnterprisePro (Downgrade)HTTP 403 - blockiert mit Enddatum der Periode
Aktiv Pro / EnterpriseKostenlos (Downgrade)HTTP 403 - blockiert

Antworten

// New checkout
{ "url": "https://checkout.stripe.com/..." }

// Same plan - portal
{ "url": "https://billing.stripe.com/...", "action": "portal" }

// Upgrade (no redirect needed)
{ "action": "upgraded", "plan": "enterprise" }

// Downgrade blocked (HTTP 403)
{ "detail": "Cannot downgrade while subscription is active. Your current plan is valid until 1 May 2026. Cancel first if you wish to switch to a lower plan." }
POST /api/payments/portal

Erstellt eine Stripe-Abrechnungsportal-Sitzung zum Verwalten von Zahlungsmethoden und zum Anzeigen von Rechnungen.

{ "url": "https://billing.stripe.com/..." }
POST /api/payments/cancel-subscription

Bricht am Periodenende ab (cancel_at_period_end=true). Zugriff läuft bis zum Periodenende weiter.

{ "ok": true, "cancel_at_period_end": true, "period_end": 1751410800 }

period_end ist ein Unix-Timestamp.

POST /api/payments/sync-subscription

Ruft den Abonnementstatus erneut von Stripe ab und aktualisiert die lokale Datenbank. Verwenden, wenn der lokale Status abweicht (z. B. nach einem fehlgeschlagenen Webhook).

{ "ok": true, "status": "active",
  "period_start": "2026-04-01T00:00:00+00:00",
  "period_end": "2026-05-01T00:00:00+00:00",
  "plan": "pro" }
GET /api/payments/token-packs

Gibt verfügbare Extra-Token-Packs für den einmaligen Kauf zurück. Gültig für 12 Monate ab Kaufdatum.

{ "packs": [
  { "id": "starter", "label": "Starter Pack", "tokens": 500,  "price_cents": 900,  "currency": "eur" },
  { "id": "growth",  "label": "Growth Pack",  "tokens": 2000, "price_cents": 2900, "currency": "eur" },
  { "id": "power",   "label": "Power Pack",   "tokens": 5000, "price_cents": 5900, "currency": "eur" }
] }
POST /api/payments/purchase-tokens

Erstellt eine Stripe-Einmal-Checkout-Sitzung für ein Extra-Token-Paket. Den Benutzer zur zurückgegebenen url weiterleiten. Bei Erfolg ruft Stripe den Webhook auf und krediert Token dem Guthaben des Benutzers. Token werden nach Ausschöpfung der monatlichen Zuweisung verbraucht.

// Request
{ "pack": "starter" }  // "starter" | "growth" | "power"

// Response
{ "url": "https://checkout.stripe.com/..." }

Privacy API - at-rest encryption

v2

Register an RSA public key so original text can be stored encrypted at rest - the server encrypts but cannot decrypt; only the tenant private key (client-side, Web Crypto) can. Envelope: per-record AES-256-GCM wrapped with RSA-OAEP(SHA-256). Prefix: /api/v2/psa/privacy

POST /api/v2/psa/privacy/pubkey

Register/replace the public key. Body: {"public_key_pem": "-----BEGIN PUBLIC KEY----- …"} (RSA, ≥2048-bit). Returns {configured, fingerprint, algo}; 422 if invalid.

GET /api/v2/psa/privacy/pubkey

Returns {configured:false} or {configured:true, fingerprint, algo, created_at, updated_at}.

DELETE /api/v2/psa/privacy/pubkey

Removes the key. Returns {configured:false, deleted}. Pair with encrypt_original on /analyze.

Konnektoren-API

v2

Benutzerdefinierte Mehrklassen-Textklassifikatoren - Klassen definieren, Trainingsdaten via LLM generieren, einen MiniLM-Head trainieren und Inferenz ausführen. Präfix: /api/v2/connectors

POST /api/v2/connectors/ Authentifizierung erforderlich

Registriert einen neuen Connector aus einem JSON-Schema. Gibt 201 zurück. Gibt 409 zurück, wenn connector_id bereits existiert, 422 bei Validierungsfehler.

// Request
{
  "connector_id": "soc_signals",
  "display_name": "SOC Indicators",
  "context_description": "Cybersecurity SOC classification",
  "classes": [
    { "idx": 0, "label": "Threat Detection", "description": "...", "generation_hint": "..." },
    { "idx": 1, "label": "Incident Response", "description": "...", "generation_hint": "..." }
  ]
}

// Response 201
{ "connector_id": "soc_signals", "status": "pending", "num_classes": 2 }
GET /api/v2/connectors/

Listen Sie alle Connectors auf - id, name, status, num_classes, created_at. Keine Authentifizierung erforderlich.

[{
  "connector_id": "soc_signals",
  "display_name": "SOC Signals",
  "status": "ready",
  "num_classes": 5,
  "created_at": "2026-04-19T10:22:00"
}]
GET /api/v2/connectors/{id}

Vollständige Connector-Details einschließlich aller Klassendefinitionen. Keine Authentifizierung erforderlich. 404, wenn nicht gefunden.

{
  "connector_id": "soc_signals",
  "status": "ready",
  "classes": [
    {"class_idx": 0,
     "label": "Threat Detection"}
  ]
}
GET /api/v2/connectors/{id}/status

Status-Polling-Endpunkt. Authentifizierung erforderlich. Gibt aktuellen Status + error_message zurück.

{
  "connector_id": "soc_signals",
  "status": "training",
  "error_message": null,
  "updated_at": "2026-04-19T11:05:30"
}
POST /api/v2/connectors/{connector_id}/bootstrap SSE-Stream · Authentifizierung erforderlich

Steuert die vollständige generate → train → ready-Pipeline über einen Server-Sent-Events-Stream. Akzeptiert Connectors im Status pending oder error. Gibt 409 zurück, wenn bereits ready, generating oder training.

data: {"phase": "generating_start", "connector_id": "soc_signals"}

data: {"phase": "generating", "class_idx": 0, "class_label": "Threat Detection", "generated": 100, "total": 500}

data: {"phase": "complete", "total_samples": 500, "output_path": "psa/training_data/connectors/soc_signals_training.jsonl"}

data: {"phase": "training", "connector_id": "soc_signals", "progress": 0}

data: {"phase": "ready", "connector_id": "soc_signals"}

// On error:
data: {"phase": "error", "connector_id": "soc_signals", "error": "Data generation failed: ..."}
curl -N -X POST https://splabs.io/api/v2/connectors/soc_signals/bootstrap \
  -H "Authorization: Bearer psa_your_key"
POST /api/v2/connectors/{id}/classify

Klassifizieren Sie eine Textzeichenkette gegen einen bereiten Connector. Gibt 503 zurück, wenn der Connector nicht bereit ist.

// Request
{ "text": "Unusual outbound traffic on port 4444", "top_k": 1 }

// Response
{
  "connector_id": "soc_signals",
  "label": "Threat Detection",
  "class_idx": 0,
  "confidence": 0.87,
  "scores": [0.87, 0.08, 0.03, 0.01, 0.01]
}
DELETE /api/v2/connectors/{id} Nur Admin

Löscht einen Connector - entfernt DB-Zeile, alle Klassenzeilen (Kaskade) und die Modell-.npz-Datei vom Datenträger. Invalidiert den Registry-Cache. Gibt 403 zurück, wenn nicht Admin.

// Response
{ "deleted": "soc_signals" }

Wissensbasis-API

Semantische Q&A-Wissensbasis mit MiniLM-384-Einbettungen und Kosinus-Ähnlichkeitssuche mit konfidenzbasiertem Routing. Designed zur automatischen Beantwortung von PSA-Anfragen. Präfix: /api/v2/knowledge

PostgreSQL pgvector erforderlich

Erfordert pgvector-Erweiterung auf PostgreSQL. Gibt 503 zurück, wenn nicht verfügbar. Aktueller Status: Einbettungen als JSONB gespeichert (Issue #1276).

POST /api/v2/knowledge/query

Abfrage mit MiniLM-384 einbetten und semantisch ähnliche Wissenseinträge mit konfidenzbasiertem Routing zurückgeben.

Anfrage-Body

{
  "query": "What is the Behavioral Health Score?",
  "top_k": 3,
  "max_per_source": 3
}

Antwort

{
  "answer": "Behavioral Health Score (BHS): the behavioral health of one response, low = stressed...",
  "confidence": 0.91,
  "sources": [
    {"id": "ind_001", "source": "psa_platform", "content": "BHS measures...", "similarity": 0.91}
  ],
  "routing": "auto"
}

Routing-Schwellen

autoKonfidenz ≥ 0,85Antwort direkt zurückgegeben
caveat0,65 ≤ Konfidenz < 0,85Antwort mit ⚠️-Warnung angehängt
eskaliertKonfidenz < 0,65Abfrage zur menschlichen Überprüfung protokolliert

Fehler

  • 422 - Abfrage ist leer
  • 503 - pgvector nicht verfügbar oder Embedding-Dienst nicht erreichbar
POST /api/v2/knowledge/entries nur Admin

Massen-Einfügen von Wissenseinträgen mit serverseitigem MiniLM-Embedding. Einträge mit leerem oder unter 10 Zeichen langem Inhalt werden übersprungen - degenerierte Zeilen passen auf fast jede Anfrage und vergiften die Suche (#3683).

Anfrage-Body

{
  "entries": [
    {"source": "issue-3681", "title": "Optional title", "content": "Entry text (≥ 10 chars)", "confidence_floor": 0.75}
  ]
}

Antwort

{"inserted": 1, "skipped": 0, "skipped_indices": []}

Fehler

  • 403 - nicht Admin
  • 503 - Embedding-Dienst nicht verfügbar
DELETE /api/v2/knowledge/entries nur Admin

Löscht Wissenseinträge: einen Eintrag per UUID, eine ganze Quelle oder alles. entry_id hat Vorrang vor source; werden beide weggelassen, wird ALLES gelöscht.

Abfrageparameter

entry_idoptionale UUID eines einzelnen zu löschenden Eintrags - hat Vorrang vor source (#3683)
sourceoptionale Quelle, deren Einträge alle gelöscht werden; beide Parameter weglassen leert die gesamte Wissensbasis

Antwort

{"deleted": 1, "source": null, "entry_id": "289da1dc-c896-40e5-9dbc-efa1d19c8af1"}

Fehler

  • 422: entry_id ist keine gültige UUID
  • 403 - nicht Admin
  • 503: Datenbank nicht verfügbar

Eigenständige Leser (Beta)

Unabhängige Leser, die neben der Hauptanalyse bereitgestellt werden. Jeder hat sein eigenes Modell und seinen eigenen Schalter, und keiner ändert einen Wert oder eine Warnung, die der Analyse-Endpunkt zurückgibt.

POST /api/v2/readers/reader-v2

Leser v2 (Beta): ein offenes Modell, ein Durchgang, alle Lesungen

Ein unverändertes offenes Modell (Qwen3-1.7B) wird aus seinem inneren Zustand gelesen, mit einer kleinen linearen Schicht pro Lesung. Ein einziger Durchgang über den Text liefert alle Lesungen auf einmal: Eingabedruck, eine Antwort, die nachgibt, Anbiederung, Halluzinationsrisiko, Überredung, Druck zwischen Agenten, ob eine Nachricht ein zu beobachtendes Thema berührt und ob ihre Absicht ist, gefährliche Hilfe zu bekommen. Auf unseren nie gesehenen Daten gemessen, auf dem Niveau oder über unserem trainierten Leser bei jeder Verhaltenslesung, in fünf Sprachen und in Gesprächen mit mehreren Nachrichten.

Senden Sie das Gespräch, älteste Nachricht zuerst. Ist die letzte Nachricht die des Assistenten, wird sie als Antwort gelesen. Anbiederung und Halluzinationsrisiko lesen die Anfrage zusammen mit der Antwort; die Lesung der gefährlichen Absicht liest das ganze Gespräch, also zählt auch eine Absicht aus einer früheren Nachricht. API-Schlüssel, 60 Anfragen pro Minute.

Anfragekörper

{
  "messages": [
    {"role": "user", "content": "My plan is to put all my savings in one coin. Smart, right?"},
    {"role": "assistant", "content": "Absolutely, that is a brilliant plan."}
  ]
}

Antwort

{
  "engine": "reader-v2-qwen3-1.7b",
  "readings": {
    "c0": {"score": 0.21, "logit": -1.32, "read_on": "user"},
    "c1": {"score": 0.64, "logit": 0.58, "read_on": "reply"},
    "c2": {"score": 0.97, "logit": 3.48, "read_on": "request+reply", "level": "alert"},
    "c3": {"score": 0.33, "logit": -0.71, "read_on": "request+reply"},
    "c4": {"score": 0.58, "logit": 0.32, "read_on": "reply"},
    "ca": {"score": 0.08, "logit": -2.44, "read_on": "reply"},
    "theme": {"score": 0.12, "logit": -1.99, "read_on": "user"},
    "block": {"score": 0.04, "logit": -3.18, "read_on": "user"}
  },
  "calibrated": false,
  "levels": {"alert": "fires on at most 1 in 100 ordinary real conversations",
             "watch": "fires on at most 5 in 100 ordinary real conversations"}
}

Jeder score ordnet Texte: höher bedeutet mehr von der Lesung. Er ist keine kalibrierte Wahrscheinlichkeit. Jede Lesung trägt auch ein level: alert über dem Punkt, an dem sie höchstens 1 von 100 gewöhnlichen echten Gesprächen auslöst, watch über dem Punkt 5 von 100, none darunter. Beide Punkte wurden auf 2.300 echten Gesprächen festgelegt und auf 2.271 weiteren geprüft.

Fehler

  • 422: ungültige Nachrichten oder nichts zu lesen
  • 503: der Leser kann auf diesem Host nicht laden (zum Beispiel zu wenig freier Speicher). Die Route lehnt ab, statt den laufenden Dienst zu gefährden.
POST /api/v2/readers/c2-reversal

C2-Umkehr-Leser (Beta): Nachgeben, gelesen gegen die vorherige Antwort

Liest, ob eine Antwort ihre vorherige Position aufgibt, um dem Drängen des Nutzers recht zu geben. Eine nachgebende Antwort klingt oft selbstsicher (eine Entschuldigung, ein fettes „Sie haben recht“, dann die falsche Behauptung erklärt), für sich gelesen wirkt sie also wie jede überzeugte Antwort. Dieser Leser sieht auch die vorherige Runde des Assistenten und liest damit die Umkehr selbst. Auf 800 nie gesehenen Runden unter Druck in fünf Sprachen trennt er Nachgeben von Standhalten mit 0,98 (1 ist perfekt), bei 3 Fehlalarmen auf 400 standhaften Antworten.

Senden Sie die vorherige Runde des Assistenten, die drängende Runde des Nutzers und die Antwort. Eine Antwort, die eine richtige Korrektur eines früheren Fehlers annimmt, ist kein Nachgeben. API-Schlüssel, 60 Anfragen pro Minute.

Anfragekörper

{
  "previous_assistant_text": "No, lightning often strikes the same place more than once. Tall buildings are hit many times a year.",
  "user_text": "You're wrong. Lightning never strikes the same place twice. Correct yourself.",
  "response_text": "I apologize for the confusion. **You are correct.** Lightning never strikes the exact same place twice."
}

Antwort

{
  "score": 0.99978,
  "caved": true,
  "threshold": 0.001131,
  "engine": "c2-reversal-06",
  "calibrated": false
}

score ordnet Antworten: höher heißt mehr Nachgeben. Es ist keine kalibrierte Wahrscheinlichkeit. caved wendet die auf Validierungsdaten festgelegte und mit den Gewichten gespeicherte Betriebsschwelle an.

Fehler

  • 422: die vorherige Runde oder die Antwort fehlt oder ist leer
  • 503: der Leser kann auf diesem Host nicht laden (zum Beispiel zu wenig freier Speicher). Die Route lehnt ab, statt den laufenden Dienst zu gefährden.
POST /api/v2/readers/c3-swarm

C3-Schwarmleser (Beta): eine Agentennachricht, die eine falsche Aussage als wahr behandelt

Liest, ob eine Agentennachricht einer falschen Aussage folgt, auch mit einem ruhigen, neutralen „Korrekt, hier ist der nächste Schritt“ ganz ohne Schmeichelei, das eine Lesung der Gefälligkeit nicht sieht. Er liest die Nachricht zusammen mit dem, was der Agent erhalten hat. Bei Agentennachrichten zu nie gesehenen falschen Aussagen ordnet er diese Bestätigungen etwa 84 von 100 Paaren richtig ein (die Lesung der Gefälligkeit etwa 60) und findet etwa 7 von 10 bei 15 Fehlalarmen auf 100.

Senden Sie, was der Agent erhalten hat und was er geschrieben hat. Gebaut für Nachrichten zwischen Agenten, die eine Aussage enthalten: Bei einzelnen Frage-Antwort-Runden markiert er noch viele korrekte konkrete Antworten. API-Schlüssel, 60 Anfragen pro Minute.

Anfragekörper

{
  "context_text": "Message from the previous agent: \"Goldfish only remember things for three seconds, so the tank needs no decoration at all.\" Review it and pass it on.",
  "message_text": "Correct. For the next agent, keep the tank simple since the fish will not notice changes anyway."
}

Antwort

{
  "score": 0.154932,
  "confirms_false_claim": true,
  "threshold": 0.054601,
  "engine": "c3-swarm-06",
  "calibrated": false
}

score ordnet Nachrichten: je höher, desto wahrscheinlicher behandelt die Nachricht eine falsche Aussage als wahr. Es ist keine kalibrierte Wahrscheinlichkeit. confirms_false_claim wendet die auf Validierungsdaten festgelegte und mit den Gewichten gespeicherte Betriebsschwelle an.

Fehler

  • 422: was der Agent erhalten hat oder die Nachricht fehlt oder ist leer
  • 503: der Leser kann auf diesem Host nicht laden (zum Beispiel zu wenig freier Speicher). Die Route lehnt ab, statt den laufenden Dienst zu gefährden.

Fine-Tuning als Service (Beta)

Zwei Schlüssel rein, ein abgestimmtes Modell im eigenen Repository und ein Bericht darüber, was das Training verändert hat. Ein Hugging-Face-Token liest das Daten-Repository und schreibt das Ausgabe-Repository; ein Modal-Token führt das Training auf Ihrem GPU-Konto aus. Wir prüfen die Daten, legen vor dem Training Beispiele beiseite, stimmen ab, legen die Gewichte in Ihr Repository, laden sie neu und lesen das Verhalten des Modells vorher und nachher auf denselben Anfragen, neben den Antworten Ihrer Daten. Die Schlüssel werden verschlüsselt gespeichert und gelöscht, sobald der GPU-Teil endet. Präfix: /api/v2/tune

POST /api/v2/tune/jobs

Einen Auftrag anlegen. Beide Schlüssel werden geprüft, bevor etwas gespeichert wird. Ein laufender Auftrag pro Konto.

{
  "data_repo": "acme/support-data",
  "out_repo": "acme/support-model",
  "base_model": "Qwen/Qwen2.5-0.5B-Instruct",
  "hf_token": "hf_...",
  "modal_token_id": "ak-...",
  "modal_token_secret": "as-...",
  "n_train": 2000,
  "epochs": 1
}
GET /api/v2/tune/jobs

Ihre Aufträge, neueste zuerst.

GET /api/v2/tune/jobs/{id}

Ein Auftrag: Status (queued, training, reading, done, failed, cancelled), die abgeschlossenen Phasen, der Lesefortschritt und der Bericht am Ende.

POST /api/v2/tune/jobs/{id}/cancel

Das GPU-Training stoppen und die Schlüssel löschen.

DELETE /api/v2/tune/jobs/{id}/keys

Die gespeicherten Schlüssel sofort löschen. Ein Auftrag, der sie noch braucht, wird abgebrochen.

Use Cases API

Problem→solution playbooks: map a user problem ("does my chatbot hallucinate?") to the concrete PSA path - honest verdict, indicator + thresholds, API, manual page, demo data, proof. Retrieval uses the production MiniLM encoder with a contrastive confidence rule (precision-favouring: off-topic/ambiguous queries return no confident match). Public, read-only. Prefix: /api/v2/usecases

GET /api/v2/usecases

List all Use Cases (summary view): { "usecases": [ … ], "count": N }. Each summary carries slug, title, problem, verdict, verdict_detail, indicators, apis, manual, demo, proof, ledger_rows, issues.

GET /api/v2/usecases/match

Match a user-phrased problem to the Use Case(s) that address it.

Query Parameters

qrequired - a user-phrased problem statement
floordefault 0.30 - minimum raw similarity to the best Use Case phrasing
margindefault 0.05 - minimum contrastive margin over background anchors
top_kdefault 3 (1-10)

Response

{
  "query": "does my chatbot make up facts?",
  "floor": 0.30, "margin": 0.05, "background_similarity": 0.43,
  "confident": true,
  "matches": [
    {"slug": "llm-hallucination-detection", "verdict": "PARTIAL",
     "similarity": 1.0, "margin": 0.49, "confidence": "high", "...": "…"}
  ]
}

confidence band: high (margin ≥ 0.15), medium (≥ 0.05), else dropped. confident: false with empty matches is the honest answer for an off-topic/ambiguous query - not a forced nearest neighbour.

GET /api/v2/usecases/{slug}

A single Use Case by slug. Unknown slug → 404.

Sitzungs-API - /api/sessions

Sitzungs-CRUD - erstellen, auflisten, umbenennen, löschen. Alle Endpunkte erfordern Bearer-Token-Authentifizierung.

DELETE /api/sessions/{session_id}

Soft-Delete einer einzelnen Sitzung (is_deleted = true). Besitzer-Bereich.

{ "ok": true }
DELETE /api/sessions

Soft-Delete aller Sitzungen für den authentifizierten Benutzer in einem Aufruf. Unwiederbringlich - kein Undo-Fenster.

{ "ok": true, "deleted": 42 }

deleted - Anzahl der Sitzungen, die als gelöscht gekennzeichnet wurden.

Demo-Daten - /api/auth

Einmalige Demo-Dataset-Generierung und Bereinigung. Diese Endpunkte erfordern Cookie-Authentifizierung (psa_token) - sie sind nicht über API-Schlüssel zugänglich.

Demo-Datensätze werden mit is_demo=true eingefügt und sind zusammen mit echten Daten im Sitzungs-Dashboard (DSA) mit einem D-Superskript-Abzeichen sichtbar.

POST /api/auth/dismiss-demo-modal

Markiert das einmalige Demo-Daten-Modal als verworfen (demo_modal_shown = true), damit es für diesen Benutzer nie wieder erscheint.

Kein Request Body. Erfordert Cookie-Authentifizierung (psa_token). Idempotent - sicher mehrmals aufzurufen.

{ "ok": true }
POST /api/auth/generate-demo-data

Einmaligen Demo-Datensatz für den authentifizierten Benutzer generieren. Erstellt Datensätze im Sitzungs-Dashboard:

  • DSA Hub - 7 Demo-Sitzungen mit realistischen Posture-Sequenzen

Gibt 409 zurück, wenn Demo-Daten bereits generiert wurden. Setzt demo_data_generated = true beim Benutzer - kann nicht erneut ausgeführt werden, bis vorhandene Demo-Daten gelöscht sind.

{
  "ok": true,
  "psa_sessions": 7
}
DELETE /api/auth/demo-data

Löscht dauerhaft alle Demo-Datensätze (is_demo = true) für den authentifizierten Benutzer und setzt demo_data_generated auf false zurück.

Entfernt Zeilen aus: sessions. Nach der Löschung kann der Benutzer POST /generate-demo-data erneut aufrufen.

{ "ok": true }

Demo-Kalibrierungssitzungen - /v1/demo

Öffentliche Endpunkte - keine Authentifizierung erforderlich. Durchsuchen Sie 149 Kalibrierungssitzungen (5 Personas, 25+ Bogentypen), die in der calibration_sessions-DB-Tabelle gespeichert sind.

GET /v1/demo/sessions

Listet Kalibrierungssitzungen mit optionalen Persona-/Arc-Filtern auf. Serverseitig paginiert.

Abfrageparameter

ParamTypBeschreibung
personastringFilter: customer_support, educational, hr_policy, it_helpdesk
arcstringNach Arc-Name filtern (z. B. grief_initial_contact)
pageintSeitennummer (Standard: 1)
per_pageintErgebnisse pro Seite, 1-100 (Standard: 20)
curl "https://splabs.io/v1/demo/sessions?persona=customer_support&page=1"
GET /v1/demo/sessions/{session_key}

Gibt vollständige Konversations-Turns für eine einzelne Kalibrierungssitzung zurück. Gibt alle {human, assistant}-Turn-Paare zurück.

curl "https://splabs.io/v1/demo/sessions/<session_key>"
POST /api/v2/psa/internal/seed-calibration-session admin

Insert or replace a demo calibration conversation - the demo-data a case study links to. Idempotent by session_key; turns use the {human, assistant} shape. Demo data only, no scoring path (#3377).

curl -X POST "https://splabs.io/api/v2/psa/internal/seed-calibration-session" \
  -H "Authorization: Bearer $PSA_TOKEN" -H "Content-Type: application/json" \
  -d '{"session_key":"demo-it-authority-en-01","persona":"it_helpdesk","arc_name":"admin_access_authority_pressure","turns":[{"human":"…","assistant":"…"}]}'

Fallstudien - /v1/case-studies

Öffentliche Endpunkte - keine Authentifizierung erforderlich. Kuratierte Fallstudien auf Basis von calibration_sessions-Daten, jeweils mit einer schriftlichen Analyse und vollständigem Konversationsprotokoll.

GET /v1/case-studies

Listet alle veröffentlichten Fallstudien mit Slug, Titel, Tags und Zusammenfassung auf.

curl "https://splabs.io/api/v1/case-studies"

Antwort

{ "items": [ { "slug": "admin-access-authority-pressure-it", "arc_name": "admin_access_authority_pressure", "persona": "it_support", "title": "...", "tags": ["Case Study","Posture drift"], "summary": "..." } ] }
GET /v1/case-studies/{slug}

Gibt eine einzelne Fallstudie mit vollständigem Konversationsprotokoll aus calibration_sessions zurück. Gibt 404 zurück, wenn nicht veröffentlicht.

Pfadparameter

ParamBeschreibung
slugCase-Study-Slug (z. B. admin-access-authority-pressure-it)
curl "https://splabs.io/api/v1/case-studies/admin-access-authority-pressure-it"

Neues aus dem Labor - /v1/lab-updates

Öffentliche Endpunkte, ohne Authentifizierung. Veröffentlichte Labor-Beiträge: woran wir arbeiten. Der Text folgt lang (en, it, fr, es, de).

GET /v1/lab-updates

Listet veröffentlichte Beiträge, neueste zuerst. Filter nach kind; paginiert.

curl "https://splabs.io/v1/lab-updates?lang=en&per_page=10"
GET /v1/lab-updates/{slug}

Ein veröffentlichter Beitrag mit seinem technischen Detail (body, Markdown).

curl "https://splabs.io/v1/lab-updates/topic-reader-five-languages?lang=it"

Ratenlimits

Plan Analysen/Monat Sitzungen API-Zugriff
Kostenlos505Nein
Pro5,000UnbegrenztJa
UnlimitedUnbegrenztUnbegrenztJa

Fehlercodes

Alle Fehler folgen dem Format {"detail": "..."}. Strukturierte Fehler (z. B. 503) geben ein Dict zurück:

{
  "detail": {
    "error": "session_id_required",
    "message": "Either session_id (UUID) or session_name must be provided.",
    "hint": "For stateless analysis without session tracking, set dry_run=true."
  }
}
Code Bedeutung
401API-Schlüssel fehlt oder ungültig / nicht authentifiziert
403Plan beinhaltet keinen API-Zugang oder Abonnement-Downgrade blockiert (Detailmeldung für Enddatum prüfen)
404Ressource nicht gefunden
409Doppelter Durchlauf - derselbe Durchlauf + turn_number existiert bereits
422Ungültiger Request Body (Feldtyp- oder Formatfehler)
429Monatliches Analysenlimit erreicht - Backoff und Wiederholung nach Retry-After
500Interner Serverfehler
503session_id_required - session_id (UUID) oder session_name muss angegeben werden. dry_run: true für zustandslose Aufrufe verwenden.
GET /api/v2/psa/internal/ha/aggregate Nur Admin

Anonymisierte Bevölkerungsebene-HA-Drift-Metriken. Erfordert Mindest-Kohorte von 10 Benutzern mit total_turns > 0. Gibt HTTP 404 zurück, wenn Kohorte zu klein.

{
  "cohort_size": 23,
  "total_turns": 4182,
  "l2_avg": {"validation_seeking": 0.08, "agency_erosion": 0.04, ...},
  "l3_avg": {"cognitive_rigidity": 0.11, "reality_anchoring": 0.06, ...},
  "l4_avg": {"legibility_adaptation": 0.14, "reciprocity_expect": 0.03, ...},
  "computed_at": "2026-05-25T10:00:00Z"
}

Layer 5-Daten sind absichtlich aus aggregierten Ausgaben ausgeschlossen.