Volver a Casos de Ingeniería
26 de abril de 2025

SaaS Clínico y Automatización de Reservas con Sincronización Nativa OAuth de Calendario y Conversión Atómica de Leads

Cero Doble-Reservas vía API de Slots en Tiempo Real, Onboarding Lead-to-Paciente Atómico <2s, TTFB Sub-100ms con OPcache

Laravel 12 PHP 8.2 MySQL SQLite Tailwind CSS AlpineJS n8n Google Calendar API

El Cuello de Botella

Psicólogos, terapeutas y clínicas de zooterapia dependían históricamente de herramientas genéricas, hojas de cálculo y software disgregado para gestionar agendas de citas, historias clínicas y captación de leads. Esto generaba cuellos de botella operativos severos: la disponibilidad en tiempo real era imposible de calcular con precisión, provocando doble-reservas y huecos en la agenda que erosionaban la confianza del paciente. La duplicación manual de datos entre formularios web de prospectos y ficheros clínicos de pacientes consumía horas administrativas e introducía errores de transcripción. Las notas clínicas existían en silos a través de documentos no estructurados, comprometiendo el seguimiento longitudinal de historiales psicológicos, medicaciones y progreso de sesiones.

Mientras tanto, sitios web estáticos sin renderizado optimizado, marcado de esquemas ni metadatos de SEO local dejaban estas prácticas invisibles ante consultas de búsqueda regional de alto interés, elevando el Costo de Adquisición de Clientes (CAC) y forzando la dependencia de redes de referencia offline costosas.


Arquitectura y Automatización

La plataforma está diseñada como un SaaS monolítico sobre Laravel 12 y PHP 8.2, utilizando una estrategia de doble base de datos (SQLite para pruebas locales rápidas, MySQL para persistencia de producción en VPS). La elección del monolito elimina la latencia de red entre servicios, garantiza la integridad transaccional ACID y minimiza el costo de despliegue—crítico para pequeñas prácticas clínicas que operan con infraestructuras esbeltas.

Interfaz SSR Dinámica: Construida con Laravel Breeze, Tailwind CSS 3 y AlpineJS. Las páginas se renderizan del lado del servidor con bundles mínimos de JavaScript, manteniendo el Largest Contentful Paint (LCP) y el Interaction to Next Paint (INP) bajos sin la sobrecarga de runtime de un framework SPA completo.

Sincronización Nativa de Calendario Dirigida por Eventos: En lugar de depender de paquetes de terceros pesados, la sincronización se gestiona de forma nativa. Un observador de Eloquent en el modelo Appointment se engancha a los eventos created, updated y deleted para invocar GoogleCalendarService. Este servicio gestiona el flujo completo OAuth 2.0, la rotación de tokens de refresco (con un margen de seguridad de 5 minutos pre-vencimiento) y los payloads REST directos a la API de Google Calendar. Esto elimina la hinchazón de abstracciones de terceros y mantiene la lógica del ciclo de vida del token dentro del límite de la aplicación.

Pipeline Atómico de Conversión Lead-to-Paciente: El LeadController encapsula la transición de prospecto a paciente dentro de una transacción de base de datos (DB::beginTransaction()). Cuando un lead se marca como convertido, el sistema instancia atómicamente un nuevo perfil de Patient, migra las notas del lead, localiza servicios coincidentes (con respaldos inteligentes), registra una cita (Appointment) confirmada en el calendario vinculado y cambia el estado del lead a converted. Esto garantiza pérdida de datos cero, cero registros huérfanos y cero exposición de estado parcial ante fallos.

Ingesta Asíncrona de Leads: La aplicación expone endpoints de webhook para automatización externa:

  • /api/webhook/n8n: Integración directa con flujos de trabajo de n8n para analizar y almacenar datos de leads provenientes de embudos publicitarios.
  • /api/webhook/whatsapp: Ingesta logs de comunicaciones entrantes y hooks de estado de entrega.

Motor de Disponibilidad Personalizado: El endpoint /api/disponibilidad/{user}/{date} calcula slots libres en tiempo real. Recupera las configuraciones de disponibilidad para el día de la semana solicitado y aplica un array_diff contra las reservas existientes (requested_time en leads más citas confirmadas), devolviendo una lista precisa de ventanas abiertas a la UI de reservas.

Bóveda Clínica Integral: El esquema relacional captura contexto clínico profundo: psychiatric_history, medical_conditions, current_medication, reason_for_consultation, e informes de progreso por sesión a través de la tabla patient_sessions. La carga anticipada de Eloquent (Patient::with('appointments'), Lead::with('specialist')) previene patrones de ejecución de consultas N+1 en vistas de panel y detalle.

Capa Anti-Spam Honeypot: PublicController@storeLead incluye un campo oculto _honeypot. Los bots automatizados que completan esta trampa son bloqueados inmediatamente con un estado HTTP 403, eliminando la necesidad de widgets CAPTCHA generadores de fricción y preservando las tasas de conversión de formularios públicos en dispositivos móviles.

