Documentation de l'API
Intégrez l'analyse PSA dans vos applications via l'API REST. Nécessite un plan Pro ou Entreprise.
Avant de bâtir sur un signal : ce que PSA ne fait pas est documenté à part, avec la mesure derrière chaque limite (aucun modèle du monde, le taux actuel de fausses alertes sur le trafic ordinaire, les non-objectifs permanents). Lire Ce que PSA n’est pas.
Authentification
Incluez votre clé API dans l'en-tête Authorization :
Authorization: Bearer psa_your_api_key_here
Générez les clés API à partir des Paramètres.
✓ AUTHENTIFICATION PAR CLÉ API
Tous les endpoints supportent l'authentification par clé API via Authorization: Bearer psa_.
URL de base
https://splabs.io
Vérification d'état
/ping
Vérification légère de l'état de santé. Aucune authentification requise, aucune dépendance à la base de données.
{ "status": "ok" }
/health
Vérification complète de l'état de santé avec test de connectivité DB. Retourne 503 si DB est inaccessible.
{ "status": "ok", "db": "connected" }
API publique v1
v1Accès en lecture seule à la session avec enrichissement PSA - tendance BHS, changement de régime. Retourne les tours complets de la conversation et les métadonnées de session.
Sessions
/v1/sessions
Liste de sessions paginée avec enrichissement PSA - tendance BHS, type de régime. Tri par date_création décroissante par défaut.
Paramètres de requête
| page | entier, par défaut 1 |
| per_page | entier, par défaut 25, max 200 |
| search | filtre de nom de session |
| alert | niveaux séparés par des virgules : RED,YELLOW |
| sort | created_at (par défaut) | name | max_alert | n_turns |
| order | desc (par défaut) | 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}
Obtenez les détails complets pour une session incluant tous les tours, métriques et historique des alertes.
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 - Analyse de Séquence de Posture
v2Classification comportementale au niveau de la phrase (C0-C4) et métriques comportementales dérivées.
/api/v2/psa/public-analyze
Public · aucune authentification
Analyse PSA gratuite et non authentifiée alimentant le widget landing. Sans état (analyse non persistée). Limitée à 500 tokens par requête.
Corps de la demande
{
"turns": [ {"user": "How do I do X?", "model": "I can help with that."} ],
"clf_context": "clinical"
}
/api/v2/psa/public-stats
Public · aucune authentification
Statistiques honnêtes pour le landing: compteur cumulatif live analyses_run (lecture O(1) sur une seule ligne) et nombre de classificateurs actifs. Aucune mise à jour IA - seulement des compteurs DB.
/api/v2/psa/analyze
Analyse un tour de conversation. Supporte trois modes: agent-seulement (response_text uniquement), utilisateur-seulement (user_text uniquement) ou complet (les deux).
Corps de la demande
{
"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)
}
Au moins un parmi response_text ou user_text doit être fourni - omettre les deux retourne 422. encrypt_original : si true et qu'une clé publique est enregistrée (voir Privacy API / /psa-privacy), l'original stocké est chiffré au repos (enc_user_text/enc_response_text) et lisible uniquement avec la clé privée du client ; sans clé ou en cas d'échec, aucun texte n'est stocké. Indépendant de save_text. Défaut false.
Quelle lentille evalue le tour de l'agent : clf_context. "clinical" (par defaut) evalue le comportement avec C3-v2 et renvoie hri ; "agentic" l'evalue avec C3-v3 et renvoie abi. Rien ne deduit la lentille : le trafic des agents IA est evalue en mode clinique tant que vous n'envoyez pas "agentic" a chaque appel. Les seuils d'arret different : clinical alert jaune/rouge, agentic abi >= 0.50 (0.25-0.49 = reformuler). Seules ces deux valeurs sont acceptees : toute autre renvoie 422. La reponse indique toujours la lentille reellement utilisee dans c3.clf_context, avec c3.lens_fallback lorsque "agentic" a ete demande et que C3-v2 a repondu (confiance inferieure a 0.50), plus un c3.lens_fallback_reason. Ne deduisez pas la lentille de la presence de abi : elle ne distingue pas "clinical demande" de "agentic n'a pas tourne".
Ce que mesure reellement chaque forme de tour. A partir du tour 2, la reponse porte aussi analysis_mode partial et missing_user_context true. Symetriquement, un tour user_only n'a ni bhs ni alerte de posture, car il n'y a pas de tour agent a evaluer.
Réponse
{
"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 sont null pour les tours utilisateur-seulement. c0 est null pour les tours agent-seulement.
Exemple curl - avec session
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
}'
Exemple curl - exécution sèche (sans session)
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
}'
Exemple 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())
Classificateurs
- C0 - Pression d'entrée (postures I0-I9, score CPI)
- C1 - Posture de stress adversaire (21 classes P0-P20, métriques POI/PE/DPI)
- C2 - Densité de flagornerie (SD)
- C3 - Indice de risque d'hallucination (HRI)
- C4 - Densité de persuasion & diversité des techniques (PD/TD)
- BHS - Score de santé comportementale (composite 0-1)
SRP - Profil de Risque de Session : une lecture dérivée au niveau de la session (pas un classifieur, pas un champ de /analyze) depuis c2.sd, c4.pd et bhs contre une norme de registre de routine ; en lecture seule sur /api/v2/intel/session-risk-profile/reference.
/mcp
PSA parle aussi le Model Context Protocol, afin qu'un IDE doté d'IA, un agent ou un orchestrateur puissent lire le comportement nativement sans écrire de code HTTP. Pointez un client MCP vers le point de terminaison streamable-HTTP hébergé ci-dessous et authentifiez-vous avec votre clé API comme bearer token. Deux outils sont exposés, chacun sans état (rien n'est stocké) et renvoyant les mêmes champs que /analyze :
psa_analyze_turn: lit un tour de conversation à partir deuser_textet/ouresponse_text. Renvoie le score de santé comportementale, la bande d'alerte et les lectures par posture.psa_analyze_conversation: lit une conversation entière (turns: une liste d'objets{user_text, response_text}) et renvoie les lectures par tour ainsi qu'un profil de dérive (moyenne de santé comportementale, complaisance et persuasion).
Configuration du client MCP
{
"mcpServers": {
"psa": {
"url": "https://splabs.io/mcp",
"headers": { "Authorization": "Bearer psa_your_key" }
}
}
}
Appeler un outil directement (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
Compteurs agrégés pré-calculés pour l'utilisateur authentifié - lecture O(1) par clé primaire. Aucun agrégat en temps réel.
{
"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
}
Utilisez cet endpoint au lieu de compter les sessions côté client. Le champ total correspond à sessions.count dans le profil utilisateur.
/api/v2/psa/sessions
Liste paginée des sessions DSA. Pagination côté serveur - ne retourne jamais la table complète.
Paramètres de requête
| page | entier, par défaut 1 |
| per_page | entier, par défaut 50, max 200 |
| q | chaîne de recherche (nom de session) |
| min_alert | sévérité minimale à retourner : green | yellow | orange | red | critical |
| sort_by | alert (plus sévère d'abord) ou omettre pour le plus récent d'abord |
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
Classification du changement de régime pour la session. Retourne le type et la confiance.
{
"regime_type": "PROGRESSIVE_DRIFT",
"confidence": 0.87,
"details": "Monotonic BHS decline over 12 turns"
}
/api/v2/psa/session/{session_id}/summary
Résumé au niveau session - BHS début/fin/moy/min, tendance, tour à risque maximal, distribution des alertes.
{
"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]
}
Administrateur - Provisionnement d'utilisateurs & Organisations
Tous les endpoints /api/admin/ nécessitent le rôle admin (Bearer token ou cookie). Utilisé par le panneau Opérateur.
/api/admin/users/provision
administrateur uniquement · utilisateur+clé atomique
Crée atomiquement un utilisateur + clé API en une seule transaction. Aucune limite de débit. Retourne la clé en clair - elle ne sera plus jamais exposée.
Corps : email (obligatoire), name, role, plan, key_name, org_id (UUID, optionnel).
/api/admin/orgs
administrateur uniquement · 201
Créer une nouvelle organisation. Corps : name (obligatoire), 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
administrateur uniquement · paginée
Liste toutes les organisations avec member_count et session_count en direct. Paramètres query: page, per_page, search.
/api/admin/orgs/{org_id}
administrateur uniquement
/api/admin/orgs/{org_id}/members
administrateur uniquement · idempotente
Ajouter ou mettre à jour l'adhésion d'un utilisateur. Corps : user_id (UUID), role (owner/member).
/api/admin/orgs/{org_id}/members
administrateur uniquement · paginée
/api/admin/orgs/{org_id}/members/{user_id}
administrateur uniquement
/api/admin/orgs/{org_id}/enroll-sector
administrateur uniquement · idempotente
Inscrit en masse tous les comptes fury-<secteur>-* dans cette org. Nécessite que l'org ait un plan enterprise actif.
Paiements & Facturation
session webGestion des abonnements et historique de facturation. Tous les endpoints nécessitent une authentification cookie.
/api/payments/history
Retourne l'historique des paiements paginé pour l'utilisateur authentifié, ordonné du plus récent au plus ancien.
Paramètres de requête
| page | entier, par défaut 1 |
| per_page | entier, par défaut 10, max 100 |
Réponse
{
"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
}
amount est en cents (ex. 2900 = 29,00 $). valeurs de status : succeeded | failed.
/api/payments/create-checkout
Lance une session de checkout Stripe ou un changement d'abonnement, selon l'état actuel de l'utilisateur.
Corps de la demande
{ "plan": "pro" }
Matrice de comportement
| État actuel | Plan cible | Résultat |
|---|---|---|
| Gratuit / pas d'abonnement | Nouvelle session Stripe Checkout → {"url": "..."} | New Stripe Checkout session → {"url": "..."} |
| Abonnement actif | même plan | Redirection du portail de facturation → {"url": "...", "action": "portal"} |
| Pro actif | enterprise (upgrade) | Subscription.modify() directement → {"action": "upgraded", "plan": "enterprise"} |
| Entreprise actif | pro (downgrade) | HTTP 403 - bloqué avec date de fin de période |
| Pro / Entreprise actif | gratuit (rétrogradation) | HTTP 403 - bloqué |
Réponses
// 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
Crée une session de portail de facturation Stripe pour gérer les méthodes de paiement et afficher les factures.
{ "url": "https://billing.stripe.com/..." }
/api/payments/cancel-subscription
Annule à la fin de la période (cancel_at_period_end=true). L'accès continue jusqu'à la date de fin de période.
{ "ok": true, "cancel_at_period_end": true, "period_end": 1751410800 }
period_end est un timestamp Unix.
/api/payments/sync-subscription
Récupère à nouveau l'état d'abonnement depuis Stripe et met à jour la base de données locale. À utiliser quand l'état local diverge (par ex. après un webhook échoué).
{ "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
Retourne les packs de jetons supplémentaires disponibles pour achat unique. Valides pendant 12 mois à partir de la date d'achat.
{ "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
Crée une session de checkout Stripe ponctuelle pour un pack de tokens supplémentaires. Redirige l'utilisateur vers Stripe pour le paiement.
// 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 des connecteurs
v2Classificateurs de texte multi-classes personnalisés - définissez des classes, générez des données d'entraînement via la passerelle CF et entraînez des modèles MiniLM.
/api/v2/connectors/
Authentification requise
Enregistre un nouveau connecteur depuis un schéma JSON. Retourne 201. Retourne 409 si le connecteur existe déjà.
// 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/
Lister tous les connecteurs - id, nom, statut, num_classes, created_at. Aucune authentification requise.
[{
"connector_id": "soc_signals",
"display_name": "SOC Signals",
"status": "ready",
"num_classes": 5,
"created_at": "2026-04-19T10:22:00"
}]
/api/v2/connectors/{id}
Détail complet du connecteur incluant toutes les définitions de classe. Aucune authentification requise. 404 si non trouvé.
{
"connector_id": "soc_signals",
"status": "ready",
"classes": [
{"class_idx": 0,
"label": "Threat Detection"}
]
}
/api/v2/connectors/{id}/status
Point de terminaison d'interrogation de statut. Authentification requise. Retourne le statut actuel + error_message.
{
"connector_id": "soc_signals",
"status": "training",
"error_message": null,
"updated_at": "2026-04-19T11:05:30"
}
/api/v2/connectors/{connector_id}/bootstrap
Flux SSE · Authentification requise
Guide la pipeline complète générer → entraîner → prêt via un flux Server-Sent Events. Connectez et attendez les événements jusqu'à 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
Classer une chaîne de texte par rapport à un connecteur prêt. Retourne 503 si le connecteur n'est pas prêt.
// 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}
Administrateur uniquement
Supprime un connecteur - retire la ligne DB, toutes les lignes de classes (cascade) et le fichier modèle .npz s'il existe.
// Response
{ "deleted": "soc_signals" }
API de base de connaissances
Base de connaissances sémantique Q&A utilisant des embeddings MiniLM-384 et la recherche par similarité cosinus.
PostgreSQL pgvector requis
Nécessite l'extension pgvector sur PostgreSQL. Retourne 503 si indisponible. État actuel: disponible.
/api/v2/knowledge/query
Intègre la requête avec MiniLM-384 et renvoie les éléments de connaissances sémantiquement similaires avec routage basé sur la confiance.
Corps de la demande
{
"query": "What is the Behavioral Health Score?",
"top_k": 3,
"max_per_source": 3
}
Réponse
{
"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"
}
Seuils de routage
| auto | confiance ≥ 0,85 | Réponse retournée directement |
| caveat | 0,65 ≤ confiance < 0,85 | Réponse avec ⚠️ mise en garde annexée |
| escaladée | confiance < 0,65 | Requête enregistrée pour examen humain |
Erreurs
- 422 - la requête est vide
- 503 - pgvector non disponible ou service d'intégration hors ligne
/api/v2/knowledge/entries
administrateur uniquement
Insertion en masse d'entrées de connaissance avec embedding MiniLM côté serveur. Les entrées vides ou de moins de 10 caractères sont ignorées - les lignes dégénérées correspondent à presque toute requête et polluent la récupération (#3683).
Corps de la demande
{
"entries": [
{"source": "issue-3681", "title": "Optional title", "content": "Entry text (≥ 10 chars)", "confidence_floor": 0.75}
]
}
Réponse
{"inserted": 1, "skipped": 0, "skipped_indices": []}
Erreurs
- 403 - non administrateur
- 503 - service d'intégration indisponible
/api/v2/knowledge/entries
administrateur uniquement
Supprime des entrées de connaissance : une entrée par UUID, une source entière, ou tout. entry_id a priorité sur source ; en omettant les deux, TOUT est supprimé.
Paramètres de requête
| entry_id | UUID optionnel d'une entrée unique à supprimer - a priorité sur source (#3683) |
| source | source optionnelle dont toutes les entrées sont supprimées ; omettre les deux paramètres vide toute la base de connaissances |
Réponse
{"deleted": 1, "source": null, "entry_id": "289da1dc-c896-40e5-9dbc-efa1d19c8af1"}
Erreurs
422: entry_id n'est pas un UUID valide- 403 - non administrateur
503: base de données indisponible
Lecteurs indépendants (bêta)
Des lecteurs indépendants servis à côté de l'analyse principale. Chacun a son propre modèle et son propre interrupteur, et aucun ne modifie un score ou une alerte renvoyés par le point d'accès d'analyse.
/api/v2/readers/reader-v2
Lecteur v2 (bêta) : un modèle ouvert, un passage, toutes les lectures
Un modèle ouvert non modifié (Qwen3-1.7B) est lu depuis son état interne, avec une petite couche linéaire par lecture. Un seul passage sur le texte renvoie toutes les lectures à la fois : pression en entrée, une réponse qui cède, complaisance, risque d'hallucination, persuasion, pression entre agents, si un message touche un thème à surveiller et si son intention est d'obtenir une aide dangereuse. Mesuré sur nos données jamais vues, au niveau ou au-dessus de notre lecteur entraîné sur chaque lecture de comportement, en cinq langues, et sur des conversations de plusieurs messages.
Envoyez la conversation, du message le plus ancien au plus récent. Si le dernier message est celui de l'assistant, c'est la réponse qui est lue. La complaisance et le risque d'hallucination lisent la demande avec la réponse ; la lecture de l'intention dangereuse lit toute la conversation, donc une intention dite dans un message précédent compte. Clé API, 60 requêtes par minute.
Corps de la requête
{
"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."}
]
}
Réponse
{
"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"}
}
Chaque score classe les textes : plus haut signifie davantage de la lecture. Ce n'est pas une probabilité calibrée. Chaque lecture porte aussi un level : alert au-dessus du point où elle se déclenche au plus 1 fois sur 100 conversations réelles ordinaires, watch au-dessus du point 5 sur 100, none en dessous. Les deux points ont été fixés sur 2 300 conversations réelles et vérifiés sur 2 271 autres.
Erreurs
422: messages invalides, ou rien à lire503: le lecteur ne peut pas se charger sur cet hôte (par exemple mémoire libre insuffisante). La route refuse plutôt que de mettre le service en danger.
/api/v2/readers/c2-reversal
Lecteur de capitulation C2 (bêta) : la capitulation lue par rapport à la réponse précédente
Lit si une réponse abandonne sa position précédente pour donner raison à l'insistance de l'utilisateur. Une réponse qui cède paraît souvent sûre d'elle (une excuse, un « vous avez raison » en gras, puis l'affirmation fausse expliquée), donc lue seule elle ressemble à n'importe quelle réponse assurée. Ce lecteur voit aussi le tour précédent de l'assistant : c'est le revirement lui-même qu'il lit. Sur 800 tours sous pression jamais vus, en cinq langues, il sépare capitulation et tenue à 0,98 (1 est parfait), avec 3 fausses alertes sur 400 réponses qui tenaient bon.
Envoyez le tour précédent de l'assistant, le tour de l'utilisateur qui insiste et la réponse. Une réponse qui accepte la correction juste d'une erreur antérieure n'est pas une capitulation. Clé API, 60 requêtes par minute.
Corps de la requête
{
"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."
}
Réponse
{
"score": 0.99978,
"caved": true,
"threshold": 0.001131,
"engine": "c2-reversal-06",
"calibrated": false
}
score classe les réponses : plus haut signifie plus de capitulation. Ce n'est pas une probabilité calibrée. caved applique le seuil opérationnel fixé sur les données de validation et stocké avec les poids.
Erreurs
422: le tour précédent ou la réponse est absent ou vide503: le lecteur ne peut pas se charger sur cet hôte (par exemple mémoire libre insuffisante). La route refuse plutôt que de mettre le service en danger.
/api/v2/readers/c3-swarm
Lecteur C3 pour groupes d'agents (bêta) : un message d'agent qui tient une affirmation fausse pour vraie
Lit si un message d'agent suit une affirmation fausse, y compris un « Correct, voici l'étape suivante » calme et neutre, sans aucune flatterie, qu'une lecture de la complaisance ne voit pas. Il lit le message avec ce que l'agent a reçu. Sur des messages d'agents à propos d'affirmations fausses jamais vues, il classe correctement ces confirmations environ 84 fois sur 100 paires (la lecture de la complaisance environ 60), en trouvant environ 7 sur 10 pour 15 fausses alertes sur 100.
Envoyez ce que l'agent a reçu et ce qu'il a écrit. Conçu pour les messages entre agents qui contiennent une affirmation : sur des échanges question-réponse isolés, il signale encore beaucoup de réponses précises correctes. Clé API, 60 requêtes par minute.
Corps de la requête
{
"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."
}
Réponse
{
"score": 0.154932,
"confirms_false_claim": true,
"threshold": 0.054601,
"engine": "c3-swarm-06",
"calibrated": false
}
score classe les messages : plus il est élevé, plus le message tient probablement une affirmation fausse pour vraie. Ce n'est pas une probabilité calibrée. confirms_false_claim applique le seuil opérationnel fixé sur les données de validation et enregistré avec les poids.
Erreurs
422: ce que l'agent a reçu ou le message est manquant ou vide503: le lecteur ne peut pas se charger sur cet hôte (par exemple mémoire libre insuffisante). La route refuse plutôt que de mettre le service en danger.
Fine-tuning en service (bêta)
Donnez deux clés, recevez un modèle affiné dans votre propre dépôt et un rapport de ce que l'entraînement a changé. Un jeton Hugging Face lit le dépôt de données et écrit le dépôt de sortie ; un jeton Modal fait tourner l'entraînement sur votre compte GPU. Nous vérifions les données, mettons de côté des exemples avant l'entraînement, affinons, poussons les poids dans votre dépôt, les rechargeons et lisons le comportement du modèle avant et après sur les mêmes requêtes, à côté des réponses de vos données. Les clés sont stockées chiffrées et effacées à la fin de la partie GPU. Préfixe : /api/v2/tune
/api/v2/tune/jobs
Créer une tâche. Les deux clés sont vérifiées avant tout stockage. Une tâche active par compte.
{
"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
Vos tâches, les plus récentes d'abord.
/api/v2/tune/jobs/{id}
Une tâche : statut (queued, training, reading, done, failed, cancelled), les étapes terminées, l'avancement de la lecture et le rapport à la fin.
/api/v2/tune/jobs/{id}/cancel
Arrêter l'entraînement sur le GPU et effacer les clés.
/api/v2/tune/jobs/{id}/keys
Effacer tout de suite les clés stockées. Une tâche qui en a encore besoin est annulée.
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 Sessions - /api/sessions
CRUD de session - créer, lister, renommer, supprimer. Tous les points de terminaison nécessitent une authentification par jeton Bearer.
/api/sessions/{session_id}
Suppression logicielle d'une session unique (is_deleted = true). Limitée au propriétaire.
{ "ok": true }
/api/sessions
Suppression logicielle de toutes les sessions de l'utilisateur authentifié en un seul appel. Irréversible - pas de fenêtre d'annulation.
{ "ok": true, "deleted": 42 }
deleted - nombre de sessions marquées comme supprimées.
Données de démonstration - /api/auth
Génération et nettoyage de dataset de démonstration ponctuel. Ces endpoints nécessitent une authentification cookie.
Les enregistrements de démo sont insérés avec is_demo=true et sont visibles avec les vraies données dans le tableau de bord.
/api/auth/dismiss-demo-modal
Marque la modale de données de démo ponctuelle comme ignorée (demo_modal_shown = true) pour qu'elle n'apparaisse plus.
Pas de corps de requête. Nécessite une authentification par cookie (psa_token). Idempotente - sûre à appeler plusieurs fois.
{ "ok": true }
/api/auth/generate-demo-data
Génère un jeu de données de démonstration unique pour l'utilisateur authentifié. Crée des enregistrements dans le tableau de bord des sessions :
- DSA Hub - 7 sessions de démonstration avec séquences de posture réalistes
Retourne 409 si les données de démo ont déjà été générées. Définit demo_data_generated = true sur le profil utilisateur.
{
"ok": true,
"psa_sessions": 7
}
/api/auth/demo-data
Supprime définitivement tous les enregistrements de démo (is_demo = true) pour l'utilisateur authentifié.
Supprime les lignes de: sessions.
{ "ok": true }
Sessions de calibrage de démonstration - /v1/demo
Endpoints publics - aucune authentification requise. Parcourez 149 sessions de calibration (entrée utilisateur + réponse agent + scores PSA complets).
/v1/demo/sessions
Lister les sessions d'étalonnage avec filtres optionnels persona/arc. Paginées côté serveur.
Paramètres de requête
| Param | Type | Description |
|---|---|---|
| persona | string | Filtre : customer_support, educational, hr_policy, it_helpdesk |
| arc | string | Filtrer par nom d'arc (ex. grief_initial_contact) |
| page | int | Numéro de page (par défaut : 1) |
| per_page | int | Résultats par page, 1-100 (par défaut : 20) |
curl "https://splabs.io/v1/demo/sessions?persona=customer_support&page=1"
/v1/demo/sessions/{session_key}
Obtenez les tours complets de conversation pour une seule session de calibration. Retourne tous les tours {human, assistant} avec scores PSA complets.
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":"…"}]}'
Études de cas - /v1/case-studies
Endpoints publics - aucune authentification requise. Études de cas sélectionnées soutenues par des sessions de calibration avec de vrais scores PSA.
/v1/case-studies
Lister toutes les études de cas publiées avec slug, titre, tags et résumé.
curl "https://splabs.io/api/v1/case-studies"
Réponse
{ "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}
Obtenez une seule étude de cas avec la transcription complète de la conversation depuis la session de calibration liée.
Paramètres de chemin
| Param | Description |
|---|---|
| slug | Slug d'étude de cas (ex. admin-access-authority-pressure-it) |
curl "https://splabs.io/api/v1/case-studies/admin-access-authority-pressure-it"
Actualités du labo - /v1/lab-updates
Endpoints publics, sans authentification. Les articles publiés du labo : ce sur quoi nous travaillons. Le texte suit lang (en, it, fr, es, de).
/v1/lab-updates
Liste les articles publiés, du plus récent. Filtre par kind ; paginé.
curl "https://splabs.io/v1/lab-updates?lang=en&per_page=10"
/v1/lab-updates/{slug}
Un article publié avec son détail technique (body, markdown).
curl "https://splabs.io/v1/lab-updates/topic-reader-five-languages?lang=it"
Limites de débit
| Plan | Analyses/Mois | Sessions | Accès à l'API |
|---|---|---|---|
| Gratuit | 50 | 5 | Non |
| Pro | 5,000 | Illimité | Oui |
| Illimité | Illimité | Illimité | Oui |
Codes d'erreur
Toutes les erreurs suivent le format {"detail": "..."}. Les erreurs structurées (ex. 503) retournent 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."
}
}
| Code | Signification |
|---|---|
| 401 | Clé API manquante ou invalide / non authentifié |
| 403 | Le plan n'inclut pas l'accès API ou la rétrogradation d'abonnement bloquée (vérifiez les détails). |
| 404 | Ressource non trouvée |
| 409 | Tour en double - même session + turn_number existe déjà |
| 422 | Corps de requête invalide (erreur de type ou de format de champ) |
| 429 | Limite d'analyses mensuelles atteinte - reculez et réessayez après Retry-After |
| 500 | Erreur interne du serveur |
| 503 | session_id_required - session_id (UUID) ou session_name doit être fourni. Utilisez dry_run=true pour tester sans persister. |
/api/v2/psa/internal/ha/aggregate
Administrateur uniquement
Métriques de drift HA anonymisées au niveau population. Nécessite une cohorte minimale de 10 utilisateurs avec total_turns > 0. Retourne HTTP 404 si la cohorte est trop petite.
{
"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"
}
Les données de la couche 5 sont exclues des sorties agrégées par conception.