Saltar al contenido principal

Backend

NestJS 11 en un solo proceso, arrancado desde backend/src/main.ts con prefijo global /api.

Módulos​

MóduloResponsabilidad
channels/Webhooks entrantes y envío saliente de los 7 canales
bots/Orquestación con LangGraph, tools y registro de bots
modules/scheduling/Agenda: huecos, citas, recursos, disponibilidad
modules/health/Pacientes y documentos clínicos. Mínimo a propósito: la lógica clínica vive en funciones auditadas de Postgres
data-sources/Fuentes de conocimiento: documentos, filas, APIs
embeddings/Vectores para la búsqueda semántica del catálogo
contacts/Resolución de contactos entre canales. Global
outbox/Cola de salida con planificador
reminders/Recordatorios de cita programados
usage/Medición de tokens de LLM
billing/Sincronización con Odoo
payments/Place to Pay
modules/Activación de módulos por tenant
sandbox-channels/Modo de canales compartidos para pruebas
data-sync/Copia de producción a test, solo superadmin
common/queue/, common/cache/, common/rate-limit/Infraestructura: BullMQ, caché del bot en Redis, límite de peticiones

Cuatro de ellos no exponen HTTP: contacts, reminders, outbox y usage son servicios y planificadores.

Validación​

No hay class-validator ni ValidationPipe global. Los DTO son clases de TypeScript planas y varios controladores tipan el cuerpo en línea. En la práctica significa que la validación de entrada es responsabilidad de cada handler: no des por hecho que un campo llegó con el tipo que dice la firma.

Es la deuda técnica más visible del backend y el sitio natural por donde empezar si alguien quiere mejorar la robustez.

Colas​

ColaProductorConsumidor
inboundX, email, webchat, MetaNinguno en este repositorio
data-syncEndpoint de superadminDataSyncProcessor
product-embeddingsCRM al guardar productosProductEmbeddingsProcessor

Lo de inbound está explicado en Visión general.

Guards​

No hay guard global. Los que existen se aplican endpoint por endpoint:

  • PermissionGuard — permiso concreto del rol, o pertenencia al tenant. Identifica a quien llama por su Bearer token. Un superadmin pasa sin membresía (se resuelve con user_is_superadmin(p_user_id) sobre el usuario del token; impersonando, el token es el del impersonado y manda su rol). Cubre también /api/modules, que antes no autenticaba.
  • ModuleGuard — el módulo tiene que estar activo para ese tenant.
  • SuperadminGuard — solo administración de plataforma.
  • SandboxModeGuard — solo cuando CHANNEL_MODE=sandbox.

Qué guard lleva cada endpoint se ve en la Referencia de API.

Acceso a datos​

Hay dos clientes de Supabase conviviendo, herencia de la unificación de dos backends:

  • SupabaseModule, con el token SUPABASE_CLIENT.
  • SupabaseService, en database/supabase/.

Los dos usan la clave de servicio y por tanto se saltan RLS. Es intencionado —un webhook de Meta no tiene sesión de usuario— pero implica que cualquier consulta del backend tiene que filtrar por tenant explícitamente. Aquí no hay red de seguridad.

Aparte, data-sync abre conexiones de pg directas a las dos bases con PROD_MIGRATION_DATABASE_URL y DEV_MIGRATION_DATABASE_URL.

Endpoints por módulo​

Bots​