Stack de Rendimiento y SEO Local: OPcache de PHP y el caché de configuración de Laravel están habilitados en producción para minimizar el TTFB. El enrutamiento dinámico en PublicController mapea categorías específicas de terapia a metadatos SEO especializados (títulos personalizados, meta descripciones, secciones de FAQ e iconos), apuntando a la intención local de la región "Guacara, Carabobo" para reducir el CAC orgánico.

┌─────────────┐     ┌──────────────────────────────────────────────┐
│   Paciente  │────▶│         Monolito Laravel 12                  │
│ (Navegador) │     │  (PHP 8.2 / Tailwind 3 / AlpineJS / Breeze)  │
└─────────────┘     │                                              │
                    │  ┌─────────┐ ┌─────────┐ ┌─────────────┐   │
                    │  │  Lead   │ │ Patient │ │ Appointment │   │
                    │  │  Model  │ │ Model   │ │   Model     │   │
                    │  │+Webhook │ │+Sessions│ │+Observer    │   │
                    │  │ Ingest  │ │+History │ │+Calendar    │   │
                    │  └────┬────┘ └────┬────┘ └──────┬──────┘   │
                    └───────┼───────────┼─────────────┼──────────┘
                            │           │             │
              ┌─────────────┘           │             │
              ▼                         ▼             ▼
       ┌──────────────┐        ┌────────────────┐  ┌─────────────┐
       │   MySQL      │        │ Google Calendar│  │     n8n     │
       │ (Producción) │        │ API (OAuth 2.0)│  │  Webhook    │
       │              │        │+ Token Refresh │  │  Ingestion  │
       └──────────────┘        └────────────────┘  └─────────────┘
       
       ┌──────────────┐
       │   SQLite     │
       │ (Local/Dev)  │
       └──────────────┘

ROI Medible

Conflictos de Agenda:

Cruzamiento manual de calendarios y herramientas de reserva genéricas → API de disponibilidad en tiempo real con cálculo de slots mediante array_diff contra reservas activas.

  • Antes: Tasa de doble-reservas del 15–20% y huecos frecuentes en la agenda causando inasistencias de pacientes.
  • DESPUÉS: Cero doble-reservas; slots expuestos dinámicamente a la superficie pública de reservas con precisión en tiempo real.

Onboarding Lead-to-Paciente:

Transcripción manual de datos de formularios web a ficheros clínicos → Transacción atómica del LeadController con perfil de Patient autoaprovisionado, evento de calendario y mapeo de servicios.

  • Antes: 10–15 minutos de entrada de datos administrativos por paciente nuevo; errores de transcripción frecuentes y registros duplicados.
  • DESPUÉS: Conversión atómica <2 segundos con cero registros huérfanos y sincronización garantizada con el calendario.

Integridad de Datos Clínicos:

Notas fragmentadas a través de documentos no estructurados y hojas de cálculo → Bóveda relacional centralizada con patient_sessions, historial psicológico, seguimiento de medicación e informes de progreso de sesiones.

  • Antes: Imposibilidad de ejecutar análisis longitudinal o recuperar el contexto completo del paciente rápidamente; riesgo de cumplimiento por registros faltantes.
  • DESPUÉS: Línea de tiempo clínica completa accesible en una única consulta con carga anticipada.

Conversión de Formularios Públicos:

Desafíos CAPTCHA visibles añadiendo fricción a la captación de leads → Campo honeypot oculto bloqueando bots con un 403 inmediato.

  • Antes: Tasas de abandono por CAPTCHA del 5–10% en dispositivos móviles, reduciendo directamente el throughput del embudo.
  • DESPUÉS: Fricción visible cero; spam bloqueado en la capa del controlador sin intervención del usuario.

Visibilidad SEO Local:

HTML estático sin esquemas ni metadatos localizados → Enrutamiento dinámico por categoría de terapia con títulos, meta descripciones y estructuras de FAQ adaptadas a la intención terapéutica regional.

  • Antes: Visibilidad orgánica cercana a cero para búsquedas de terapia local en la región objetivo.
  • DESPUÉS: Superficie optimizada para motores de búsqueda apuntando a modalidades locales específicas, impulsando crecimiento mensurable de impresiones orgánicas y reduciendo la dependencia de adquisición pagada.

Confiabilidad de Sincronización de Calendario:

Entrada manual de citas en Google Calendar sin sincronización bidireccional → Integración nativa OAuth 2.0 con pre-refresco de token a 5 minutos y hooks del ciclo de vida de eventos.

  • Antes: Citas perdidas por estado de calendario obsoleto y retraso de actualización manual.
  • DESPUÉS: Sincronización bidireccional en tiempo real con gestión automatizada del ciclo de vida de tokens y lógica de refresco tolerante a fallos.

Escrito por

Miguel Ortiz

Growth Engineer & Technical SEO

Hablemos de un Desafío Similar