# 🎂 Workflow: `hbdwsp` — Bot de Cumpleaños GLM (WhatsApp) **ID del Workflow:** `ZzH2eMPrsqlY7WPz` **Autor:** Eidan Then (`ethen@gomezleemarketing.com`) **Última actualización:** 12 de junio de 2026 **Versión activa:** `2d27b5f7-f5c4-41f9-b008-d8f3a50557a5` (v251) **Estado:** ✅ Activo **Zona horaria:** America/Santo_Domingo (UTC-04:00) --- ## Descripción General Este workflow automatiza el envío de tarjetas de cumpleaños personalizadas al grupo de WhatsApp corporativo de GLM. Cada día a las 10:00 AM, el sistema revisa los empleados registrados en BambooHR, filtra los que cumplen años ese día, genera una tarjeta visual personalizada con su foto y la envía al grupo correspondiente. El flujo excluye automáticamente a empleados de países no hispanos (Jamaica, Trinidad & Tobago, Barbados, etc.) y maneja casos donde el empleado no tiene fecha de cumpleaños o foto registrada. --- ## Arquitectura del Flujo ``` Schedule Trigger (10am) └── BambooHR (Reporte CSV #1509) └── Google Drive (Guardar CSV) └── Google Sheets (Leer datos) └── Pais? (Filtrar hispanos) ├── [TRUE] Obtener empleado (BambooHR) │ └── Merge (unir datos Sheets + Bamboo) │ └── Loop Over Items │ └── Tiene fecha? │ ├── [FALSE] → Wait3 → Loop (siguiente) │ └── [TRUE] → Coincide con fecha de hoy? │ ├── [FALSE] → Wait3 → Loop │ └── [TRUE] → Tiene foto? │ ├── [TRUE] → Foto Empleado URL │ └── [FALSE] → URL si no tiene foto (bizcocho) │ └── HTML (generar tarjeta) │ └── HTTP Request (screenshot browserless) │ └── Extract from File │ └── Enviar imagen WhatsApp │ └── Wait (3s) │ └── Loop (siguiente) └── [FALSE] Ignorado (país no hispano) ``` --- ## Nodos del Workflow ### 1. Schedule Trigger - **Tipo:** `scheduleTrigger` - **Configuración:** Se ejecuta todos los días a las **10:00 AM** (hora Santo Domingo, UTC-4). - **Notas:** El trigger está pinned con fecha `2026-05-29` para pruebas manuales. --- ### 2. BambooHR (Reporte CSV) - **Tipo:** `bambooHr` — `companyReport` - **Reporte ID:** `1509` - **Formato:** CSV - **Credencial:** `Eidan - BambooHR account` - **Función:** Descarga el listado completo de empleados activos con sus datos (incluyendo Birthday, Location, EEID, Work Email). --- ### 3. Google Drive (Guardar CSV) - **Tipo:** `googleDrive` — `update` - **Archivo destino:** `HBD.csv` (`1Jw9yOyIKB1QLcr9m8ObGP9PK0tC9z3n6`) - **Credencial:** `Eidan - Google Drive account` - **Función:** Actualiza el archivo CSV en Google Drive con los datos más recientes de BambooHR. Sirve como respaldo y fuente de verdad para Google Sheets. --- ### 4. Google Sheets (Leer datos) - **Tipo:** `googleSheets` — read - **Spreadsheet:** `HBD` (`1p0bCvHAIHjobiPhDfmTshZWvRP6PWAmmTtSoX77lBmM`) - **Hoja:** `HBD` (gid: `759771456`) - **Credencial:** `Eidan - Google Sheets account` - **Función:** Lee los datos procesados del spreadsheet. Aquí se encuentran campos adicionales como `Location` que se usa para el filtro de país. --- ### 5. Pais? (Filtro de país) - **Tipo:** `if` - **Lógica:** `AND` de condiciones `notEquals` - **Países excluidos (FALSE):** - Jamaica - Trinidad and Tobago / Trinidad & Tobago / Tobago - Barbados - Saint Thomas - St. Croix - **TRUE:** El empleado es de un país hispano → el flujo continúa. - **FALSE:** El empleado es de un país no hispano → se ignora silenciosamente. --- ### 6. Obtener empleado - **Tipo:** `bambooHr` — `get` - **Employee ID:** `{{ $json.EEID }}` - **Credencial:** `Eidan - BambooHR account` - **Configuración:** `retryOnFail: true` - **Función:** Obtiene el perfil completo del empleado desde BambooHR, incluyendo `photoUrl` y `photoUploaded`. --- ### 7. Merge - **Tipo:** `merge` — combine (enrich) - **Join por:** `workEmail` ↔ `Work Email` - **Modo:** `enrichInput1` (enriquece los datos de Bamboo con los de Google Sheets) - **Función:** Combina en un solo objeto los datos de BambooHR (foto, cargo, etc.) con los de Google Sheets (Location, Birthday, etc.). --- ### 8. Loop Over Items - **Tipo:** `splitInBatches` - **Función:** Procesa cada empleado de forma individual y secuencial. Al terminar con uno, vuelve al siguiente. Cuando no quedan más ítems, el flujo termina. --- ### 9. Tiene fecha? - **Tipo:** `if` - **Condición:** `!$json.Birthday` es `true` - **TRUE (sin fecha):** → `Wait3` → vuelve al loop (siguiente empleado). - **FALSE (tiene fecha):** → continúa hacia `Coincide con fecha de hoy?`. --- ### 10. Coincide con fecha de hoy? - **Tipo:** `if` - **Condición:** `$json.Birthday` equals `$now.format('MMMM dd')` (con espacios) - **Validación:** `loose` (tipos flexibles) - **`onError`:** `continueRegularOutput` - **TRUE:** La fecha coincide → continúa el flujo. - **FALSE:** No es el cumpleaños hoy → `Wait3` → siguiente empleado. --- ### 11. Tiene foto? - **Tipo:** `if` - **Condición:** `$json.photoUploaded == true` es `true` - **TRUE:** → `Foto Empleado URL` (usa la foto real del empleado) - **FALSE:** → `URL si no tiene foto` (usa imagen de bizcocho) --- ### 12. Foto Empleado URL / URL si no tiene foto - **Tipo:** `set` - **Foto real:** `{{ $json.photoUrl }}` (URL de BambooHR) - **Foto fallback:** `https://gomezleemarketing.com/wp-content/uploads/2026/03/hbdcake.png` - **Output:** Campo `photoUrl` que se pasa al nodo HTML. --- ### 13. HTML (Generación de tarjeta) - **Tipo:** `html` - **Dimensiones:** 1080 × 1920 px (formato Instagram Stories / WhatsApp) - **Background:** `https://gomezleemarketing.com/wp-content/uploads/2026/03/Comunicacion-de-Cumpleanos-Background.jpg` - **Contenido dinámico:** - Foto del empleado (circular, 530px) - Nombre del empleado (`firstName`) - Fecha de cumpleaños formateada en español (ej: "12 de junio") - **Nota:** Los divs `.hello`, `.message`, `.position` y `.team` están vacíos en la versión actual. Pueden ser rellenados con texto personalizado. --- ### 14. HTTP Request (Screenshot) - **Tipo:** `httpRequest` POST - **URL:** `https://pruebadetemplate-browserless.k11ytw.easypanel.host/chromium/screenshot` - **Token:** `8d66338953f117a7c29c55364c8c9e6d` - **Viewport:** `1080 × 1920` - **Response format:** `file` (binario) - **Función:** Convierte el HTML generado en una imagen PNG usando Browserless (Chromium headless) alojado en Easypanel. --- ### 15. Extract from File - **Tipo:** `extractFromFile` — `binaryToPropery` - **Función:** Convierte el binario de la imagen en base64 (`data`) para enviarlo a la API de WhatsApp. --- ### 16. Enviar mensaje al whatsapp imagen - **Tipo:** `httpRequest` POST - **URL:** `https://wsp.gomezleemarketing.com/message/sendMedia/botsoporte` - **API Key:** `C267126ABB45-4C12-B626-6BAB1833F5D7` - **Número destino:** `120363404268176806@g.us` (Grupo oficial Motorola) - **Tipo de media:** `image` - **Media:** `{{ $json.data }}` (base64) - **Delay:** 2000ms --- ### 17. Wait (entre empleados) - **Tipo:** `wait` - **Duración:** 3 segundos - **Función:** Pausa entre el envío y la vuelta al loop para no saturar la API de WhatsApp. ### Wait3 (skip al siguiente) - **Tipo:** `wait` - **Duración:** 1 segundo - **Función:** Pequeña pausa antes de saltar al siguiente empleado cuando no cumple años o no tiene fecha. --- ## Subworkflow: Respaldo en Gitea Este subworkflow es independiente (trigger manual) y guarda/actualiza el JSON del workflow en Gitea como respaldo de versión. ### Nodos: | Nodo | Función | |------|---------| | `Valores` | Define `repo_name: wsp-cumpleanos` y `file_name: hbd-panama-motorola` | | `Obtener archivo` | GET al repo Gitea para obtener el SHA actual del archivo | | `Get a workflow1/2` | Obtiene el JSON del propio workflow (`ZzH2eMPrsqlY7WPz`) vía API de n8n | | `Code in JavaScript` | Prepara payload con SHA para **actualizar** (PUT) | | `Code in JavaScript1` | Prepara payload sin SHA para **crear** (POST) | | `Actualizar archivo` | PUT al endpoint de Gitea (actualiza si ya existe) | | `Subir archivo` | POST al endpoint de Gitea (crea si no existe) | **Repositorio:** `https://git.digitalcompass.agency/EidanThen/wsp-cumpleanos` --- ## Requisitos de Instalación ### Servicios externos requeridos | Servicio | Uso | Credencial | |----------|-----|------------| | **BambooHR** | Fuente de datos de empleados | `Eidan - BambooHR account` | | **Google Drive** | Almacenamiento del CSV actualizado | `Eidan - Google Drive account` | | **Google Sheets** | Lectura de datos procesados | `Eidan - Google Sheets account` | | **Browserless** (Easypanel) | Renderizado HTML → PNG | Token en URL | | **EvolutionAPI** (WhatsApp) | Envío de imágenes al grupo | API Key en header | | **Gitea** (opcional) | Respaldo del workflow JSON | `Eidan - gitea` | | **n8n API** (opcional) | Leer JSON del workflow para backup | `Eidan - n8n account` | --- ### Paso 1: Configurar credenciales en n8n 1. Ve a **Settings → Credentials** en n8n. 2. Crea las siguientes credenciales: #### BambooHR - **Tipo:** BambooHR API - **API Key:** (tu API key de BambooHR) - **Subdomain:** `gomezleemarketing` #### Google Drive / Sheets - **Tipo:** Google OAuth2 - Autoriza con la cuenta `ethen@gomezleemarketing.com` - Aplica a ambos nodos (Drive y Sheets) #### Gitea (Header Auth) - **Tipo:** Header Auth - **Name:** `Authorization` - **Value:** `token TU_TOKEN_GITEA` --- ### Paso 2: Configurar Google Sheets El spreadsheet `HBD` debe tener al menos las columnas: | Columna | Descripción | |---------|-------------| | `EEID` | ID del empleado en BambooHR | | `Work Email` | Email corporativo (clave de join con Bamboo) | | `Birthday` | Fecha en formato `MMMM dd` (ej: `June 12`) | | `Location` | País del empleado (ej: `Dominican Republic`) | | `firstName` | Nombre del empleado | --- ### Paso 3: Configurar EvolutionAPI (WhatsApp) El workflow usa EvolutionAPI como intermediario para WhatsApp: - **Base URL:** `https://wsp.gomezleemarketing.com` - **Instancia:** `botsoporte` - **API Key:** configurada en el header `apikey` Asegúrate de que la instancia `botsoporte` esté conectada y activa antes de ejecutar el workflow. --- ### Paso 4: Importar el workflow en n8n 1. En n8n, ve a **Workflows → Import from file/URL**. 2. Pega el JSON del workflow o importa desde Gitea. 3. Asigna las credenciales correctas a cada nodo. 4. Activa el workflow con el toggle. --- ## Configuración del Grupo de WhatsApp | Entorno | Número del grupo | |---------|-----------------| | **Producción (Motorola)** | `120363404268176806@g.us` | | **Pruebas** | `120363426353723539@g.us` | Para cambiar el grupo destino, editar el campo `number` en el nodo **Enviar mensaje al whatsapp imagen**. --- ## Personalización de la Tarjeta HTML La tarjeta visual se genera en el nodo `HTML`. Los siguientes divs están disponibles para personalización: ```html
¡Feliz Cumpleaños!
Tu mensaje aquí...
{{ cargo del empleado }}
Equipo GLM
``` Para agregar el cargo dinámico, incluir en el nodo `Obtener empleado` el campo `jobTitle` y referenciarlo con `{{ $json.jobTitle }}`. --- ## Solución de Problemas ### El workflow no envía tarjetas aunque es el cumpleaños del empleado **Causa probable:** El formato de la fecha en Google Sheets no coincide con `$now.format('MMMM dd')`. **Solución:** Asegúrate de que el campo `Birthday` en el CSV/Sheets tenga el formato `June 12` (no `06/12` ni `12-06-1990`). El nodo `Coincide con fecha de hoy?` también tiene un espacio trailing en el `rightValue`; revisar que no cause falsos negativos. --- ### La imagen no se genera (error en Browserless) **Causa probable:** La instancia de Browserless está caída o el token expiró. **Solución:** 1. Verificar que el contenedor de Browserless esté corriendo en Easypanel. 2. Probar manualmente con curl: ```bash curl -X POST "https://pruebadetemplate-browserless.k11ytw.easypanel.host/chromium/screenshot?token=8d66338953f117a7c29c55364c8c9e6d" \ -H "Content-Type: application/json" \ -d '{"html": "