MétodoRutaQué haceGuardsCódigo
GET/api/bots/:botId/flow-versionssin describir
PermissionGuard
FlowVersions
POST/api/bots/:botId/flow-versionssin describir
PermissionGuard
FlowVersions
DELETE/api/bots/:botId/flow-versions/:versionIdsin describir
PermissionGuard
FlowVersions
GET/api/bots/:botId/flow-versions/:versionIdsin describir
PermissionGuard
FlowVersions
POST/api/bots/:botId/flow-versions/:versionId/activatesin describir
PermissionGuard
FlowVersions
GET/api/bots/agent-toolsStatic manifest of all tools the flow editor can offer in the "Herramientas del agente" picker on LLM nodes. Per-bot runtime availability (module enabled, tools_config, indexed records…) is enforced server-side at agent invocation time, so this list is intentionally global. Every service a factory might need must be passed here. A factory whose dependency is missing returns null and its tool silently disappears from the picker — which is how the scheduling and catalog tools were invisible even though they worked at runtime.
PermissionGuard
Bots
POST/api/bots/approvals/:id/continuesin describir
PermissionGuard
Approvals
GET/api/bots/conversations/:conversationId/stateDónde está la conversación y por qué pasos pasó. Solo estructura, sin PHI.
PermissionGuard
ConversationState
GET/api/bots/conversations/:conversationId/state/historyPuntos de guardado de la conversación (uno por mensaje). Solo estructura.
PermissionGuard
ConversationState
POST/api/bots/conversations/:conversationId/state/replayReproduce EN SECO un mensaje desde un punto de guardado: devuelve el recorrido que haría hoy el flujo, sin enviar nada ni tocar la conversación. Es una herramienta de depuración, por eso pide bot.update.
PermissionGuard
ConversationState
POST/api/bots/conversations/:conversationId/state/resetOlvida la memoria del bot en esta conversación (el próximo mensaje empieza de cero).
PermissionGuard
ConversationState
POST/api/bots/dashboards/generatesin describirningunoDashboards
POST/api/bots/dashboards/sessionssin describirningunoDashboards
GET/api/bots/dashboards/sessions/:sessionIdsin describirningunoDashboards
POST/api/bots/dashboards/sessions/:sessionId/filessin describirningunoDashboards
POST/api/bots/dashboards/sessions/:sessionId/messagesin describirningunoDashboards
POST/api/bots/messagesin describir
PermissionGuard
Bots
POST/api/bots/reloadsin describir
PermissionGuard
Bots
GET/api/bots/tenantsQué tenants tiene cargados el registro: dato de operación, solo superadmin.
PermissionGuard
Bots
GET/api/bots/tenants/:tenantIdsin describir
PermissionGuard
Bots
GET/api/subflowssin describir
PermissionGuard
Subflows
POST/api/subflowssin describir
PermissionGuard
Subflows
DELETE/api/subflows/:idsin describir
PermissionGuard
Subflows
GET/api/subflows/:idsin describir
PermissionGuard
Subflows
PATCH/api/subflows/:idsin describir
PermissionGuard
Subflows
GET/api/subflows/:id/usagessin describir
PermissionGuard
Subflows
GET/api/subflows/:id/versionssin describir
PermissionGuard
Subflows
POST/api/subflows/:id/versionssin describir
PermissionGuard
Subflows
GET/api/subflows/:id/versions/:versionIdsin describir
PermissionGuard
Subflows

29 endpoints.

Agenda​

