{"openapi":"3.0.3","info":{"title":"Senda Enterprise API","version":"5.25.0","description":"## Senda Meta-Engine API\n\nAPI RESTful para integrar Senda con sistemas empresariales.\n\n### Autenticación\nTodas las rutas protegidas requieren un cookie de sesión `senda_session`\nobtenido vía `POST /api/auth/login`.\n\n### Multi-tenancy\nEl `tenant_id` se deriva siempre del JWT de sesión.\n**Nunca** se acepta `tenant_id` en el body de la solicitud.\n\n### Rate Limits\n- 60 req/min para endpoints de chat\n- 300 req/min para endpoints de datos\n\n### Soporte\n- Documentación: https://docs.senda.ai\n- Status: https://status.senda.ai","contact":{"name":"Senda Enterprise Support","email":"enterprise@senda.ai"},"license":{"name":"Proprietary","url":"https://senda.ai/terms"}},"servers":[{"url":"https://api.senda.ai","description":"Production"},{"url":"http://localhost:8787","description":"Local Dev"}],"tags":[{"name":"Auth","description":"Autenticación y gestión de sesiones"},{"name":"Chat","description":"Conversaciones con agentes"},{"name":"Actions","description":"Catálogo y ejecución de acciones"},{"name":"Agents","description":"Gestión de agentes y espacios"},{"name":"Users","description":"Gestión de usuarios del tenant"},{"name":"Webhooks","description":"Webhooks entrantes y suscripciones salientes"},{"name":"Export","description":"Exportación de datos (CSV/PDF)"},{"name":"Security","description":"Políticas de seguridad y acceso"},{"name":"Analytics","description":"Métricas de adopción y auditoría"},{"name":"Health","description":"Estado del sistema"},{"name":"Rooms","description":"Salas de Trabajo Cognitivo (war rooms multi-usuario/multi-agente) — feature_war_rooms"},{"name":"Compliance","description":"Proveniencia de decisiones e integridad de auditoría — feature_compliance_pack"}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"Senda-Token"},"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key (sk_live_...)"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","example":"Acceso denegado"}}},"Session":{"type":"object","properties":{"user_id":{"type":"string"},"tenant_id":{"type":"string"},"role_id":{"type":"string","enum":["r_user","r_admin","r_superadmin","r_security_officer","r_space_admin"]},"nombre":{"type":"string"},"email":{"type":"string","format":"email"}}},"ActionResponse":{"type":"object","properties":{"success":{"type":"boolean"},"action_id":{"type":"string"},"action_name":{"type":"string"},"response":{"type":"object","nullable":true},"error":{"type":"string","nullable":true}}},"WebhookSubscription":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"event_filter":{"type":"string","example":"on_action_executed"},"is_active":{"type":"boolean"},"total_delivered":{"type":"integer"},"total_dead_letter":{"type":"integer"},"created_at":{"type":"string","format":"date-time"}}}}},"security":[{"apiKeyAuth":[]},{"cookieAuth":[]}],"paths":{"/api/auth/login":{"post":{"tags":["Auth"],"summary":"Iniciar sesión","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","format":"password"}}}}}},"responses":{"200":{"description":"Login exitoso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"Credenciales inválidas","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"IP no permitida o cuenta bloqueada"},"412":{"description":"Contraseña expirada — requiere cambio"}}}},"/api/auth/logout":{"post":{"tags":["Auth"],"summary":"Cerrar sesión","responses":{"200":{"description":"Sesión cerrada"}}}},"/api/auth/me":{"get":{"tags":["Auth"],"summary":"Obtener sesión actual","responses":{"200":{"description":"Datos de sesión","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"No autenticado"}}}},"/api/auth/mfa/setup":{"post":{"tags":["Auth"],"summary":"Iniciar configuración TOTP MFA","responses":{"200":{"description":"QR code y secret OTP"}}}},"/api/auth/mfa/verify":{"post":{"tags":["Auth"],"summary":"Verificar código TOTP","responses":{"200":{"description":"MFA habilitado"},"400":{"description":"Código inválido o expirado"}}}},"/v1/chat/completions":{"post":{"tags":["Chat"],"summary":"Enviar mensaje al agente (streaming SSE)","description":"Requiere el scope `chat`. Límite de velocidad: 60 req/min.","security":[{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"message":{"type":"string","description":"Mensaje del usuario"},"space":{"type":"string","description":"ID o código del espacio de agentes (opcional)"},"chat_id":{"type":"string","nullable":true,"description":"ID de conversación existente (opcional)"},"history":{"type":"array","items":{"type":"object"},"description":"Historial de mensajes previos (opcional)"}}}}}},"responses":{"200":{"description":"Stream SSE con respuesta del agente","content":{"text/event-stream":{}}},"401":{"description":"No autenticado"},"403":{"description":"Permisos insuficientes o cuenta bloqueada"}}}},"/v1/chat/history/{chatId}":{"get":{"tags":["Chat"],"summary":"Obtener historial de chat","description":"Requiere el scope `chat`. Límite de velocidad: 300 req/min.","security":[{"apiKeyAuth":[]}],"parameters":[{"name":"chatId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lista de mensajes del chat"},"401":{"description":"No autenticado"}}}},"/v1/agents":{"get":{"tags":["Agents"],"summary":"Listar agentes del tenant","description":"Requiere el scope `agents:read` o `agents`. Límite de velocidad: 300 req/min.","security":[{"apiKeyAuth":[]}],"parameters":[{"name":"space","in":"query","required":false,"schema":{"type":"string"},"description":"ID o código del espacio"}],"responses":{"200":{"description":"Lista de agentes configurados"}}}},"/v1/agents/{id}":{"get":{"tags":["Agents"],"summary":"Detalle de un agente","description":"Requiere el scope `agents:read` o `agents`. Límite de velocidad: 300 req/min.","security":[{"apiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Detalle del agente con sus acciones y archivos de conocimiento vinculados"},"404":{"description":"Agente no encontrado"}}}},"/v1/knowledge":{"get":{"tags":["Knowledge"],"summary":"Listar archivos de conocimiento (RAG)","description":"Requiere el scope `knowledge:read` o `files`. Límite de velocidad: 300 req/min.","security":[{"apiKeyAuth":[]}],"parameters":[{"name":"agent_id","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lista de archivos de conocimiento"}}}},"/v1/knowledge/ingest":{"post":{"tags":["Knowledge"],"summary":"Subir un documento de texto a la base de conocimiento (RAG)","description":"Requiere el scope `knowledge:write` o `files`. Límite de velocidad: 60 req/min.","security":[{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agent_id","filename","content"],"properties":{"agent_id":{"type":"string"},"filename":{"type":"string"},"content":{"type":"string"}}}}}},"responses":{"201":{"description":"Documento cargado e ingresado en cola de indexación"}}}},"/v1/actions":{"get":{"tags":["Actions"],"summary":"Listar acciones activas del catálogo","description":"Requiere el scope `actions:read` o `actions`. Límite de velocidad: 300 req/min.","security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Lista de acciones"}}}},"/v1/actions/{id}/execute":{"post":{"tags":["Actions"],"summary":"Ejecutar una acción directamente","description":"Requiere el scope `actions:execute` o `actions`. Límite de velocidad: 60 req/min.","security":[{"apiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","description":"Parámetros necesarios para la ejecución"}}}}}},"responses":{"200":{"description":"Resultado de la ejecución de la acción externa"},"502":{"description":"Fallo al ejecutar la acción remota"}}}},"/v1/usage":{"get":{"tags":["Analytics"],"summary":"Métricas de consumo por periodo","description":"Requiere el scope `usage:read` o `analytics`. Límite de velocidad: 300 req/min.","security":[{"apiKeyAuth":[]}],"parameters":[{"name":"period","in":"query","schema":{"type":"string","enum":["24h","7d","30d"],"default":"30d"}}],"responses":{"200":{"description":"Volumen de mensajes, tokens consumidos, y ejecuciones de acciones exitosas"}}}},"/api/rooms":{"post":{"tags":["Rooms"],"summary":"Crear sala (find-or-create por external_ref)","description":"Con `external_ref`, semántica find-or-create: 201 si la sala se creó, 200 con la instancia abierta existente si el ref ya tenía sala (idempotencia de webhooks/retries). Acepta `blueprint_id` para instanciar desde plantilla.","responses":{"200":{"description":"Sala existente reutilizada (created: false)"},"201":{"description":"Sala creada"},"403":{"description":"Flag off o sin acceso al espacio"}}},"get":{"tags":["Rooms"],"summary":"Listar instancias de salas","description":"Filtros: `blueprint_id`, `status` (open|closed). Los no-admin solo ven salas donde participan.","responses":{"200":{"description":"Lista de salas"}}}},"/api/rooms/{id}":{"get":{"tags":["Rooms"],"summary":"Detalle de sala con participantes","responses":{"200":{"description":"Sala + participantes"},"403":{"description":"No participante"},"404":{"description":"No encontrada"}}}},"/api/rooms/{id}/participants":{"post":{"tags":["Rooms"],"summary":"Invitar humano (user_id) o agente (agent_id, always_on)","responses":{"201":{"description":"Participante agregado"}}}},"/api/rooms/{id}/close":{"post":{"tags":["Rooms"],"summary":"Cerrar sala y disparar el acta de los agentes always-on","responses":{"200":{"description":"Sala cerrada, actas producidas"},"409":{"description":"Ya cerrada"}}}},"/api/rooms/{id}/invite":{"post":{"tags":["Rooms"],"summary":"Invitar a un usuario con notificación directa (estilo Teams)","description":"Agrega al usuario como participante y le entrega la invitación con deep link a la sala: notificación in-app siempre, Web Push si el tenant tiene `push_notifications` activo y el usuario tiene suscripciones (semántica default-ON, opt-out por preferencia). Body: `{ user_id }`.","responses":{"201":{"description":"{ success, invited, notified: { inApp, pushSent } }"},"400":{"description":"user_id faltante o auto-invitación"},"403":{"description":"El invitador no es participante"},"404":{"description":"Usuario no encontrado"},"409":{"description":"Sala cerrada"}}}},"/api/rooms/{id}/invitable-users":{"get":{"tags":["Rooms"],"summary":"Usuarios invitables a la sala","description":"Humanos activos del tenant que aún no participan (excluye service accounts y usuario de sistema). Filtro `?q=` por nombre/email, máx. 50. Accesible a cualquier participante — no expone el directorio completo a no-admins.","responses":{"200":{"description":"Lista { id, nombre, email, avatar_url }"},"403":{"description":"No participante"}}}},"/api/chats/{id}/messages/append":{"post":{"tags":["Chat"],"summary":"Append server-authoritative de un mensaje humano","description":"El servidor genera el ID y estampa la atribución desde la sesión; user_id/agent_id/role del payload se ignoran.","responses":{"201":{"description":"{ id, seq, created_at }"}}}},"/api/chats/{id}/messages?after={seq}":{"get":{"tags":["Rooms"],"summary":"Poll de sala con cursor (envelope transport-agnostic)","description":"Cursor = rowid. Respuesta: `{ cursor, messages, presence, room_status }`. La presencia del solicitante se actualiza en el mismo request (piggyback). El mismo frame viajará por WebSocket tras un upgrade a Durable Objects — el contrato no cambia. Solo aplica a chats con room_mode.","responses":{"200":{"description":"Envelope de poll"},"400":{"description":"Chat clásico (sin room_mode)"},"403":{"description":"No participante"}}}},"/api/room-blueprints":{"get":{"tags":["Rooms"],"summary":"Listar plantillas de salas (tipos)","responses":{"200":{"description":"Blueprints del tenant"}}},"post":{"tags":["Rooms"],"summary":"Crear plantilla (admin)","description":"lifecycle: `ephemeral` (instancia nueva por ref tras cierre) o `persistent` (singleton vivo por ref).","responses":{"201":{"description":"Blueprint creado"}}}},"/api/room-blueprints/seed-defaults":{"post":{"tags":["Rooms"],"summary":"Sembrar plantillas de fábrica + agentes de acta (admin, idempotente)","description":"Crea \"Sala de Estado de Proyecto\" (persistent) y \"War Room de Incidentes\" (ephemeral), junto con los agentes always-on de Resumen y Decisiones del espacio.","responses":{"201":{"description":"IDs de agentes y blueprints"}}}},"/api/compliance/provenance":{"get":{"tags":["Compliance"],"summary":"Reporte de proveniencia de decisiones","description":"Por ejecución: quién disparó, quién confirmó (texto verbatim + snapshots de config HITL), clasificación de supervisión (human_confirmed | human_triggered_auto | autonomous | shadow). Requiere Security Officer.","responses":{"200":{"description":"{ count, rows }"},"403":{"description":"Flag off o sin rol"}}}},"/api/compliance/integrity":{"get":{"tags":["Compliance"],"summary":"Verificación de hash chain de auditoría (audit_logs + admin_audit_log)","responses":{"200":{"description":"Estado de ambas cadenas con first_invalid_id si falla"}}}},"/api/compliance/hitl-map":{"get":{"tags":["Compliance"],"summary":"Mapa de supervisión humana por tipo de acción","responses":{"200":{"description":"Config actual vs ejecuciones observadas"}}}},"/api/compliance/export":{"get":{"tags":["Compliance"],"summary":"Export del reporte de proveniencia","description":"format=csv|json. CSV con escaping RFC 4180.","responses":{"200":{"description":"Archivo descargable"}}}},"/api/chats/{id}/corrections":{"post":{"tags":["Chat"],"summary":"Registrar corrección humana a un mensaje de agente (memoria organizacional)","description":"El original_output se toma de D1 (el payload no puede reescribir historia). Flag: feature_decision_records.","responses":{"201":{"description":"{ id, status: open }"}}}},"/api/agents/{id}/corrections":{"get":{"tags":["Agents"],"summary":"Listar correcciones registradas para un agente","responses":{"200":{"description":"Decision records del agente"}}}},"/api/webhook-subs":{"get":{"tags":["Webhooks"],"summary":"Listar suscripciones de webhooks salientes (Admin)","responses":{"200":{"description":"Lista de suscripciones","content":{"application/json":{"schema":{"type":"object","properties":{"subscriptions":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}}}}},"post":{"tags":["Webhooks"],"summary":"Crear suscripción de webhook saliente (Admin)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","url"],"properties":{"name":{"type":"string"},"url":{"type":"string","format":"uri","description":"URL destino (ej. Telar, Make, n8n)"},"event_filter":{"type":"string","default":"*","description":"on_action_executed | on_action_failed | * (todos)"},"signing_secret":{"type":"string","nullable":true,"description":"Secret para verificar X-Senda-Signature (HMAC-SHA256)"}}}}}},"responses":{"201":{"description":"Suscripción creada"}}}},"/api/webhook-subs/{id}/log":{"get":{"tags":["Webhooks"],"summary":"Historial de entregas de una suscripción","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lista de entregas con estado y logs de error"}}}},"/api/webhook-subs/{id}/retry":{"post":{"tags":["Webhooks"],"summary":"Reintentar una entrega dead_letter","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["delivery_id"],"properties":{"delivery_id":{"type":"string"}}}}}},"responses":{"200":{"description":"Reintento programado"}}}},"/api/export/conversations":{"get":{"tags":["Export"],"summary":"Exportar conversaciones (CSV)","parameters":[{"name":"from","in":"query","schema":{"type":"string","format":"date"},"description":"Fecha inicio (default: 30 días atrás)"},{"name":"to","in":"query","schema":{"type":"string","format":"date"},"description":"Fecha fin (default: hoy)"},{"name":"group_id","in":"query","schema":{"type":"string"},"description":"Filtrar por espacio"}],"responses":{"200":{"description":"CSV de conversaciones","content":{"text/csv":{}}}}}},"/api/export/metrics":{"get":{"tags":["Export"],"summary":"Exportar métricas de uso (CSV)","responses":{"200":{"description":"CSV con volumen de chats, acciones y uso de AI","content":{"text/csv":{}}}}}},"/api/export/roi-report":{"get":{"tags":["Export"],"summary":"Reporte ROI para directorio (HTML imprimible → PDF)","responses":{"200":{"description":"HTML con métricas de ROI, listo para imprimir o guardar como PDF","content":{"text/html":{}}}}}},"/api/security/ip-allowlist":{"get":{"tags":["Security"],"summary":"Listar reglas de IP allowlist del tenant (Admin)","responses":{"200":{"description":"Lista de reglas CIDR"}}},"post":{"tags":["Security"],"summary":"Agregar regla de IP allowlist (Admin)","responses":{"201":{"description":"Regla creada"}}}},"/api/security/password-policy":{"get":{"tags":["Security"],"summary":"Obtener política de contraseñas del tenant (Admin)","responses":{"200":{"description":"Configuración de política actual"}}},"put":{"tags":["Security"],"summary":"Actualizar política de contraseñas (Admin)","responses":{"200":{"description":"Política actualizada"}}}},"/api/audit/verify":{"get":{"tags":["Analytics"],"summary":"Verificar integridad del hash chain del audit log (Security Officer)","responses":{"200":{"description":"Resultado de verificación con entries_checked y entries_invalid"}}}},"/api/audit/adoption":{"get":{"tags":["Analytics"],"summary":"Métricas de adopción departamental (Admin)","parameters":[{"name":"days","in":"query","schema":{"type":"integer","default":30,"maximum":90},"description":"Período en días"}],"responses":{"200":{"description":"DAU diario, mensajes, acciones y desglose por espacio"}}}},"/api/space-admin/assignments":{"get":{"tags":["Users"],"summary":"Listar asignaciones de Space Admin (Admin)","responses":{"200":{"description":"Lista de asignaciones"}}},"post":{"tags":["Users"],"summary":"Asignar usuario como Space Admin de un espacio","responses":{"200":{"description":"Asignación creada"}}}},"/health":{"get":{"tags":["Health"],"summary":"Estado del sistema","security":[],"responses":{"200":{"description":"Sistema operativo","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"]},"version":{"type":"string","example":"5.1.5"},"timestamp":{"type":"string","format":"date-time"},"checks":{"type":"object","properties":{"database":{"type":"string","enum":["ok","degraded"]},"kv":{"type":"string","enum":["ok","degraded"]}}}}}}}}}}}}}