Test

", "viewport": {"width": 1080, "height": 1920}}' ``` --- ### El mensaje no llega al grupo de WhatsApp **Causa probable:** La instancia `botsoporte` de EvolutionAPI no está conectada. **Solución:** 1. Ir al panel de EvolutionAPI en `https://wsp.gomezleemarketing.com`. 2. Verificar que la instancia `botsoporte` muestra estado `open`. 3. Si está desconectada, escanear el QR de WhatsApp nuevamente. --- ### Empleados de países no hispanos reciben tarjetas **Causa probable:** El campo `Location` en Google Sheets tiene un valor diferente al esperado (ej: `Trinidad` en vez de `Trinidad and Tobago`). **Solución:** Revisar el nodo `Pais?` y agregar las variantes del nombre del país en las condiciones `notEquals`. --- ## Flujo de Error El workflow tiene configurado el workflow de error `lX5ITf9s4TSQglsM`. Ante un error crítico no manejado, se disparará ese workflow de manejo de errores. --- ## Notas de Mantenimiento - El reporte de BambooHR (#1509) debe incluir los campos `Birthday`, `Location`, `EEID`, y `Work Email`. - La imagen de fondo y el fallback de bizcocho están alojadas en el servidor de WordPress de GLM. Si cambian de dominio, actualizar las URLs en el nodo `HTML` y en el nodo `URL si no tiene foto`. - El workflow respalda su propio JSON en Gitea bajo `EidanThen/wsp-cumpleanos/hbd-panama-motorola.json`. Ejecutar el subworkflow manual periódicamente o tras cambios importantes.