MétodoRutaQué haceGuardsCódigo
GET/api/scheduling/appointmentsCitas de un rango con los nombres resueltos. Alimenta el calendario.
ModuleGuard
Scheduling
POST/api/scheduling/appointmentssin describir
ModuleGuard
AppointmentWriteGuard
Scheduling
POST/api/scheduling/appointments/cancelsin describir
ModuleGuard
AppointmentWriteGuard
Scheduling
POST/api/scheduling/appointments/contactAsignar o quitar el paciente de una cita existente.
ModuleGuard
AppointmentWriteGuard
Scheduling
POST/api/scheduling/appointments/offer-rescheduleCancela cada cita listada y le ofrece al paciente reagendar por WhatsApp. Se llama desde la alerta de conflicto al guardar un bloqueo de agenda. El body solo trae `resource_id` (no un `appointment_id` suelto): con eso `AppointmentWriteGuard` toma la vía "alta" y comprueba que quien llama sea dueño de ESE recurso, que es exactamente lo que hace falta —todas las citas de la lista son, por construcción, de ese mismo recurso—. Las citas se recargan de la base por id: no se confía en los datos que mande el cliente.
ModuleGuard
AppointmentWriteGuard
Scheduling
POST/api/scheduling/appointments/reschedulesin describir
ModuleGuard
AppointmentWriteGuard
Scheduling
POST/api/scheduling/appointments/send-reminderEnvía ya el recordatorio de una cita, a mano. Sale por el mismo canal por el que se reservó (la conversación de origen decide la ruta). Lleva `AppointmentWriteGuard`: avisar a un paciente es actuar sobre su cita, y el guard saca el tenant de la fila real y exige `manage` (con alcance propio si no hay `view_all`).
ModuleGuard
AppointmentWriteGuard
Scheduling
POST/api/scheduling/appointments/statusConfirmar, completar, marcar ausencia o reabrir.
ModuleGuard
AppointmentWriteGuard
Scheduling
POST/api/scheduling/appointments/substatusPoner o quitar el sub-estado de una cita (etiqueta informativa). Mismo guard que el estado: quien no puede tocar esa agenda, tampoco etiqueta.
ModuleGuard
AppointmentWriteGuard
Scheduling
GET/api/scheduling/config/:entityLista entidades de configuración del tenant. Query opcionales: `resource_id`, `location_id` (eq). Para `availabilityExceptions` también `from`/`to` (solape) y `resource_ids` (lista separada por comas) — una sola petición para el calendario.
ModuleGuard
PermissionGuard
SchedulingConfig
POST/api/scheduling/config/:entitysin describir
ModuleGuard
PermissionGuard
SchedulingWriteGuard
SchedulingConfig
DELETE/api/scheduling/config/:entity/:idsin describir
ModuleGuard
PermissionGuard
SchedulingWriteGuard
SchedulingConfig
PATCH/api/scheduling/config/:entity/:idsin describir
ModuleGuard
PermissionGuard
SchedulingWriteGuard
SchedulingConfig
GET/api/scheduling/config/availability-exceptions/conflictsCitas que chocarían con un bloqueo de agenda antes de guardarlo. Ruta de dos segmentos (`availability-exceptions/conflicts`), así que no colisiona con el comodín `:entity` de una sola palabra — pero se declara aquí arriba igualmente, junto al resto de rutas específicas, por consistencia con ellas.
ModuleGuard
PermissionGuard
SchedulingConfig
GET/api/scheduling/config/block-reschedule-offersin describir
ModuleGuard
PermissionGuard
SchedulingConfig
PUT/api/scheduling/config/block-reschedule-offersin describir
ModuleGuard
PermissionGuard
SchedulingConfig
GET/api/scheduling/config/calendar-viewsin describir
ModuleGuard
PermissionGuard
SchedulingConfig
PUT/api/scheduling/config/calendar-viewsin describir
ModuleGuard
PermissionGuard
SchedulingConfig
GET/api/scheduling/config/linkable-usersUsuarios vinculables a un recurso. Va antes de :entity para no colisionar.
ModuleGuard
PermissionGuard
SchedulingConfig
GET/api/scheduling/config/reminderssin describir
ModuleGuard
PermissionGuard
SchedulingConfig
PUT/api/scheduling/config/remindersCambiar cuándo se avisa a los pacientes es configuración de la sede, no mantenimiento de la ficha de un profesional: exige el permiso completo, no la vía "propio" de SchedulingWriteGuard.
ModuleGuard
PermissionGuard
SchedulingConfig
PUT/api/scheduling/config/reminders/cold-outreachsin describir
ModuleGuard
PermissionGuard
SchedulingConfig
GET/api/scheduling/config/reminders/outreachsin describir
ModuleGuard
PermissionGuard
SchedulingConfig
POST/api/scheduling/config/reminders/whatsapp-templatesin describir
ModuleGuard
PermissionGuard
SchedulingConfig
PUT/api/scheduling/config/reminders/whatsapp-templatesin describir
ModuleGuard
PermissionGuard
SchedulingConfig
GET/api/scheduling/config/reminders/whatsapp-template/candidatessin describir
ModuleGuard
PermissionGuard
SchedulingConfig
POST/api/scheduling/config/reminders/whatsapp-template/refreshsin describir
ModuleGuard
PermissionGuard
SchedulingConfig
GET/api/scheduling/config/resources/:id/servicessin describir
ModuleGuard
PermissionGuard
SchedulingConfig
POST/api/scheduling/config/resources/:id/servicessin describir
ModuleGuard
PermissionGuard
SchedulingWriteGuard
SchedulingConfig
GET/api/scheduling/config/services/:id/locationssin describir
ModuleGuard
PermissionGuard
SchedulingConfig
POST/api/scheduling/config/services/:id/locationssin describir
ModuleGuard
PermissionGuard
SchedulingWriteGuard
SchedulingConfig
GET/api/scheduling/config/status-colorsColor de cada estado de cita. Lo lee cualquiera del tenant: se pinta en todas las vistas.
ModuleGuard
PermissionGuard
SchedulingConfig
PUT/api/scheduling/config/status-colorssin describir
ModuleGuard
PermissionGuard
SchedulingConfig
GET/api/scheduling/config/substatusesSub-estados de cita (etiquetas con color encima del estado). Los lee cualquiera del tenant: se pintan en calendario, citas y hoy.
ModuleGuard
PermissionGuard
SchedulingConfig
PUT/api/scheduling/config/substatusessin describir
ModuleGuard
PermissionGuard
SchedulingConfig
GET/api/scheduling/config/substatuses/usageUso por sub-estado y estado: solo para quien configura (avisa antes de romper).
ModuleGuard
PermissionGuard
SchedulingConfig
GET/api/scheduling/contacts/:contactId/appointmentssin describir
ModuleGuard
Scheduling
GET/api/scheduling/slotsHuecos disponibles. `location_id` es opcional a propósito: sin él busca en todas las sedes y cada hueco viene etiquetado con la suya.
ModuleGuard
Scheduling

38 endpoints.

Fuentes de datos​

MétodoRutaQué haceGuardsCódigo
GET/api/data-sourcessin describirningunoDataSources
DELETE/api/data-sources/:idsin describirningunoDataSources
GET/api/data-sources/:idsin describirningunoDataSources
PATCH/api/data-sources/:idsin describirningunoDataSources
DELETE/api/data-sources/:id/node-attachsin describirningunoDataSources
POST/api/data-sources/:id/node-attachsin describirningunoDataSources
POST/api/data-sources/:id/reindexsin describirningunoDataSources
GET/api/data-sources/:id/signed-urlsin describirningunoDataSources
POST/api/data-sources/apisin describirningunoDataSources
POST/api/data-sources/documentsin describirningunoDataSources
POST/api/data-sources/rowssin describirningunoDataSources

11 endpoints.

El resto está en la Referencia de API.