# #TheGLMWay — Workflow de Automatización Onboarding (n8n) > **Sistema automatizado que detecta nuevos ingresos en BambooHR, los inscribe en la sesión de GLM Way correspondiente a su idioma, gestiona toda la comunicación pre-sesión y recolecta la evaluación post-sesión.** > > **ID Workflow:** `F1iPg8hktNAebZua` · **Nombre:** `GLMWay` · **Timezone:** `America/Santo_Domingo` · **Estado:** Activo --- ## 1. Propósito Centralizar y automatizar el ciclo de vida completo de la capacitación **#TheGLMWay** para nuevos colaboradores: 1. Detectar el ingreso (hireDate) en **BambooHR** el día 1. 2. Asignar al colaborador a la **sesión más cercana** según su idioma (país → ES/EN). 3. Notificarle por email con fecha, hora localizada y enlace de Meet. 4. Avisar al **coordinador RRHH** del país y al **facilitador** 48h / 24h antes. 5. Enviar **recordatorio** al colaborador 24h antes. 6. Verificar **asistencia real** vía Google Meet API y enviar **evaluación** (Google Forms) post-sesión. 7. Medir métricas de evaluaciones enviadas/respondidas. Todo persiste en **Supabase** (`inscritos_glmway`, `sesiones_glmway`, `coordinadores_rrhh_glmway`) y se sincroniza con **Google Calendar / Sheets / Chat**. --- ## 2. Arquitectura General ``` BambooHR (Webhook + Polling) ─┬─► Supabase (inscritos_glmway) │ Google Calendar (ethen@) ─────┼─► Supabase (sesiones_glmway) Google Sheets (Calendario) ───┼─► Supabase (coordinadores_rrhh_glmway) │ ├─► Gmail (notificación + recordatorio + evaluación) ├─► Google Chat (facilitador + coordinadores RRHH) └─► WhatsApp Evolution API (alerta sin coordinador) Meet API ─────────────────────► Asistencia ─► Evaluación Gitea (git.digitalcompass.agency) ─► Backup JSON del workflow ``` **Credenciales utilizadas:** | Servicio | Credencial n8n | Uso | |---|---|---| | BambooHR | `Eidan - BambooHR account` + `GLM Credential` (Basic Auth) | `Get an employee`, `HTTP /employees/{id}` | | Supabase | `Sofia test - Supabase account` | Tablas `inscritos_glmway`, `sesiones_glmway`, `coordinadores_rrhh_glmway`, RPCs | | Gmail | `Eidan - Gmail account` (OAuth2) | 10 nodos `Send a message` | | Google Calendar | `Eidan - Google Calendar account` | Lectura `ethen@gomezleemarketing.com` query `#TheGLMWay` | | Google Sheets | `Eidan - Google Sheets account` | Sheet `1Xs-MkeXiZvWeJuwm-IblcI-ilr7lyhMtED0R7wczmRg` | | Google Chat | `Eidan - Chat account` (OAuth) | Spaces `AAQAgX1mjaY` (GLMWay), `AAQAQj1t9AA` (RRHH) | | Google Contacts | `Eidan - Google Contacts account` | Resolución `contactId` para menciones `` en Chat | | Google Meet | `Eidan - Google account` (OAuth2) | `meet.googleapis.com/v2/conferenceRecords` | | Gitea | `Eidan - gitea` (headerAuth) | Backup `mgomez/calendario-glm-way:glmway-workflow.json` | | n8n API | `Eidan - n8n account` | `Get a workflow` para backup | | WhatsApp | Evolution API `wsp.gomezleemarketing.com` `botsoporte` | Alerta a `18494105855@s.whatsapp.net` | --- ## 3. Modelo de Datos (Supabase) ### `inscritos_glmway` | Campo | Tipo | Descripción | |---|---|---| | `empleado_id` | text PK | ID BambooHR | | `nombre` | text | displayName | | `email` | text | workEmail | | `departamento` | text | department | | `division` | text | division | | `puesto` | text | jobTitle | | `pais` | text | location (BambooHR) | | `fecha_ingreso` | date | hireDate `YYYY-MM-DD` | | `fecha_sesion_inscrito` | text | `"14 January"` (sesión asignada) | | `sesion_id` | text | FK `sesiones_glmway.id` | | `estado` | text | `inactivo` → `notificado` → `recordado` → `evaluacion_enviada` | | `coordinador_rrhh` | text | nombre coordinador del país | | `instructor` | text | actualizado post-sesión | | `asistio_evento_glmway` | bool | asistencia detectada Meet | | `fecha_sesion_inscrito` | text | reutilizado para cálculo 24h | **Estados:** `inactivo` (registrado sin email), `notificado` (email inicial enviado), `recordado` (recordatorio 24h), `evaluacion_enviada` (evaluación enviada). ### `sesiones_glmway` | Campo | Descripción | |---|---| | `id` / `meet_id` | ID evento Calendar / Meet | | `titulo` | `"Entrenamiento #TheGLMWay"` o `"#TheGLMWay Training"` | | `fecha_sesion` | `"14 January"` | | `hora_inicio` / `hora_fin` | `HH:mm` (hora Santo Domingo) | | `instructor` | Nombre del facilitador | | `correo_instructor` | Email | | `enlace_reunion` / `url` | Meet link | | `idioma` | `ES` / `EN` | | `participantes` | JSONB `{"RD": [{nombre, correo, departamento, division}], "Mexico": [...]}` | RPCs: `insertar_participantes_sesion_glmway(p_sesion_id, p_pais, p_empleados)`, `sync_sesiones_glmway`, `insertar_coordinadores_rrhh_glmway`, `incrementar_evaluaciones_enviadas_glmway`, `incrementar_evaluaciones_resueltas_glmway`. ### `coordinadores_rrhh_glmway` | Campo | Descripción | |---|---| | `pais` | País asignado | | `nombre` | Nombre completo | | `correo` | Email | | `estado` | `no_notificado` (filtro) | --- ## 4. Lógica de Idioma por País ```js // ES = país NO está en lista Caribe anglófono // EN = país SÍ está en lista ["Jamaica","Trinidad and Tobago","Barbados","Saint Thomas","St. Croix","Tobago","Guyana","Aruba"] ``` Todos los nodos `Pais?`, `Pais?1`, `Pais?2`, `Pais?3`, `Pais?4` usan esta lista. Si falta coordinador para el país, se dispara alerta WhatsApp a Matos (`18494105855`). Conversión de hora: todos los `Cambiar zona horaria*` (6 nodos) toman `hora_inicio` en `America/Santo_Domingo` como base y la convierten con `Intl.DateTimeFormat` al timezone del país del colaborador (mapa `TIMEZONES` con 25 países). --- ## 5. Flujos Detallados (10 sub-workflows en 1 workflow) ### Flujo A — Notificación Inicial (Webhook BambooHR) **Trigger:** `Webhook POST /glmway` (BambooHR webhook nuevos ingresos) ``` Webhook → Get an employee → Info extra (hireDate, supervisorEmail, jobTitle) → Calcular fecha hireDate (isValid = hireDate <= today) → Tiene correo y pais? ─┬─ NO → Insertar inscritos (inactivo) └─ SI → Entra hoy? ─┬─ NO → Insertar inscritos (inactivo) └─ SI → Obtener coordinador3 → Hay coordinador? ─ NO → Alerta WhatsApp + Pais? SI → Pais? → [ES/EN] → Crear variable idioma → Obtener sesiones (filtro idioma) → Calcular sesion mas cercana → Guardar datos empleado+sesion (estado=notificado) → Convertir fecha a es/en → Cambiar zona horaria → Send Gmail (ES/EN) → Registrar empleado a sesión (RPC) ``` *Templates Gmail:* `Entrenamiento #TheGLMWay` / `#TheGLMWay Training` con `fechaFormateada`, `hora_convertida`, `enlace_reunion`. ### Flujo B — Reintento Inactivos (Polling Horario) **Trigger:** `Schedule Trigger1` — cada hora ``` Obtener inactivos (estado=inactivo) → Loop Over Items (SplitInBatches) → Get an employee1 → Info extra 1 → Calcular fecha hireDate1 → Tiene correo y pais?1 → Entra hoy?1 ─ SI → Obtener coordinador4 → Hay coordinador?1 → Pais?1 → (mismo camino ES/EN) → Actualizar datos del empleado y sesion asignada → ... → Send Gmail 1 → NO → Wait2 2s → Loop ``` ### Flujo C — Recordatorio 24h al Colaborador **Trigger:** `Schedule Trigger2` — martes y miércoles 17:00 ``` Obtener notificados (estado=notificado) → Loop Over Items1 → Calcular falta 24h o es el mismo dia (parsea "14 December", diferenciaDias 0-1) → Falta 24h para el evento? ─ SI → Obtener sesion → Pais?2 → Convertir fecha 2 → Cambiar zona horaria 4/5 → Send Gmail recordatorio → Actualizar estado a recordado → Wait1 2s → Loop ─ NO → Wait1 → Loop ``` *Asunto recordatorio ES:* `Entrenamiento #TheGLMWay` — *Texto:* “Te recordamos que la sesión será el {{fecha}} a las {{hora}}...” ### Flujo D — Notificación a Facilitador (48h / día del evento) **Trigger:** `Schedule Trigger` — miércoles 16:00 ``` Obtener sesion1 (todas) → Calcular sesion mas cercana4 (match hoy) → Obtener miembros (Chat Space AAQAgX1mjaY) → Limpiar id → Get a contact → Unir datos2 → Hay inscritos? ─ SI → Es espanol o ingles1 (switch por idioma sesión) → Formatear datos ingles/espanol (card con totalParticipantes, enlace) → Enviar notificación a glmway notificaciones ─ NO → Es espanol o ingles (switch) → Formatear datos ingles1/espanol1 (card “No hay participantes”) → Enviar notificación ``` *Si hay inscritos:* `🔔 *Recordatorio de #TheGLMWay para hoy* \n Instructor: ` con `totalParticipantes` y `Entrar a reunión`. *Si no:* `🔔 *Aviso de sesión sin participantes*`. ### Flujo E — Notificación a Coordinador RRHH (48h + 24h) **Trigger:** `Schedule Trigger3` — martes 16:00 + miércoles 16:00 ``` Obtener sesiones (all) → Calcular falta 48h o 24h1 (sesión más cercana futura) → Hay inscritos en la sesion? ─ SI → Obtener coordinadores1 (estado=no_notificado filtro) → Matchear coordinador y pais inscrito (normaliza acentos) → Obtener miembros RRHH (AAQAQj1t9AA) → Limpiar id1 → Get a contact1 → Unir datos3 (match nombre coordinador ↔ Google Contact) → Pais?3 → Formatear datos ingles2/espanol2 → Enviar notificación a rrhh notificaciones ``` *Card RRHH:* `🔔 *Aviso para Coordinadores RRHH*` con `Total inscritos: X` + listado por país `📍 RD (2): • Nombre (Depto | División)`. ### Flujo F — Post-Sesión: Asistencia + Evaluación **Trigger:** `Schedule Trigger7` — miércoles 19:50 (post-sesión) ``` Hora de la reunion (timestamp → startTime T22:50:24Z, endTime T24:00:00Z, fechaCorta "11 August") → Obtener conferenceRecord (Meet API v2) → Buscar sesion con hora UTC (filtra por fecha, distanciaMs mínima) → Obtener participantes (conferenceRecords/{name}/participants) → Detectar participantes en la sesion (extrae signedinUser.displayName) → Obtener sesion2 (por fecha_sesion) ─┬─→ Buscar participantes (match normalizado displayName ↔ nombre registrado) │ └─→ Actualizar instructor de inscritos → Split Out (participantes) → Actualizar asistencia (supabase asistio_evento_glmway bool, wfts por nombre) → Asistio? (solo si asistio=false se envía evaluación — filtra asistentes? verificar lógica) → Cortar nombre (primerNombre) → Pais?4 → Send Gmail evaluación (HTML con branding GLM + botón Google Forms) → Actualizar estado a evaluacion_enviada → Incrementar conteo evaluaciones enviadas (RPC) ``` *Evaluación ES:* `Evaluación #TheGLMWay` → `https://docs.google.com/forms/d/e/1FAIpQLSf8QUfmXulvw4e9BwZjTFG4mEUWybdFSAes_dK-OFnWN-L43Q/viewform` con header `#2d3748` + logo GLM. *EN:* `#TheGLMWay Quiz` → `...Sf61KpkDr...`. ### Flujo G — Webhook Evaluación Respondida **Trigger:** `Webhook1 POST /evaluacion-glmway` ``` Webhook1 → Incrementar conteo de evaluaciones respondidas (RPC incrementar_evaluaciones_resueltas_glmway) ``` ### Flujo H — Sincronización Anual Calendario → Sheets **Trigger:** `Schedule Trigger4` — `0 0 0 1 1 *` (1 de enero) ``` Obtener entrenamientos ingles y espanol (Calendar año completo query #TheGLMWay) → Formatear fecha (DD Month) → Borrar fechas (Sheets rango A2:D80) → Wait4 → Insertar fechas (appendOrUpdate por Fecha) ``` ### Flujo I — Sincronización Diaria Coordinadores **Trigger:** `Schedule Trigger5` — diario 11:00 ``` Obtener coordinadores (Sheets “Coordinadores RRHH”) → Aggregate (input) → Insertar/actualizar coordinadores a la bd (RPC) ``` ### Flujo J — Sincronización Diaria Sesiones **Trigger:** `Schedule Trigger6` — miércoles 15:00 ``` Obtener fechas e instructores (Sheets Calendario) + Obtener entrenamientos ingles y espanol1 (Calendar) → Formatear fecha y detectar idioma (detecta "#TheGLMWay Training"→EN, "Entrenamiento #TheGLMWay"→ES, extrae hora_inicio/fin, url) → Unir datos (merge por Fecha) → Edit Fields (meet_id, titulo, fecha_sesion, hora_inicio/fin, instructor, correo_instructor, enlace_reunion, idioma) → Aggregate1 → Insertar fechas1 (RPC sync_sesiones_glmway) ``` ### Flujo K — Backup Automático del Workflow **Trigger:** `When clicking ‘Execute workflow’` (manual) ``` Valores (mgomez / calendario-glm-way / glmway-workflow) → Obtener archivo (Gitea GET contents/*.json) ─┬─ éxito (onError continue) → Get a workflow1 → Code JS2 (base64 JSON + sha) → Actualizar archivo (PUT) └─ error (no existe) → Get a workflow2 → Code JS4 (base64) → Subir archivo (POST) ``` --- ## 6. Diagrama de Estados del Inscrito ``` [BambooHR hireDate futuro] ──► inactivo (insertado sin email/sin fecha) │ ▼ (hireDate == today, con coordinador) notificado ──► (24h antes) ──► recordado │ ▼ (post-sesión Meet) evaluacion_enviada ──► (webhook) conteo respondida++ ``` --- ## 7. Notificaciones | Destinatario | Canal | Cuándo | Formato | |---|---|---|---| | Nuevo ingreso | Gmail | Día 1 (Flujo A/B) | Texto plano ES/EN con fecha, hora local, Meet link | | Nuevo ingreso | Gmail | 24h antes (Flujo C) | Recordatorio ES/EN | | Facilitador | Google Chat `AAQAgX1mjaY` | Miércoles 16:00 (Flujo D) | CardV2 con mención `` + total participantes | | Coordinador RRHH | Google Chat `AAQAQj1t9AA` | Mar/Mié 16:00 (Flujo E) | CardV2 por país con listado detallado | | Coordinador RRHH | WhatsApp | Sin coordinador (Flujo A/B) | `⚠️ No hay coordinador RRHH en el país...` | | Inscrito | Gmail | Post-sesión 19:50 (Flujo F) | HTML evaluación con botón a Google Forms | --- ## 8. Instalación y Configuración ### Requisitos - n8n ≥ 1.x con timezone `America/Santo_Domingo` - Credenciales: BambooHR API Key, Supabase service_role, Google OAuth (Calendar, Sheets, Gmail, Meet, Chat, Contacts), Gitea token, Evolution WhatsApp API key - Supabase: crear tablas + RPCs (`insertar_participantes_sesion_glmway`, `sync_sesiones_glmway`, etc.) - Google Sheet `Calendario #TheGLMWay` con hojas `Calendario` (col Fecha) y `Coordinadores RRHH` ### Importar 1. n8n → `Import from File` → `GLMWay.json` 2. Reasignar credenciales (IDs en JSON son de instancia origen `Eidan`) 3. Activar workflow 4. Configurar webhook BambooHR → `https:///webhook/glmway` (POST) 5. Verificar `Schedule Triggers` — respetar horario Santo Domingo ### Variables Clave a Revisar - `WEBHOOK_URL` base, `wsp.gomezleemarketing.com` apikey, `git.digitalcompass.agency` repo, `ethen@gomezleemarketing.com` calendar, Chat Space keys/tokens (rotar — están hardcodeados en nodos HTTP), Supabase URL `dbit.digitalcompass.agency`. --- ## 9. Operación y Monitoreo - **Error Workflow:** `lX5ITf9s4TSQglsM` (configurado en `settings.errorWorkflow`) — revisar ejecuciones fallidas. - **Reintentos:** `Informacion extra del empleado` con retry 2s, otros con `retryOnFail: true`. - **Loops:** `Loop Over Items` / `Loop Over Items1` con `Wait` 2s para evitar rate-limit BambooHR/Gmail. - **Logs:** `console.log` en nodos Code (`Sesiones procesadas`, eventos ignorados). - **PinData:** Triggers con datos de prueba agosto 2026 — remover en prod. --- ## 10. Riesgos y Recomendaciones | Riesgo | Mitigación sugerida | |---|---| | Tokens Chat hardcodeados (`key=AIza...&token=...`) en URLs | Migrar a credencial `googleChatOAuth2Api` o variable de entorno, rotar tokens | | `service_role` JWT expuesto en nodos HTTP | Mover a credencial Header Auth, no en claro | | `TIMEZONES` incompleto (faltan países GLM) | Centralizar en tabla `paises_timezones` | | Parseo fecha `"14 January"` frágil (locale/año) | Usar `date-fns` con año explícito o ISO en `sesiones_glmway` | | Duplicados por webhook + polling horario | Añadir constraint único `empleado_id` + upsert | | `Wait` fijo 2s puede exceder límites Gmail (100/día) | Batch con `SplitInBatches` size controlado | --- ## 11. Archivos Relacionados - `GLMWay.json` — definición completa del workflow (importable en n8n) - `README_GLMWay.md` — este documento --- *Documentado a partir de `GLMWay.json` v`f657df3d-e5b6-445e-ad75-df66da81ebf3` — última edición inspeccionada 27/08/2026.* *GomezLee Marketing — Automatización #TheGLMWay.*