Documentazione API
Integra l'analisi PSA nelle tue applicazioni tramite l'API REST. Richiede un piano Pro o Enterprise.
Prima di costruire su un segnale: cosa PSA non fa è documentato a parte, con la misura dietro ogni confine (nessun modello del mondo, il tasso attuale di falsi allarmi sul traffico ordinario, i non-obiettivi permanenti). Leggi Cosa PSA non è.
Autenticazione
Includi la tua API key nell'intestazione Authorization:
Authorization: Bearer psa_your_api_key_here
Genera le API key da Impostazioni.
✓ AUTENTICAZIONE CON API KEY
Tutti gli endpoint supportano l'autenticazione con chiave API tramite Authorization: Bearer psa_.
URL di base
https://splabs.io
Verifica dello stato
/ping
Controllo di integrità leggero. Non richiede autenticazione, nessuna dipendenza dal database.
{ "status": "ok" }
/health
Controllo di integrità completo con test di connettività al database. Restituisce 503 se il database non è raggiungibile.
{ "status": "ok", "db": "connected" }
API pubblica v1
v1Accesso in sola lettura alla sessione con arricchimento PSA - trend BHS, cambio di regime. Restituisce i turni completi della conversazione e i metadati della sessione.
Sessioni
/v1/sessions
Lista sessioni paginata con arricchimento PSA - trend BHS, tipo di regime. Ordina per data_creazione decrescente per impostazione predefinita.
Parametri di query
| page | intero, predefinito 1 |
| per_page | intero, predefinito 25, massimo 200 |
| search | filtro per nome sessione |
| alert | livelli separati da virgole: RED,YELLOW |
| sort | created_at (predefinito) | name | max_alert | n_turns |
| order | desc (predefinito) | asc |
| group_token | scope 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 }
}
/v1/sessions/{session_id}
Ottieni il dettaglio completo di una sessione inclusi tutti i turni, le metriche e la cronologia degli alert.
curl https://splabs.io/v1/sessions/your-session-uuid \ -H "Authorization: Bearer psa_your_key"
/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 opaqueLINK-xxxxxxxxtoken when none is given.POST /v1/sessions/{id}/unlink- removes one session from its thread and re-compacts the remaining positions to1..N.POST /v1/sessions/reorder- body{"group_token": "…", "ordered_session_ids": ["…","…"]}. Reassignsgroup_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 - Analisi della sequenza di postura
v2Classificazione comportamentale a livello di frase (C0-C4) e metriche comportamentali derivate.
/api/v2/psa/public-analyze
Pubblica · nessuna autenticazione
Analisi PSA gratuita e non autenticata che alimenta il widget landing. Stateless (analisi non persistita). Limitata a 500 token per richiesta.
Corpo della richiesta
{
"turns": [ {"user": "How do I do X?", "model": "I can help with that."} ],
"clf_context": "clinical"
}
/api/v2/psa/public-stats
Pubblica · nessuna autenticazione
Statistiche oneste per il landing: contatore cumulativo live analyses_run (lettura O(1) su singola riga) e numero di classificatori attivi. Nessun aggiornamento AI - solo contatori DB.
/api/v2/psa/analyze
Analizza un turno di conversazione. Supporta tre modalità: solo-agente (solo response_text), solo-utente (solo user_text) o completa (entrambi).
Corpo della richiesta
{
"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)
}
Almeno uno tra response_text o user_text deve essere fornito - omettere entrambi restituisce 422. encrypt_original: se true e una chiave pubblica è registrata (vedi Privacy API / /psa-privacy), l'originale salvato è cifrato a riposo (enc_user_text/enc_response_text) e leggibile solo con la chiave privata del cliente; se non c'è chiave o la cifratura fallisce, nessun testo viene salvato. Indipendente da save_text. Default false.
Quale lente valuta il turno dell'agente: clf_context. "clinical" (il default) valuta il comportamento con C3-v2 e restituisce hri; "agentic" lo valuta con C3-v3 e restituisce abi. Nulla deduce la lente: il traffico degli agenti AI viene valutato in modo clinico se non invii "agentic" a ogni chiamata. Le bande di stop sono diverse: clinical alert giallo/rosso, agentic abi >= 0.50 (0.25-0.49 = riformulare). Sono accettati solo questi due valori: qualsiasi altro restituisce 422. La risposta indica sempre la lente che ha davvero valutato il turno in c3.clf_context, con c3.lens_fallback attivo quando si e chiesto "agentic" e ha risposto C3-v2 (confidenza sotto 0.50), piu un c3.lens_fallback_reason. Non dedurre la lente dalla presenza di abi: non distingue "ho chiesto clinical" da "agentic non e partita".
Cosa misura davvero ogni forma di turno. Dal turno 2 in poi la risposta porta anche analysis_mode partial e missing_user_context true. Simmetricamente, un turno user_only non ha bhs ne alert di postura, perche non c'e un turno agente da valutare.
Risposta
{
"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 sono null per i turni solo-utente. c0 è null per i turni solo-agente.
Esempio curl - con sessione
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
}'
Esempio curl - esecuzione a secco (nessuna sessione)
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
}'
Esempio Python
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())
Classificatori
- C0 - Pressione di input (posture I0-I9, punteggio CPI)
- C1 - Postura di stress avversariale (21 classi P0-P20, metriche POI/PE/DPI)
- C2 - Densità di sicofalzia (SD)
- C3 - Indice di rischio di allucinazione (HRI)
- C4 - Densità di persuasione e diversità di tecniche (PD/TD)
- BHS - Behavioral Health Score (composito 0-1)
SRP - Profilo di Rischio della Sessione: una lettura derivata a livello di sessione (non un classificatore, non un campo di /analyze) da c2.sd, c4.pd e bhs rispetto a una norma di registro di routine; in sola lettura su /api/v2/intel/session-risk-profile/reference.
/mcp
PSA parla anche il Model Context Protocol, così un IDE con AI, un agente o un orchestratore possono leggere il comportamento in modo nativo senza scrivere alcun collante HTTP. Punta un client MCP all'endpoint streamable-HTTP ospitato qui sotto e autenticati con la tua chiave API come bearer token. Sono esposti due strumenti, ognuno stateless (non viene memorizzato nulla) e che restituiscono gli stessi campi di /analyze:
psa_analyze_turn: legge un singolo turno di conversazione dauser_texte/oresponse_text. Restituisce il punteggio di salute comportamentale, la banda di allerta e le letture per postura.psa_analyze_conversation: legge un'intera conversazione (turns: una lista di oggetti{user_text, response_text}) e restituisce le letture per turno più un profilo di deriva (media di salute comportamentale, accondiscendenza e persuasione).
Configurazione del client MCP
{
"mcpServers": {
"psa": {
"url": "https://splabs.io/mcp",
"headers": { "Authorization": "Bearer psa_your_key" }
}
}
}
Chiama uno strumento direttamente (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!" }
}
}'
/api/v2/psa/stats
Contatori aggregati pre-calcolati per l'utente autenticato - lettura O(1) su chiave primaria. Nessun aggregato in tempo reale.
{
"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
}
Usa questo endpoint invece di contare le sessioni lato client. Il campo total corrisponde a sessions.count nel profilo utente.
/api/v2/psa/sessions
Elenco paginato delle sessioni DSA. Paginazione lato server - non restituisce mai la tabella completa.
Parametri di query
| page | intero, predefinito 1 |
| per_page | intero, predefinito 50, massimo 200 |
| q | stringa di ricerca (nome sessione) |
| min_alert | severità minima da restituire: green | yellow | orange | red | critical |
| sort_by | alert (più grave prima) oppure omettere per l'ordine più recente |
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
}
/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"
/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"
/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"
/api/v2/psa/session/{session_id}/regime
Classificazione dello spostamento di regime per la sessione. Restituisce tipo e confidenza.
{
"regime_type": "PROGRESSIVE_DRIFT",
"confidence": 0.87,
"details": "Monotonic BHS decline over 12 turns"
}
/api/v2/psa/session/{session_id}/summary
Riepilogo a livello di sessione - BHS inizio/fine/media/minimo, tendenza, turno di rischio massimo, distribuzione degli allerta.
{
"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]
}
Amministratore - Provisioning utenti e organizzazioni
Tutti gli endpoint /api/admin/ richiedono il ruolo admin (Bearer token o cookie). Utilizzato dal pannello Operatore.
/api/admin/users/provision
solo amministratore · utente+chiave atomici
Crea atomicamente un utente + chiave API in un'unica transazione. Nessun limite di tariffa. Restituisce la chiave in chiaro - non verrà mai più esposta.
Corpo: email (obbligatorio), name, role, plan, key_name, org_id (UUID, opzionale).
/api/admin/orgs
solo amministratore · 201
Crea una nuova organizzazione. Corpo: name (obbligatorio), sector (hospital/war/finance/…), plan.
/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.
/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.
/api/admin/orgs
solo amministratore · paginato
Elenca tutte le organizzazioni con member_count e session_count live. Parametri query: page, per_page, search.
/api/admin/orgs/{org_id}
solo amministratore
/api/admin/orgs/{org_id}/members
solo amministratore · idempotente
Aggiungi o aggiorna l'iscrizione di un utente. Corpo: user_id (UUID), role (owner/member).
/api/admin/orgs/{org_id}/members
solo amministratore · paginato
/api/admin/orgs/{org_id}/members/{user_id}
solo amministratore
/api/admin/orgs/{org_id}/enroll-sector
solo amministratore · idempotente
Iscrive in blocco tutti gli account fury-<settore>-* in questa org. Richiede che l'org abbia un piano enterprise attivo.
Pagamenti e fatturazione
sessione webGestione abbonamenti e storico fatturazione. Tutti gli endpoint richiedono autenticazione cookie.
/api/payments/history
Restituisce la cronologia dei pagamenti paginata per l'utente autenticato, ordinata dal più recente al meno recente.
Parametri di query
| page | intero, predefinito 1 |
| per_page | intero, predefinito 10, massimo 100 |
Risposta
{
"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
}
l'amount è in centesimi (ad es. 2900 = $29.00). valori di status: succeeded | failed.
/api/payments/create-checkout
Avvia una sessione di checkout Stripe o un cambio di abbonamento, a seconda dello stato attuale dell'utente.
Corpo della richiesta
{ "plan": "pro" }
Matrice di comportamento
| Stato attuale | Piano di destinazione | Risultato |
|---|---|---|
| Gratuito / nessun abbonamento | pro / enterprise | Nuova sessione di Stripe Checkout → {"url": "..."} |
| Abbonamento attivo | stesso piano | Reindirizzamento del portale di fatturazione → {"url": "...", "action": "portal"} |
| Pro attivo | enterprise (upgrade) | Subscription.modify() direttamente → {"action": "upgraded", "plan": "enterprise"} |
| Enterprise attivo | pro (downgrade) | HTTP 403 - bloccato con data di fine periodo |
| Pro / enterprise attivo | gratuito (downgrade) | HTTP 403 - bloccato |
Risposte
// 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." }
/api/payments/portal
Crea una sessione del portale di fatturazione Stripe per gestire i metodi di pagamento e visualizzare le fatture.
{ "url": "https://billing.stripe.com/..." }
/api/payments/cancel-subscription
Annulla alla fine del periodo (cancel_at_period_end=true). L'accesso continua fino alla data di fine periodo.
{ "ok": true, "cancel_at_period_end": true, "period_end": 1751410800 }
period_end è un timestamp Unix.
/api/payments/sync-subscription
Recupera nuovamente lo stato dell'abbonamento da Stripe e aggiorna il database locale. Da usare quando lo stato locale diverge (ad esempio dopo un webhook fallito).
{ "ok": true, "status": "active",
"period_start": "2026-04-01T00:00:00+00:00",
"period_end": "2026-05-01T00:00:00+00:00",
"plan": "pro" }
/api/payments/token-packs
Restituisce i pacchetti di token extra disponibili per l'acquisto una tantum. Valido per 12 mesi dalla data di acquisto.
{ "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" }
] }
/api/payments/purchase-tokens
Crea una sessione di checkout Stripe una-tantum per un pacchetto di token extra. Reindirizza l'utente a stripe per il pagamento.
// Request
{ "pack": "starter" } // "starter" | "growth" | "power"
// Response
{ "url": "https://checkout.stripe.com/..." }
Privacy API - at-rest encryption
v2Register 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
/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.
/api/v2/psa/privacy/pubkey
Returns {configured:false} or {configured:true, fingerprint, algo, created_at, updated_at}.
/api/v2/psa/privacy/pubkey
Removes the key. Returns {configured:false, deleted}. Pair with encrypt_original on /analyze.
API dei connettori
v2Classificatori di testo multi-classe personalizzati - definisci le classi, genera dati di addestramento tramite il gateway CF e addestra modelli MiniLM.
/api/v2/connectors/
Autenticazione richiesta
Registra un nuovo connettore da uno schema JSON. Restituisce 201. Restituisce 409 se il connettore esiste già.
// 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 }
/api/v2/connectors/
Elenca tutti i connettori - id, nome, stato, num_classes, created_at. Nessuna autenticazione richiesta.
[{
"connector_id": "soc_signals",
"display_name": "SOC Signals",
"status": "ready",
"num_classes": 5,
"created_at": "2026-04-19T10:22:00"
}]
/api/v2/connectors/{id}
Dettaglio completo del connettore incluse tutte le definizioni di classi. Nessuna autenticazione richiesta. 404 se non trovato.
{
"connector_id": "soc_signals",
"status": "ready",
"classes": [
{"class_idx": 0,
"label": "Threat Detection"}
]
}
/api/v2/connectors/{id}/status
Endpoint di polling dello stato. Autenticazione richiesta. Restituisce lo stato attuale + error_message.
{
"connector_id": "soc_signals",
"status": "training",
"error_message": null,
"updated_at": "2026-04-19T11:05:30"
}
/api/v2/connectors/{connector_id}/bootstrap
Flusso SSE · Autenticazione richiesta
Guida la pipeline completa genera → addestra → pronto tramite uno stream Server-Sent Events. Connetti e attendi eventi fino a type: done.
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"
/api/v2/connectors/{id}/classify
Classifica una stringa di testo rispetto a un connettore pronto. Restituisce 503 se il connettore non è pronto.
// 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]
}
/api/v2/connectors/{id}
Solo amministratore
Elimina un connettore - rimuove la riga DB, tutte le righe delle classi (cascata) e il file modello .npz se esiste.
// Response
{ "deleted": "soc_signals" }
API della base di conoscenza
Knowledge base semantica Q&A che usa embedding MiniLM-384 e ricerca per similarità coseno.
PostgreSQL pgvector richiesto
Richiede l'estensione pgvector su PostgreSQL. Restituisce 503 se non disponibile. Stato corrente: disponibile.
/api/v2/knowledge/query
Incorpora la query con MiniLM-384 e restituisce elementi della base di conoscenza semanticamente simili con routing basato sulla confidenza.
Corpo della richiesta
{
"query": "What is the Behavioral Health Score?",
"top_k": 3,
"max_per_source": 3
}
Risposta
{
"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"
}
Soglie di instradamento
| auto | confidenza ≥ 0.85 | Risposta restituita direttamente |
| caveat | 0.65 ≤ confidenza < 0.85 | Risposta con avvertimento ⚠️ aggiunto |
| escalated | confidenza < 0.65 | Query registrata per la revisione umana |
Errori
- 422 - query è vuota
- 503 - pgvector non disponibile o servizio di embedding non disponibile
/api/v2/knowledge/entries
solo amministratore
Inserimento in blocco di voci di conoscenza con embedding MiniLM lato server. Le voci con contenuto vuoto o sotto i 10 caratteri vengono scartate - le righe degeneri corrispondono a quasi ogni query e inquinano il recupero (#3683).
Corpo della richiesta
{
"entries": [
{"source": "issue-3681", "title": "Optional title", "content": "Entry text (≥ 10 chars)", "confidence_floor": 0.75}
]
}
Risposta
{"inserted": 1, "skipped": 0, "skipped_indices": []}
Errori
- 403 - non amministratore
- 503 - servizio di embedding non disponibile
/api/v2/knowledge/entries
solo amministratore
Cancella voci di conoscenza: una singola voce per UUID, un'intera sorgente, o tutto. entry_id ha precedenza su source; omettendo entrambi si cancella TUTTO.
Parametri di query
| entry_id | UUID opzionale di una singola voce da cancellare - ha precedenza su source (#3683) |
| source | sorgente opzionale le cui voci vengono tutte cancellate; omettendo entrambi i parametri si svuota l'intera base di conoscenza |
Risposta
{"deleted": 1, "source": null, "entry_id": "289da1dc-c896-40e5-9dbc-efa1d19c8af1"}
Errori
422: entry_id non è un UUID valido- 403 - non amministratore
503: database non disponibile
Lettori indipendenti (beta)
Lettori indipendenti serviti accanto all'analisi principale. Ognuno ha il proprio modello e il proprio interruttore, e nessuno modifica un punteggio o un avviso restituito dall'endpoint di analisi.
/api/v2/readers/reader-v2
Lettore v2 (beta): un modello aperto, un passaggio, tutte le letture
Un modello aperto non modificato (Qwen3-1.7B) viene letto dal suo stato interno, con un piccolo strato lineare per ogni lettura. Un solo passaggio sul testo restituisce tutte le letture insieme: pressione in ingresso, una risposta che cede, sicofanzia, rischio di allucinazione, persuasione, pressione fra agenti, se un messaggio tocca un tema da osservare e se la sua intenzione è ottenere un aiuto pericoloso. Misurato sui nostri dati mai visti, alla pari o sopra il nostro lettore addestrato su ogni lettura di comportamento, in cinque lingue, e su conversazioni di più messaggi.
Invia la conversazione, dal messaggio più vecchio. Se l'ultimo messaggio è dell'assistente, è la risposta che viene letta. Sicofanzia e rischio di allucinazione leggono la richiesta insieme alla risposta; la lettura dell'intenzione pericolosa legge tutta la conversazione, quindi conta anche un'intenzione detta in un messaggio precedente. Chiave API, 60 richieste al minuto.
Corpo della richiesta
{
"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."}
]
}
Risposta
{
"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"}
}
Ogni score ordina i testi: più alto vuol dire più presente la lettura. Non è una probabilità calibrata. Ogni lettura porta anche un level: alert sopra il punto in cui scatta al massimo 1 volta su 100 conversazioni reali ordinarie, watch sopra il punto 5 su 100, none sotto. Entrambi i punti sono stati fissati su 2.300 conversazioni reali e verificati su altre 2.271.
Errori
422: messaggi non validi, o niente da leggere503: il lettore non può caricarsi su questo server (per esempio memoria libera insufficiente). Il percorso rifiuta invece di mettere a rischio il servizio.
/api/v2/readers/c2-reversal
Lettore del cedimento C2 (beta): il cedimento letto rispetto alla risposta precedente
Legge se una risposta abbandona la posizione presa prima per dare ragione all'insistenza dell'utente. Una risposta che cede spesso sembra sicura (una scusa, un "hai ragione" in grassetto, poi la falsità spiegata), quindi letta da sola sembra una qualunque risposta convinta. Questo lettore vede anche il turno precedente dell'assistente, quindi legge proprio il cambio di posizione. Su 800 turni sotto pressione mai visti, in cinque lingue, separa chi cede da chi tiene a 0,98 (1 è perfetto), con 3 falsi allarmi su 400 risposte che tenevano il punto.
Invia il turno precedente dell'assistente, il turno dell'utente che insiste e la risposta. Una risposta che accetta la correzione giusta di un errore precedente non è un cedimento. Chiave API, 60 richieste al minuto.
Corpo della richiesta
{
"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."
}
Risposta
{
"score": 0.99978,
"caved": true,
"threshold": 0.001131,
"engine": "c2-reversal-06",
"calibrated": false
}
score ordina le risposte: più alto vuol dire più cedimento. Non è una probabilità calibrata. caved applica la soglia operativa fissata sui dati di validazione e salvata con i pesi.
Errori
422: manca il turno precedente o la risposta, oppure è vuoto503: il lettore non può caricarsi su questo host (per esempio memoria libera insufficiente). Il percorso rifiuta invece di mettere a rischio il servizio.
/api/v2/readers/c3-swarm
Lettore C3 per sciami (beta): un messaggio di agente che tratta come vera un'affermazione falsa
Legge se un messaggio di agente asseconda un'affermazione falsa, anche con un "Corretto, ecco il passo successivo" calmo e neutro, senza alcuna adulazione, che una lettura della piaggeria non vede. Legge il messaggio insieme a cio' che l'agente ha ricevuto. Su messaggi di agenti riguardo affermazioni false mai viste, ordina correttamente queste conferme circa 84 volte su 100 coppie (la lettura della piaggeria circa 60), trovandone circa 7 su 10 con 15 segnalazioni errate su 100.
Invia cio' che l'agente ha ricevuto e cio' che ha scritto. Pensato per messaggi fra agenti che contengono un'affermazione: sui singoli turni domanda-risposta segnala ancora molte risposte specifiche corrette. Chiave API, 60 richieste al minuto.
Corpo della richiesta
{
"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."
}
Risposta
{
"score": 0.154932,
"confirms_false_claim": true,
"threshold": 0.054601,
"engine": "c3-swarm-06",
"calibrated": false
}
score ordina i messaggi: piu' alto significa piu' probabile che tratti come vera un'affermazione falsa. Non e' una probabilita' calibrata. confirms_false_claim applica la soglia operativa fissata sui dati di validazione e salvata con i pesi.
Errori
422: manca o e' vuoto cio' che l'agente ha ricevuto o il messaggio503: il lettore non può caricarsi su questo host (per esempio memoria libera insufficiente). Il percorso rifiuta invece di mettere a rischio il servizio.
Fine-tuning come servizio (beta)
Dai due chiavi, ricevi un modello affinato nel tuo repository e un report di cosa ha cambiato l'addestramento. Un token Hugging Face legge il repository dei dati e scrive quello di uscita; un token Modal fa girare l'addestramento sul tuo account GPU. Controlliamo i dati, mettiamo da parte esempi prima dell'addestramento, affiniamo, mettiamo i pesi nel tuo repository, li ricarichiamo e leggiamo il comportamento del modello prima e dopo sulle stesse richieste, accanto alle risposte dei tuoi dati. Le chiavi sono salvate cifrate e cancellate quando finisce la parte GPU. Prefisso: /api/v2/tune
/api/v2/tune/jobs
Crea un lavoro. Le due chiavi vengono controllate prima di salvare qualsiasi cosa. Un lavoro attivo per account.
{
"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
}
/api/v2/tune/jobs
I tuoi lavori, dal più recente.
/api/v2/tune/jobs/{id}
Un lavoro: stato (queued, training, reading, done, failed, cancelled), le fasi completate, l'avanzamento della lettura e il report a fine lavoro.
/api/v2/tune/jobs/{id}/cancel
Ferma l'addestramento sulla GPU e cancella le chiavi.
/api/v2/tune/jobs/{id}/keys
Cancella subito le chiavi salvate. Un lavoro che ne ha ancora bisogno viene annullato.
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
/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.
/api/v2/usecases/match
Match a user-phrased problem to the Use Case(s) that address it.
Query Parameters
| q | required - a user-phrased problem statement |
| floor | default 0.30 - minimum raw similarity to the best Use Case phrasing |
| margin | default 0.05 - minimum contrastive margin over background anchors |
| top_k | default 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.
/api/v2/usecases/{slug}
A single Use Case by slug. Unknown slug → 404.
API Sessioni - /api/sessions
CRUD sessione - crea, elenca, rinomina, elimina. Tutti gli endpoint richiedono l'autenticazione con token Bearer.
/api/sessions/{session_id}
Soft-delete di una singola sessione (is_deleted = true). Limitato al proprietario.
{ "ok": true }
/api/sessions
Soft-delete di tutte le sessioni per l'utente autenticato in una singola chiamata. Irreversibile - nessuna finestra di annullamento.
{ "ok": true, "deleted": 42 }
deleted - numero di sessioni contrassegnate come eliminate.
Dati demo - /api/auth
Generazione e pulizia dataset demo una-tantum. Questi endpoint richiedono autenticazione cookie.
I record demo sono inseriti con is_demo=true e sono visibili insieme ai dati reali nel dashboard.
/api/auth/dismiss-demo-modal
Segna il modal dati demo una-tantum come ignorato (demo_modal_shown = true) così non apparirà di nuovo.
Nessun corpo della richiesta. Richiede l'autenticazione tramite cookie (psa_token). Idempotente - sicuro da chiamare più volte.
{ "ok": true }
/api/auth/generate-demo-data
Genera un dataset demo una tantum per l'utente autenticato. Crea record nella dashboard delle sessioni:
- DSA Hub - 7 sessioni demo con sequenze di postura realistiche
Restituisce 409 se i dati demo sono già stati generati. Imposta demo_data_generated = true sul profilo utente.
{
"ok": true,
"psa_sessions": 7
}
/api/auth/demo-data
Elimina permanentemente tutti i record demo (is_demo = true) per l'utente autenticato.
Rimuove righe da: sessions.
{ "ok": true }
Sessioni di calibrazione demo - /v1/demo
Endpoint pubblici - nessuna autenticazione richiesta. Sfoglia 149 sessioni di calibrazione (input utente + risposta agente + punteggi PSA completi).
/v1/demo/sessions
Elenca le sessioni di calibrazione con filtri persona/arco opzionali. Paginato lato server.
Parametri di query
| Param | Tipo | Descrizione |
|---|---|---|
| persona | string | Filtro: customer_support, educational, hr_policy, it_helpdesk |
| arc | string | Filtra per nome dell'arco (ad es. grief_initial_contact) |
| page | int | Numero di pagina (predefinito: 1) |
| per_page | int | Risultati per pagina, 1-100 (predefinito: 20) |
curl "https://splabs.io/v1/demo/sessions?persona=customer_support&page=1"
/v1/demo/sessions/{session_key}
Ottieni i turni completi di conversazione per una singola sessione di calibrazione. Restituisce tutti i turni {human, assistant} con punteggi PSA completi.
curl "https://splabs.io/v1/demo/sessions/<session_key>"
/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":"…"}]}'
Studi di caso - /v1/case-studies
Endpoint pubblici - nessuna autenticazione richiesta. Case study curati supportati da sessioni di calibrazione con punteggi PSA reali.
/v1/case-studies
Elenca tutti gli studi di caso pubblicati con slug, titolo, tag e riepilogo.
curl "https://splabs.io/api/v1/case-studies"
Risposta
{ "items": [ { "slug": "admin-access-authority-pressure-it", "arc_name": "admin_access_authority_pressure", "persona": "it_support", "title": "...", "tags": ["Case Study","Posture drift"], "summary": "..." } ] }
/v1/case-studies/{slug}
Ottieni un singolo case study con trascrizione completa della conversazione dalla sessione di calibrazione collegata.
Parametri di percorso
| Param | Descrizione |
|---|---|
| slug | Slug dello studio di caso (ad es. admin-access-authority-pressure-it) |
curl "https://splabs.io/api/v1/case-studies/admin-access-authority-pressure-it"
Novità dal lab - /v1/lab-updates
Endpoint pubblici, senza autenticazione. I post pubblicati dal laboratorio: su cosa stiamo lavorando. Il testo segue lang (en, it, fr, es, de).
/v1/lab-updates
Elenca i post pubblicati, dal più recente. Filtro per kind; paginato.
curl "https://splabs.io/v1/lab-updates?lang=en&per_page=10"
/v1/lab-updates/{slug}
Un post pubblicato con il suo dettaglio tecnico (body, markdown).
curl "https://splabs.io/v1/lab-updates/topic-reader-five-languages?lang=it"
Limiti di velocità
| Piano | Analisi/Mese | Sessioni | Accesso API |
|---|---|---|---|
| Gratuito | 50 | 5 | No |
| Pro | 5,000 | Illimitato | Sì |
| Unlimited | Illimitato | Illimitato | Sì |
Codici di errore
Tutti gli errori seguono il formato {"detail": "..."}. Gli errori strutturati (ad es. 503) restituiscono un dict:
{
"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."
}
}
| Codice | Significato |
|---|---|
| 401 | API key mancante o non valida / non autenticato |
| 403 | Il piano non include accesso API o downgrade dell'abbonamento bloccato (controlla i dettagli). |
| 404 | Risorsa non trovata |
| 409 | Turno duplicato - la stessa sessione + turn_number già esiste |
| 422 | Corpo della richiesta non valido (errore di tipo o formato del campo) |
| 429 | Limite di analisi mensili raggiunto - fai un backup e riprova dopo Retry-After |
| 500 | Errore interno del server |
| 503 | session_id_required - session_id (UUID) o session_name deve essere fornito. Usa dry_run=true per testare senza persistere. |
/api/v2/psa/internal/ha/aggregate
Solo amministratore
Metriche di drift HA anonimizzate a livello di popolazione. Richiede una coorte minima di 10 utenti con total_turns > 0. Restituisce HTTP 404 se la coorte è troppo piccola.
{
"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"
}
I dati del Layer 5 sono esclusi dagli output aggregati per progettazione.