Documentación de API
Integre análisis de PSA en sus aplicaciones a través de la API REST. Requiere un plan Pro o Enterprise.
Antes de construir sobre una señal: lo que PSA no hace está documentado aparte, con la medición detrás de cada límite (sin modelo del mundo, la tasa actual de falsas alarmas en tráfico ordinario, los no-objetivos permanentes). Lea Lo que PSA no es.
Autenticación
Incluya su clave API en el encabezado Autorización:
Authorization: Bearer psa_your_api_key_here
Genere claves API desde Configuración.
✓ AUTENTICACIÓN POR CLAVE API
Todos los endpoints soportan autenticación con clave API mediante Authorization: Bearer psa_.
URL base
https://splabs.io
Estado
/ping
Verificación de salud simple. No requiere autenticación, sin dependencia de base de datos.
{ "status": "ok" }
/health
Verificación de salud completa con prueba de conectividad de BD. Devuelve 503 si BD no es accesible.
{ "status": "ok", "db": "connected" }
API pública v1
v1Acceso de solo lectura a la sesión con enriquecimiento PSA - tendencia BHS, cambio de régimen. Devuelve los turnos completos de la conversación y los metadatos de sesión.
Sesiones
/v1/sessions
Lista de sesiones paginada con enriquecimiento PSA - tendencia BHS, tipo de régimen. Ordena por fecha_creación descendente por defecto.
Parámetros de consulta
| página | entero, predeterminado 1 |
| por_página | entero, predeterminado 25, máximo 200 |
| búsqueda | filtro de nombre de sesión |
| alerta | niveles separados por comas: RED,YELLOW |
| ordenar | created_at (predeterminado) | name | max_alert | n_turns |
| orden | desc (predeterminado) | 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}
Obtener detalle completo de una sesión, incluidos todos los turnos, métricas e historial de alertas.
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 - Análisis de Secuencia de Postura
v2Clasificación conductual a nivel de oración (C0-C4) y métricas conductuales derivadas.
/api/v2/psa/public-analyze
Público · sin autenticación
Análisis PSA gratuito y no autenticado que alimenta el widget del landing. Sin estado (análisis no persistido). Limitado a 500 tokens por solicitud.
Cuerpo de solicitud
{
"turns": [ {"user": "How do I do X?", "model": "I can help with that."} ],
"clf_context": "clinical"
}
/api/v2/psa/public-stats
Público · sin autenticación
Estadísticas honestas para el landing: contador acumulativo en vivo analyses_run (lectura O(1) en una sola fila) y número de clasificadores activos. Sin actualizaciones de IA - solo contadores de DB.
/api/v2/psa/analyze
Analiza un turno de conversación. Soporta tres modos: solo-agente (solo response_text), solo-usuario (solo user_text) o completo (ambos).
Cuerpo de solicitud
{
"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)
}
Al menos uno de response_text o user_text debe ser proporcionado - omitir ambos devuelve 422. encrypt_original: si es true y hay una clave pública registrada (ver Privacy API / /psa-privacy), el original almacenado se cifra en reposo (enc_user_text/enc_response_text) y solo se puede leer con la clave privada del cliente; si no hay clave o falla el cifrado, no se almacena texto. Independiente de save_text. Por defecto false.
Que lente evalua el turno del agente: clf_context. "clinical" (por defecto) evalua el comportamiento con C3-v2 y devuelve hri; "agentic" lo evalua con C3-v3 y devuelve abi. Nada deduce la lente: el trafico de agentes de IA se evalua en modo clinico salvo que envies "agentic" en cada llamada. Las bandas de parada son distintas: clinical alert amarillo/rojo, agentic abi >= 0.50 (0.25-0.49 = reformular). Solo se aceptan estos dos valores: cualquier otro devuelve 422. La respuesta siempre indica la lente que realmente evaluo el turno en c3.clf_context, con c3.lens_fallback cuando se pidio "agentic" y respondio C3-v2 (confianza por debajo de 0.50), mas un c3.lens_fallback_reason. No deduzcas la lente de la presencia de abi: no distingue "se pidio clinical" de "agentic no se ejecuto".
Que mide realmente cada forma de turno. Desde el turno 2 en adelante la respuesta tambien lleva analysis_mode partial y missing_user_context true. Simetricamente, un turno user_only no tiene bhs ni alerta de postura, porque no hay turno del agente que evaluar.
Respuesta
{
"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 son null para los turnos solo-usuario. c0 es null para los turnos solo-agente.
Ejemplo curl - con sesión
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
}'
Ejemplo curl - prueba seca (sin sesión)
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
}'
Ejemplo de 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())
Clasificadores
- C0 - Presión de entrada (posturas I0-I9, puntuación CPI)
- C1 - Postura de estrés adversarial (21 clases P0-P20, métricas POI/PE/DPI)
- C2 - Densidad de sicoofancia (SD)
- C3 - Índice de riesgo de alucinación (HRI)
- C4 - Densidad de persuasión y diversidad de técnicas (PD/TD)
- BHS - Puntuación de Salud Conductual (compuesta 0-1)
SRP - Perfil de Riesgo de Sesión: una lectura derivada a nivel de sesión (no un clasificador, no un campo de /analyze) desde c2.sd, c4.pd y bhs contra una norma de registro de rutina; de solo lectura en /api/v2/intel/session-risk-profile/reference.
/mcp
PSA también habla el Model Context Protocol, para que un IDE con IA, un agente o un orquestador puedan leer el comportamiento de forma nativa sin escribir nada de HTTP. Apunta un cliente MCP al endpoint streamable-HTTP alojado más abajo y autentícate con tu clave API como bearer token. Se exponen dos herramientas, cada una sin estado (no se almacena nada) y que devuelven los mismos campos que /analyze:
psa_analyze_turn: lee un turno de conversación desdeuser_texty/oresponse_text. Devuelve la puntuación de salud del comportamiento, la banda de alerta y las lecturas por postura.psa_analyze_conversation: lee una conversación entera (turns: una lista de objetos{user_text, response_text}) y devuelve las lecturas por turno más un perfil de deriva (media de salud del comportamiento, adulación y persuasión).
Configuración del cliente MCP
{
"mcpServers": {
"psa": {
"url": "https://splabs.io/mcp",
"headers": { "Authorization": "Bearer psa_your_key" }
}
}
}
Llamar a una herramienta directamente (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
Contadores agregados pre-calculados para el usuario autenticado - lectura O(1) por clave primaria. Sin agregados en tiempo real.
{
"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
}
Use este endpoint en lugar de contar sesiones del lado del cliente. El campo total coincide con sessions.count en el perfil de usuario.
/api/v2/psa/sessions
Lista paginada de sesiones DSA. Paginación del lado del servidor - nunca devuelve la tabla completa.
Parámetros de consulta
| página | entero, predeterminado 1 |
| por_página | entero, predeterminado 50, máximo 200 |
| q | cadena de búsqueda (nombre de sesión) |
| alerta_mín | severidad mínima a devolver: green | yellow | orange | red | critical |
| ordenar_por | alert (más severo primero) u omitir para más reciente primero |
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
Clasificación de cambio de régimen para la sesión. Devuelve tipo y confianza.
{
"regime_type": "PROGRESSIVE_DRIFT",
"confidence": 0.87,
"details": "Monotonic BHS decline over 12 turns"
}
/api/v2/psa/session/{session_id}/summary
Resumen a nivel de sesión - BHS inicio/fin/promedio/mínimo, tendencia, turno de mayor riesgo, distribución de alertas.
{
"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]
}
Administración - Aprovisionamiento de Usuarios y Organizaciones
Todos los endpoints /api/admin/ requieren el rol admin (Bearer token o cookie). Usado por el panel Operador.
/api/admin/users/provision
solo administrador · usuario+clave atómico
Crea atómicamente un usuario + clave API en una sola transacción. Sin límite de tasa. Devuelve la clave en texto plano - nunca más se expondrá.
Cuerpo: email (requerido), name, role, plan, key_name, org_id (UUID, opcional).
/api/admin/orgs
solo administrador · 201
Cree una nueva organización. Cuerpo: name (requerido), 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 administrador · paginado
Lista todas las organizaciones con member_count y session_count en vivo. Parámetros query: page, per_page, search.
/api/admin/orgs/{org_id}
solo administrador
/api/admin/orgs/{org_id}/members
solo administrador · idempotente
Agregue o actualice la membresía de un usuario. Cuerpo: user_id (UUID), role (owner/member).
/api/admin/orgs/{org_id}/members
solo administrador · paginado
/api/admin/orgs/{org_id}/members/{user_id}
solo administrador
/api/admin/orgs/{org_id}/enroll-sector
solo administrador · idempotente
Inscribe en masa todas las cuentas fury-<sector>-* en esta org. Requiere que la org tenga un plan enterprise activo.
Pagos y Facturación
sesión webGestión de suscripciones e historial de facturación. Todos los endpoints requieren autenticación cookie.
/api/payments/history
Devuelve el historial de pagos paginado para el usuario autenticado, ordenado más reciente primero.
Parámetros de consulta
| página | entero, predeterminado 1 |
| por_página | entero, predeterminado 10, máximo 100 |
Respuesta
{
"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 centavos (p. ej. 2900 = $29.00). valores de status: succeeded | failed.
/api/payments/create-checkout
Inicia una sesión de checkout de Stripe o un cambio de suscripción, dependiendo del estado actual del usuario.
Cuerpo de solicitud
{ "plan": "pro" }
Matriz de Comportamiento
| Estado actual | Plan de destino | Resultado |
|---|---|---|
| Gratuito / sin suscripción | pro / enterprise | Nueva sesión de Checkout de Stripe → {"url": "..."} |
| Suscripción activa | mismo plan | Redirección de portal de facturación → {"url": "...", "action": "portal"} |
| Pro activo | enterprise (upgrade) | Subscription.modify() directamente → {"action": "upgraded", "plan": "enterprise"} |
| Enterprise activo | pro (downgrade) | HTTP 403 - bloqueado con fecha de fin de período |
| Pro / Enterprise activo | gratuito (degradación) | HTTP 403 - bloqueado |
Respuestas
// 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 sesión de portal de facturación de Stripe para administrar métodos de pago y ver facturas.
{ "url": "https://billing.stripe.com/..." }
/api/payments/cancel-subscription
Se cancela al final del período (cancel_at_period_end=true). El acceso continúa hasta la fecha de fin de período.
{ "ok": true, "cancel_at_period_end": true, "period_end": 1751410800 }
period_end es una marca de tiempo Unix.
/api/payments/sync-subscription
Vuelve a obtener el estado de la suscripción desde Stripe y actualiza la base de datos local. Úselo cuando el estado local diverge (p. ej., tras un webhook fallido).
{ "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
Devuelve paquetes de tokens extra disponibles para compra única. Válido durante 12 meses desde la fecha de compra.
{ "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 sesión de checkout de Stripe de pago único para un paquete de tokens adicionales. Redirige al usuario a Stripe para el pago.
// 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 de Conectores
v2Clasificadores de texto multi-clase personalizados - defina clases, genere datos de entrenamiento via la pasarela CF y entrene modelos MiniLM.
/api/v2/connectors/
Autenticación requerida
Registra un nuevo conector desde un esquema JSON. Devuelve 201. Devuelve 409 si el conector ya existe.
// 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/
Enumere todos los conectores - id, nombre, estado, num_classes, created_at. Sin autenticación requerida.
[{
"connector_id": "soc_signals",
"display_name": "SOC Signals",
"status": "ready",
"num_classes": 5,
"created_at": "2026-04-19T10:22:00"
}]
/api/v2/connectors/{id}
Detalle completo del conector que incluye todas las definiciones de clase. Sin autenticación requerida. 404 si no se encuentra.
{
"connector_id": "soc_signals",
"status": "ready",
"classes": [
{"class_idx": 0,
"label": "Threat Detection"}
]
}
/api/v2/connectors/{id}/status
Punto final de sondeo de estado. Autenticación requerida. Devuelve estado actual + error_message.
{
"connector_id": "soc_signals",
"status": "training",
"error_message": null,
"updated_at": "2026-04-19T11:05:30"
}
/api/v2/connectors/{connector_id}/bootstrap
Flujo SSE · Autenticación requerida
Guía la pipeline completa generar → entrenar → listo via un flujo Server-Sent Events. Conéctese y espere eventos hasta 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
Clasifique una cadena de texto contra un conector listo. Devuelve 503 si el conector no está listo.
// 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 administrador
Elimina un conector - elimina la fila de DB, todas las filas de clases (cascada) y el archivo de modelo .npz si existe.
// Response
{ "deleted": "soc_signals" }
API de Base de Conocimientos
Base de conocimiento semántica Q&A usando embeddings MiniLM-384 y búsqueda por similitud coseno.
PostgreSQL pgvector Requerido
Requiere la extensión pgvector en PostgreSQL. Devuelve 503 si no está disponible. Estado actual: disponible.
/api/v2/knowledge/query
Incrusta la consulta con MiniLM-384 y devuelve elementos de conocimiento semánticamente similares con enrutamiento basado en confianza.
Cuerpo de solicitud
{
"query": "What is the Behavioral Health Score?",
"top_k": 3,
"max_per_source": 3
}
Respuesta
{
"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"
}
Umbrales de Enrutamiento
| auto | confianza ≥ 0.85 | Respuesta devuelta directamente |
| advertencia | 0.65 ≤ confianza < 0.85 | Respuesta con ⚠️ advertencia añadida |
| escalado | confianza < 0.65 | Consulta registrada para revisión humana |
Errores
- 422 - query está vacía
- 503 - pgvector no está disponible o el servicio de incrustación está caído
/api/v2/knowledge/entries
solo administrador
Inserción masiva de entradas de conocimiento con embedding MiniLM en el servidor. Las entradas vacías o con menos de 10 caracteres se descartan - las filas degeneradas coinciden con casi cualquier consulta y contaminan la recuperación (#3683).
Cuerpo de solicitud
{
"entries": [
{"source": "issue-3681", "title": "Optional title", "content": "Entry text (≥ 10 chars)", "confidence_floor": 0.75}
]
}
Respuesta
{"inserted": 1, "skipped": 0, "skipped_indices": []}
Errores
- 403 - no es administrador
- 503 - servicio de incrustación no disponible
/api/v2/knowledge/entries
solo administrador
Elimina entradas de conocimiento: una entrada por UUID, una fuente completa, o todo. entry_id tiene prioridad sobre source; omitiendo ambos se borra TODO.
Parámetros de consulta
| entry_id | UUID opcional de una sola entrada a eliminar - tiene prioridad sobre source (#3683) |
| source | fuente opcional cuyas entradas se eliminan todas; omitir ambos parámetros vacía toda la base de conocimiento |
Respuesta
{"deleted": 1, "source": null, "entry_id": "289da1dc-c896-40e5-9dbc-efa1d19c8af1"}
Errores
422: entry_id no es un UUID válido- 403 - no es administrador
503: base de datos no disponible
Lectores independientes (beta)
Lectores independientes servidos junto al análisis principal. Cada uno tiene su propio modelo y su propio interruptor, y ninguno cambia una puntuación o una alerta devuelta por el endpoint de análisis.
/api/v2/readers/reader-v2
Lector v2 (beta): un modelo abierto, una pasada, todas las lecturas
Un modelo abierto sin modificar (Qwen3-1.7B) se lee desde su estado interno, con una pequeña capa lineal por lectura. Una sola pasada por el texto devuelve todas las lecturas a la vez: presión de entrada, una respuesta que cede, adulación, riesgo de alucinación, persuasión, presión entre agentes, si un mensaje toca un tema a vigilar y si su intención es obtener ayuda peligrosa. Medido en nuestros datos nunca vistos, al nivel o por encima de nuestro lector entrenado en cada lectura de comportamiento, en cinco idiomas, y en conversaciones de varios mensajes.
Envía la conversación, del mensaje más antiguo al más reciente. Si el último mensaje es del asistente, es la respuesta que se lee. La adulación y el riesgo de alucinación leen la petición junto con la respuesta; la lectura de intención peligrosa lee toda la conversación, así que cuenta también una intención dicha en un mensaje anterior. Clave API, 60 peticiones por minuto.
Cuerpo de la petición
{
"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."}
]
}
Respuesta
{
"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"}
}
Cada score ordena los textos: más alto significa más de la lectura. No es una probabilidad calibrada. Cada lectura lleva también un level: alert por encima del punto en que salta como mucho 1 vez de cada 100 conversaciones reales ordinarias, watch por encima del punto 5 de cada 100, none por debajo. Ambos puntos se fijaron sobre 2.300 conversaciones reales y se comprobaron sobre otras 2.271.
Errores
422: mensajes no válidos, o nada que leer503: el lector no puede cargarse en este servidor (por ejemplo, memoria libre insuficiente). La ruta se niega antes que poner en riesgo el servicio.
/api/v2/readers/c2-reversal
Lector de cesión C2 (beta): la cesión leída frente a la respuesta anterior
Lee si una respuesta abandona su posición anterior para dar la razón a la insistencia del usuario. Una respuesta que cede suele sonar segura (una disculpa, un "tienes razón" en negrita y luego la afirmación falsa explicada), así que leída sola parece cualquier respuesta segura. Este lector ve también el turno anterior del asistente, así que lee el cambio de postura en sí. En 800 turnos bajo presión nunca vistos, en cinco idiomas, separa ceder de mantenerse a 0,98 (1 es perfecto), con 3 falsas alarmas en 400 respuestas que se mantenían firmes.
Envía el turno anterior del asistente, el turno del usuario que insiste y la respuesta. Una respuesta que acepta la corrección correcta de un error anterior no es una cesión. Clave API, 60 solicitudes por minuto.
Cuerpo de la petición
{
"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."
}
Respuesta
{
"score": 0.99978,
"caved": true,
"threshold": 0.001131,
"engine": "c2-reversal-06",
"calibrated": false
}
score ordena las respuestas: más alto significa más cesión. No es una probabilidad calibrada. caved aplica el umbral operativo fijado en los datos de validación y guardado con los pesos.
Errores
422: falta el turno anterior o la respuesta, o está vacío503: el lector no puede cargarse en este host (por ejemplo, memoria libre insuficiente). La ruta se niega antes que poner en riesgo el servicio.
/api/v2/readers/c3-swarm
Lector C3 para enjambres (beta): un mensaje de agente que da por cierta una afirmación falsa
Lee si un mensaje de agente le sigue la corriente a una afirmación falsa, incluido un «Correcto, este es el siguiente paso» tranquilo y neutro, sin ninguna adulación, que una lectura de la complacencia no ve. Lee el mensaje junto con lo que el agente recibió. En mensajes de agentes sobre afirmaciones falsas nunca vistas, ordena bien estas confirmaciones cerca de 84 veces de cada 100 pares (la lectura de la complacencia cerca de 60), y encuentra cerca de 7 de cada 10 con 15 falsas alarmas de cada 100.
Envía lo que el agente recibió y lo que escribió. Pensado para mensajes entre agentes que contienen una afirmación: en turnos sueltos de pregunta y respuesta todavía marca muchas respuestas concretas correctas. Clave de API, 60 solicitudes por minuto.
Cuerpo de la petición
{
"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."
}
Respuesta
{
"score": 0.154932,
"confirms_false_claim": true,
"threshold": 0.054601,
"engine": "c3-swarm-06",
"calibrated": false
}
score ordena los mensajes: cuanto más alto, más probable que dé por cierta una afirmación falsa. No es una probabilidad calibrada. confirms_false_claim aplica el umbral operativo fijado con datos de validación y guardado con los pesos.
Errores
422: falta o está vacío lo que el agente recibió o el mensaje503: el lector no puede cargarse en este host (por ejemplo, memoria libre insuficiente). La ruta se niega antes que poner en riesgo el servicio.
Fine-tuning como servicio (beta)
Da dos claves y recibe un modelo afinado en tu propio repositorio y un informe de lo que cambió el entrenamiento. Un token de Hugging Face lee tu repositorio de datos y escribe el de salida; un token de Modal ejecuta el entrenamiento en tu cuenta de GPU. Comprobamos los datos, apartamos ejemplos antes de entrenar, afinamos, subimos los pesos a tu repositorio, los recargamos y leemos el comportamiento del modelo antes y después con las mismas peticiones, junto a las respuestas de tus datos. Las claves se guardan cifradas y se borran al terminar la parte de GPU. Prefijo: /api/v2/tune
/api/v2/tune/jobs
Crear un trabajo. Ambas claves se comprueban antes de guardar nada. Un trabajo activo por cuenta.
{
"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
Tus trabajos, del más reciente.
/api/v2/tune/jobs/{id}
Un trabajo: estado (queued, training, reading, done, failed, cancelled), las fases completadas, el avance de la lectura y el informe al terminar.
/api/v2/tune/jobs/{id}/cancel
Detener el entrenamiento en la GPU y borrar las claves.
/api/v2/tune/jobs/{id}/keys
Borrar ya las claves guardadas. Un trabajo que aún las necesita se cancela.
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 de Sesiones - /api/sessions
Sesiones CRUD - crear, enumerar, renombrar, eliminar. Todos los puntos finales requieren autenticación de token de portador.
/api/sessions/{session_id}
Elimine suavemente una sesión única (is_deleted = true). Limitado al propietario.
{ "ok": true }
/api/sessions
Elimine suavemente todas las sesiones para el usuario autenticado en una sola llamada. Irreversible - sin ventana de deshacer.
{ "ok": true, "deleted": 42 }
deleted - número de sesiones marcadas como eliminadas.
Datos de Demostración - /api/auth
Generación y limpieza de dataset de demostración de una sola vez. Estos endpoints requieren autenticación cookie.
Los registros de demostración se insertan con is_demo=true y son visibles junto con los datos reales en el panel.
/api/auth/dismiss-demo-modal
Marca el modal de datos de demostración de una sola vez como ignorado (demo_modal_shown = true) para que no aparezca de nuevo.
Sin cuerpo de solicitud. Requiere autenticación de cookie (psa_token). Idempotente - seguro para llamar múltiples veces.
{ "ok": true }
/api/auth/generate-demo-data
Genera un conjunto de datos de demostración único para el usuario autenticado. Crea registros en el panel de sesiones:
- DSA Hub - 7 sesiones de demostración con secuencias de postura realistas
Devuelve 409 si los datos de demostración ya fueron generados. Establece demo_data_generated = true en el perfil de usuario.
{
"ok": true,
"psa_sessions": 7
}
/api/auth/demo-data
Elimina permanentemente todos los registros de demostración (is_demo = true) para el usuario autenticado.
Elimina filas de: sessions.
{ "ok": true }
Sesiones de Calibración de Demostración - /v1/demo
Endpoints públicos - no se requiere autenticación. Explore 149 sesiones de calibración (entrada de usuario + respuesta de agente + puntuaciones PSA completas).
/v1/demo/sessions
Enumere sesiones de calibración con filtros de persona/arco opcionales. Paginado del lado del servidor.
Parámetros de consulta
| Param | Tipo | Descripción |
|---|---|---|
| persona | cadena | Filtro: customer_support, educational, hr_policy, it_helpdesk |
| arco | cadena | Filtrar por nombre de arco (p. ej. grief_initial_contact) |
| página | int | Número de página (predeterminado: 1) |
| por_página | int | Resultados por página, 1-100 (predeterminado: 20) |
curl "https://splabs.io/v1/demo/sessions?persona=customer_support&page=1"
/v1/demo/sessions/{session_key}
Obtenga los turnos completos de conversación para una sola sesión de calibración. Devuelve todos los turnos {human, assistant} con puntuaciones PSA completas.
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":"…"}]}'
Estudios de Caso - /v1/case-studies
Endpoints públicos - no se requiere autenticación. Casos de estudio seleccionados respaldados por sesiones de calibración con puntuaciones PSA reales.
/v1/case-studies
Enumere todos los estudios de caso publicados con slug, título, etiquetas y resumen.
curl "https://splabs.io/api/v1/case-studies"
Respuesta
{ "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}
Obtenga un solo caso de estudio con la transcripción completa de la conversación desde la sesión de calibración vinculada.
Parámetros de Ruta
| Param | Descripción |
|---|---|
| slug | Slug del estudio de caso (p. ej. admin-access-authority-pressure-it) |
curl "https://splabs.io/api/v1/case-studies/admin-access-authority-pressure-it"
Novedades del lab - /v1/lab-updates
Endpoints públicos, sin autenticación. Las publicaciones del laboratorio: en qué estamos trabajando. El texto sigue lang (en, it, fr, es, de).
/v1/lab-updates
Lista las publicaciones, de la más reciente. Filtro por kind; paginado.
curl "https://splabs.io/v1/lab-updates?lang=en&per_page=10"
/v1/lab-updates/{slug}
Una publicación con su detalle técnico (body, markdown).
curl "https://splabs.io/v1/lab-updates/topic-reader-five-languages?lang=it"
Límites de Velocidad
| Plan | Análisis/Mes | Sesiones | Acceso a API |
|---|---|---|---|
| Gratuito | 50 | 5 | No |
| Pro | 5,000 | Ilimitado | Sí |
| Unlimited | Ilimitado | Ilimitado | Sí |
Códigos de Error
Todos los errores siguen el formato {"detail": "..."}. Errores estructurados (p. ej. 503) devuelven 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."
}
}
| Código | Significado |
|---|---|
| 401 | Clave API faltante o inválida / no autenticado |
| 403 | El plan no incluye acceso API o degradación de suscripción bloqueada (verificar detalles). |
| 404 | Recurso no encontrado |
| 409 | Turno duplicado - misma sesión + turn_number ya existe |
| 422 | Cuerpo de solicitud inválido (error de tipo o formato de campo) |
| 429 | Límite de análisis mensual alcanzado - retroceda y reintente después de Retry-After |
| 500 | Error interno del servidor |
| 503 | session_id_required - session_id (UUID) o session_name debe ser proporcionado. Use dry_run=true para probar sin persistir. |
/api/v2/psa/internal/ha/aggregate
Solo administrador
Métricas de drift HA anonimizadas a nivel de población. Requiere una cohorte mínima de 10 usuarios con total_turns > 0. Devuelve HTTP 404 si la cohorte es demasiado pequeña.
{
"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"
}
Los datos de la Capa 5 se excluyen de los resultados agregados por diseño.