WhatsApp · Addon
El addon WhatsApp añade un canal de mensajería WhatsApp Business al CRM.
Está pensado para envío de plantillas utility (facturas emitidas, e-CF
aprobados, recordatorios de pago) y para tener una bandeja de entrada
bidireccional cuando el cliente responde.
Qué incluye
- Inbox. Lista de conversaciones bidireccional. Click en una conversación
abre el thread + Customer 360 a la derecha. Polling cada 5 segundos sin
flicker.
- Conexión. Asistente para vincular tu número de WhatsApp Business con
el CRM usando el Embedded Signup oficial de Meta (Fase 3). Ver sección
abajo.
- Settings → Compliance. Dashboard de opt-in (Oleada B.1).
- Botón "WhatsApp" en facturas. Modal de envío de la plantilla
factura_emitida con preview rich y badge de opt-in del contacto.
Conexión (Fase 3 — Embedded Signup)
La pestaña Conexión vincula tu número de WhatsApp Business con SimplifyCRM
sin que tengas que crear una App propia en Meta. Usa el Embedded Signup
oficial: un popup de Meta donde te autenticas con tu cuenta de Facebook,
eliges tu Business Portfolio y tu número, y confirmas los permisos.
Elige cómo conectar
Primero eliges una de dos rutas:
- Uso mi número actual (Coexistence). Sigues usando WhatsApp Business en tu
celular y además respondes desde el CRM, sobre el mismo número. Requiere
que ese número ya esté activo en la app WhatsApp Business del celular.
- Número nuevo para el CRM. Conectas un número dedicado que tú proporcionas
(un SIM/número aparte o un fijo), verificado por SMS o llamada. Ese número
no debe estar en uso en otra cuenta de WhatsApp, y queda solo en el CRM
(no en la app móvil). Ni Meta ni SimplifyCRM proporcionan números.
Antes de empezar (prerequisitos)
Comunes a ambas rutas:
- Una cuenta de Facebook activa.
- Un Business Portfolio en Meta — puedes crearlo durante la conexión si
no tienes uno.
Según la ruta, además: el número ya en WhatsApp Business móvil (Coexistence) o
un número que pueda recibir SMS/llamada (Número nuevo).
Verificación del BM: para enviar a escala y mostrar tu nombre verificado,
el Business Portfolio debe estar verificado con el nombre legal que
coincida con tu RNC (Meta lo revisa; tarda 1-3 días). Puedes conectar antes
de verificar, pero con límites de mensajería más bajos.
Marca los ítems del checklist para habilitar "Conectar mi WhatsApp".
Qué pasa al conectar
- Se abre el popup de Meta. Te autenticas, eliges Portfolio + número y confirmas.
- SimplifyCRM recibe la autorización, guarda tu token encriptado (nunca en
texto plano) y suscribe tu número para recibir mensajes entrantes.
- La pantalla muestra el estado Conectado con tu número y nombre.
A partir de ahí puedes crear plantillas, recibir mensajes y enviar desde el CRM.
Coexistence Mode
Si conectas un número que ya usas en WhatsApp Business móvil, conservas la app
del celular: los mensajes se sincronizan en ambos lados. Importante: abre
tu WhatsApp Business móvil al menos una vez cada 13 días para mantener la
conexión activa.
Notas
- La opción "Configuración pendiente" aparece si el administrador de
SimplifyCRM todavía no terminó de configurar el Embedded Signup en Meta.
- Un mismo número solo puede estar conectado a una plataforma a la vez. Si
tu número ya está en otra (Twilio, Wati, etc.), desconectalo ahí primero.
- Solo el owner o un admin del tenant puede conectar/desconectar.
Settings → Compliance (Oleada B.1)
Ley 172-13 (datos personales) y Ley 210-14 (anti-SPAM) requieren documentar
el consentimiento de cada contacto para recibir mensajería WhatsApp. Esta
sección permite:
- Ver totales por estado (con opt-in, sin opt-in, revocados).
- Filtrar la lista de contactos por estado o buscar por nombre/teléfono/RNC.
- Marcar opt-in manualmente, seleccionando la fuente (onboarding, registro
manual, importado, inbound). La fecha se registra automáticamente.
- Revocar opt-in. El sistema registra el evento en el log de auditoría con
timestamp y usuario.
- Exportar la lista filtrada como CSV (formato compatible con DGII /
fiscalizador externo).
- Ver el log de auditoría (últimos 200 eventos): granted / revoked /
imported / source_changed, con timestamp y usuario.
¿Cuándo es obligatorio el opt-in?
| Tipo de mensaje | Opt-in requerido |
|---|---|
| Plantillas utility (factura, recordatorio, e-CF aprobado) | No — el envío es válido bajo "transactional service" |
| Plantillas marketing | Sí — opt-in granted vigente |
| Mensajes libres (ventana 24h) | Sí — opt-in implícito por inbound del cliente |
El modal de envío de factura muestra el badge informacional, pero **no
bloquea** el envío cuando se trata de plantillas utility. El badge cambia a
verde cuando el contacto tiene opt-in registrado.
Source enum
onboarding_checkbox— el contacto marcó la casilla al registrarse.manual_admin— un usuario del CRM lo registró manualmente.imported— vino de un sistema externo (CSV, integración).webhook_inbound— el cliente envió un mensaje inbound (Fase 2 webhook).revoked_stop_keyword— el cliente respondió STOP/BAJA/CANCELAR vía
inbound y el sistema revocó el opt-in automáticamente (Oleada B.6.1
auto-revoke trigger SQL). Ver "Auto-revoke al recibir STOP" más abajo.
Auto-revoke al recibir STOP (Oleada B.6.1)
Cuando un cliente con opt-in activo responde por WhatsApp con cualquiera
de estas palabras (case-insensitive, como palabra completa):
STOP · BAJA · CANCELAR · NO MAS · DETENER · UNSUBSCRIBE
El sistema revoca automáticamente el opt-in del contacto:
- El webhook recibe el mensaje inbound y lo deja en la cola
whatsapp_webhook_events.
- El parser DB lo proyecta a
whatsapp_messages(direction='inbound'). - Un trigger AFTER INSERT (
trg_whatsapp_messages_optout_auto_revoke)
detecta el keyword y ejecuta:
UPDATE contacts SET whatsappOptInAt=NULL, whatsappOptInSource='revoked_stop_keyword' WHERE id=...
- El trigger existente de auditoría persiste un row en
whatsapp_consent_audit con event='revoked' source='revoked_stop_keyword'.
Cumplimiento Ley 172-13 / 210-14 en tiempo real, sin intervención manual.
En el Inbox:
- La conversación cambia su badge a ⛔ OPT-OUT automáticamente al
llegar el mensaje (el estado se deriva client-side; el polling de 15s
lo refresca en la lista).
- El composer del thread se deshabilita: no se puede enviar mensajes
libres ni plantillas a un contacto que optó-out.
- El Customer 360 sidebar (si la conversación seleccionada es la que
optó-out) se re-monta para reflejar el nuevo estado del opt-in.
Limitaciones honestas:
- El match es por palabra exacta (boundaries de palabra). "STOP" en
"stoplight" NO dispara revocación.
- El match es solo en el campo
body_text. Mensajes con archivo
adjunto sin caption o reactions emoji no son detectados.
- Si un cliente envía STOP y luego cambia de opinión, el opt-in se
puede re-otorgar manualmente desde Settings → Compliance o desde el
form de contacto. El audit log preserva ambas transiciones.
Audit trail automático
El registro en whatsapp_consent_audit lo escribe un trigger DB sobre la
tabla contacts. Esto significa que cualquier path de edición (form del
CRM, edición programática, import) deja rastro sin que el caller tenga que
recordar coordinarlo. Eventos posibles:
granted— primera vez que se otorga.revoked— opt-in retirado.imported— opt-in importado de un sistema externo.source_changed— se cambió la fuente sin retirar el consentimiento.
Form de contacto
En el form de crear/editar contacto hay una sección "Comunicaciones
WhatsApp" con un checkbox "Este contacto acepta recibir mensajes WhatsApp
(opt-in)". Cuando se marca, aparece un dropdown para elegir la fuente del
consentimiento. La decisión queda grabada en el log de auditoría
automáticamente.
Inbox · Customer 360 sidebar
Al abrir una conversación, el sidebar derecho muestra:
- Nombre + teléfono del contacto.
- Email + RNC si aplica.
- Opt-in: fecha del consentimiento si está granted, "pendiente" si no.
Al hacer hover sobre el valor, el tooltip muestra la fuente.
- Acciones rápidas WhatsApp (Oleada B.4) — botones que envían plantillas
utility populadas con datos del contacto. Ver sección siguiente.
Notificaciones de mensajes entrantes (Web Push)
Cuando un cliente te escribe, recibes una notificación con su nombre y un preview del
mensaje — aunque tengas el CRM cerrado, en otra pestaña, o el navegador minimizado. Al
hacer clic, se abre el CRM en esa conversación. Es un push del servidor, así que no depende
de tener el inbox abierto.
Cómo activarlo:
- En el inbox aparece un banner "🔔 Activar". Púlsalo y concede el permiso del navegador
una sola vez (queda recordado en ese dispositivo).
- Si lo bloqueaste por error: candado 🔒 de la barra de direcciones → Notificaciones → Permitir.
- Si el CRM está al frente y lo estás mirando, no te interrumpe con un toast: solo refresca la
lista.
En iPhone / iPad: iOS solo permite notificaciones web a apps instaladas. Toca el botón
Compartir → "Agregar a pantalla de inicio", abre la app instalada desde el ícono, entra al
inbox y pulsa Activar ahí. (Requiere iOS 16.4 o superior.)
Por dispositivo: el permiso es por navegador/dispositivo. Si usas el CRM en la laptop y en
el teléfono, actívalo en cada uno para recibir en ambos.
Mensajes de audio e imágenes (media entrante)
Cuando un cliente envía una nota de voz / audio o una imagen, el Inbox
los muestra reproducibles dentro de la conversación:
- Audio / nota de voz → reproductor con play. Las notas de voz llevan la
etiqueta 🎤 Nota de voz.
- Imagen → se muestra la miniatura; clic para abrirla en grande. Si el
cliente puso texto junto a la imagen, aparece como pie de foto.
Mientras el archivo se descarga (unos segundos tras recibirlo) verás
"⏳ descargando…"; al refrescar la conversación aparece el reproductor o la
imagen.
Cómo funciona (técnico): WhatsApp no envía el archivo en el webhook, solo
un identificador. El sistema lo descarga en segundo plano y lo guarda cifrado
por inquilino en almacenamiento privado; solo tu negocio puede verlo. Los
enlaces de reproducción son temporales (1 hora) y se renuevan solos.
Límites: se capturan audios e imágenes (hasta ~20 MB). Video, documentos y
stickers por ahora solo se registran como tipo, sin reproducción (próxima
oleada). Enviar audio/imagen desde el CRM también es una mejora futura.
En preparación: transcripción de las notas de voz a texto, para leerlas
sin reproducir y buscarlas. Quedó la base lista; se activará en una próxima
entrega.
Triage de leads (spam / no interesado / equivocado / archivar)
Cuando quien escribe es spam, un lead no interesado o un número equivocado,
puedes sacarlo del inbox activo y tratarlo distinto, sin perder el historial.
- Abre la conversación y usa el selector "Tratar conversación" en la
cabecera del hilo:
- Activa — estado normal (por defecto).
- No interesado — sigue visible pero etiquetado, para no perderle el
rastro.
- Número equivocado — sale del inbox principal.
- Spam — sale del inbox principal y revoca el opt-in del contacto
(no podrás enviarle hasta que vuelva a aceptar), por cumplimiento anti-spam
(Ley 210-14). Te pide confirmación.
- Archivar — sale del inbox sin connotación negativa (resuelto / sin
acción pendiente).
- Las conversaciones ocultas (spam / equivocado / archivadas) no se borran:
el historial y la auditoría se conservan (Ley 172-13).
- Para verlas o restaurarlas, usa el botón "Ver archivados / spam" sobre la
lista; el contador indica cuántas hay. Para reactivar una, ábrela y vuelve a
ponerla en Activa.
"Eliminar" aquí significa ocultar/archivar (reversible), nunca borrado
definitivo de los mensajes.
Auto-respuesta de primer contacto + leads nuevos
Cuando un lead te escribe por WhatsApp por primera vez, el sistema puede
reaccionar de dos formas:
- Badge 🆕 Nuevo en la lista de conversaciones: marca a todo lead que
escribió y al que nadie ha respondido aún (la auto-respuesta no cuenta
como atendido). En cuanto le respondes tú, el badge desaparece. Así
reaccionas al momento sin perder ningún lead.
- Auto-respuesta de bienvenida (opcional): un mensaje automático que se
envía una sola vez, solo al primer contacto de un lead nuevo.
Activar la auto-respuesta
En Settings del addon WhatsApp → Auto-respuesta de primer contacto:
- Activa el switch.
- Edita el mensaje (viene un texto sugerido; puedes usar "Usar texto
sugerido" para restaurarlo).
- Guardar.
Importante:
- Es gratis: responder a un cliente que escribió primero cae dentro de la
ventana de servicio de 24h de Meta (no es marketing, no requiere opt-in ni
plantilla, no tiene costo).
- Se envía una sola vez por contacto nuevo — no a conversaciones que ya
existían ni cada vez que el cliente escribe (eso protege la calidad de tu
número ante Meta).
- No se auto-responde a conversaciones marcadas como spam.
Meta NO ofrece esta automatización de forma nativa por la API en la nube
(los "mensajes de bienvenida" solo existen en la app de WhatsApp Business de
consumidor). Simpli5CRM lo hace por ti e integrado con el CRM.
Plantillas (Oleada B.2)
El catálogo de plantillas se sincroniza desde Meta Business Manager hacia
la tabla whatsapp_templates. El addon expone tres puntos de acceso:
- Sidebar → Plantillas. Vista completa: stats por estado/categoría,
filtros (status + categoría + búsqueda libre), tabla con name / category /
language / status / quality / last_synced_at. Click en una row abre un
modal preview con el body, ejemplo y los components raw (para debug).
- Settings → Plantillas (resumen). Card con stats agregados +
"Sincronizar ahora" + link a la vista completa.
- Modal de envío de factura. El selector de plantilla ahora se popula
dinámicamente desde el catálogo. factura_emitida queda como default
cuando existe; el resto de plantillas aprobadas se listan como referencia
pero su envío desde la factura no está habilitado (cada plantilla requiere
mapeo específico de placeholders — disponible desde Customer 360 quick
actions en la Oleada B.4).
Status enum
APPROVED— plantilla lista para usarse.PENDING/IN_APPEAL/PAUSED— en revisión por Meta.REJECTED/DISABLED/FLAGGED/LIMIT_EXCEEDED— no enviable.DELETED— soft-deleted localmente (Meta ya no la devuelve en sync).
Quality rating
Meta clasifica cada plantilla por calidad basándose en las quejas de los
destinatarios. GREEN = alta (sin issues), YELLOW = media (algunas quejas),
RED = baja (en riesgo de pausa). Aparece en la vista Plantillas como pill
y conviene monitorearlo: una plantilla en RED puede ser pausada por Meta
sin previo aviso.
Sincronización automática
- Diaria a las 06:00 UTC (~02:00 RD) via
pg_cronjob
whatsapp-templates-sync-daily.
- Manual desde el botón "Sincronizar ahora" en Settings o vista
Plantillas. La sync llama a la Edge fn whatsapp-templates-sync que
upserta cada plantilla por (waba_id, name, language) y soft-deletea
las que ya no aparecen en la respuesta de Meta.
Acciones rápidas WhatsApp (Oleada B.4)
Cuando el addon WhatsApp está activo, el Customer 360 sidebar muestra
botones de canal whatsapp debajo de las acciones por email. Cada botón
envía una plantilla utility aprobada en Meta con los placeholders ya
populados desde los datos del contacto — no hay nada que escribir.
Las acciones disponibles dependen del estado de la plantilla en Meta y
de los datos del contacto:
| Acción | Plantilla Meta | Se muestra cuando |
|---|---|---|
| 💬 Ver chat de WhatsApp | (ninguna — solo navega) | Contacto con teléfono válido (no requiere plantilla ni opt-in) |
| 💰 WhatsApp Saldo | recordatorio_saldo | Plantilla aprobada + contacto con teléfono + saldo pendiente > 0 |
| 📄 WhatsApp Última factura | factura_emitida | Plantilla aprobada + contacto con teléfono + existe factura |
| 📅 WhatsApp Próx. vencimiento | vencimiento_proximo | Plantilla aprobada + contacto con teléfono + factura con fecha de vencimiento futura |
💬 Ver chat de WhatsApp es la única que no envía nada: te lleva al Inbox
y abre la conversación de ese contacto. Por eso aparece **aunque la ventana de
24h esté cerrada** (ver/leer un hilo siempre se permite; la ventana de 24h solo
limita enviar mensajes libres). Si el contacto no tiene conversación con ese
número, el Inbox abre con el número ya puesto en el buscador. La acción se
oculta cuando ya estás dentro del Inbox (sería redundante).
Funcionamiento
- El addon mantiene un caché de las plantillas APPROVED en el tenant
(refrescado en background cada 5 minutos contra whatsapp_templates).
- Cuando abres el Customer 360 de un contacto, las acciones cuyos
pre-requisitos se cumplen aparecen como botones en el sidebar.
- Click en un botón envía la plantilla via
whatsapp-sendEdge fn
(mismo path que el envío manual desde la factura), con los
placeholders construidos a partir del contacto y sus facturas.
- El envío queda registrado en el activity log del contacto (o de la
factura, en el caso de Última factura / Próx. vencimiento).
Si un botón no aparece
- Plantilla no aprobada en Meta. Ver Settings → Plantillas: el status
debe ser APPROVED. Si está PENDING o REJECTED, el botón queda oculto.
- Contacto sin teléfono. Las acciones requieren
phone(con o sin +).
Editar el contacto y agregarlo.
- WhatsApp inbound sin contacto vinculado. Cuando un cliente nuevo te
escribe primero, el sidebar muestra "Contacto no vinculado" con un botón
"+ Crear contacto desde este número". Ver sección siguiente.
- Sin saldo / sin factura / sin vencimiento futuro. El botón depende
del dato que envía; si el dato no existe, el botón se oculta.
- Cache desincronizado. Settings → Plantillas → "Sincronizar ahora"
fuerza un refresh inmediato del catálogo y del cache.
Vincular un WhatsApp inbound a un contacto
Cuando un inbound llega desde un número que no está registrado en Contactos,
el Customer 360 sidebar muestra "Contacto no vinculado" con el botón **"+ Crear
contacto desde este número"**.
Pre-check automático: al hacer click el sistema re-verifica si en ese
momento ya existe un contacto con ese teléfono (por ejemplo si lo creaste
hace segundos en otra pestaña). Si lo encuentra, salta el modal y solo
refresca el sidebar — el flujo es transparente.
Si no hay match, abre el modal con dos pestañas:
Pestaña "Crear nuevo" (default) — banner verde mostrando el teléfono
de WhatsApp que va a quedar vinculado. El nombre del cliente que aparece
en su perfil de WhatsApp (push name de Meta) viene pre-cargado en el campo
Nombre. El resto de campos son opcionales:
- Apellido
- Empresa
- RNC (9 dígitos) o cédula (11)
Al hacer "Crear contacto y vincular", el contacto se crea con el teléfono
ya asociado en una sola operación. No hay UPDATE posterior — el lookup
del Customer 360 lo encuentra en el siguiente render.
Pestaña "Asociar a existente" — typeahead por nombre, empresa, RNC o
email. Importante:
- El Consumidor Final NO aparece en los resultados (es un singleton
inmutable). Si por alguna razón le quedó un teléfono asignado, contactá
soporte para limpiarlo.
- **Si el contacto seleccionado ya tenía otro teléfono distinto al de
WhatsApp**, antes de aplicar el cambio aparece una pantalla de
confirmación mostrando el teléfono actual, el que va a reemplazarlo,
y un botón rojo "Sí, reemplazar y vincular". Tienes que confirmar
explícitamente. El cancelar te lleva de vuelta al listado.
- Si el contacto no tenía teléfono o ya tenía el mismo, asocia directo
sin pedir confirmación.
Tras vincular (creado o asociado), el sidebar se re-monta inmediatamente
con el Customer 360 del contacto y las quick actions WhatsApp aplicables
aparecen. La lista de conversaciones a la izquierda también actualiza el
nombre del contacto (en lugar del teléfono raw).
Auto-opt-in al vincular desde inbound
Cuando el contacto se crea o se vincula desde un mensaje inbound de WhatsApp,
el sistema marca automáticamente el opt-in con source webhook_inbound:
- Al crear contacto nuevo: el opt-in queda granted en el mismo INSERT.
- Al asociar a un contacto existente que NO tenía opt-in: el UPDATE
setea whatsappOptInAt = now() + whatsappOptInSource = 'webhook_inbound'.
- Al asociar a un contacto que YA tenía opt-in: el opt-in original se
preserva (no se sobrescribe la fecha ni el source para no ensuciar el
audit log).
Esto cubre el caso de cumplimiento implícito: el cliente inició la
conversación → consent implícito para utility messaging (response en
ventana 24h + plantillas utility). Para marketing templates se sigue
requiriendo opt-in explícito por Settings o por el form del contacto.
Beneficio adicional: cierra el loop de B.6.1. Cuando el cliente luego
responde STOP/BAJA/CANCELAR, el trigger de auto-revoke encuentra opt-in
activo para revocar y registra el audit row correspondiente.
¿Por qué reemplaza el teléfono al asociar?
contacts.phone es una sola columna en la base. El lookup
findContactByPhone que dispara la vinculación inspecciona solo ese campo,
así que tener el teléfono de WhatsApp ahí es lo que hace que el sidebar
encuentre al contacto en sesiones futuras. Si necesitás conservar el
teléfono original (oficina, casa, etc.), agregalo en customData del
contacto, en las notas del CRM, o en el campo Empresa antes de hacer la
vinculación. Si necesitás separar "teléfono comercial" y "WhatsApp" en
columnas distintas, escribime — esa migración llega en una oleada futura.
Templates pendientes de aprobación en Meta
factura_emitida está aprobado y funcional. recordatorio_saldo y
vencimiento_proximo requieren ser creados y aprobados en
Meta WhatsApp Manager — una vez aprobados, el cron diario los recoge y
los botones aparecen automáticamente sin redeploy.
Permisos por rol
El acceso al addon se controla con tres permisos RBAC:
whatsapp:view— abrir el Inbox y ver la lista de plantillas. Es el
permiso mínimo para entrar al canal.
whatsapp:send— escribir y enviar mensajes desde el composer. Un usuario
con view pero sin send ve las conversaciones pero el composer aparece
deshabilitado (placeholder "No tienes permiso para enviar mensajes").
whatsapp:manage— acceder a Conexión y Settings (vincular/
desvincular el número, compliance, configuración del addon). Sin este permiso
esas dos pestañas no aparecen en el menú.
El propietario (owner) siempre tiene los tres. El envío también se valida en
el servidor: la Edge Function resuelve el tenant desde el token del usuario y
solo envía desde el número de ese tenant, nunca desde el de otro.
Pendiente en Oleadas siguientes
- B.3 — Tracking real de ventana 24h + costo real por mensaje.
- B.5 — Settings disconnected screen + Cost ROI card (prep Fase 3).
- Fase 2 — Webhook + status tracking (sent/delivered/read/failed) +
STOP auto-revoke real desde inbound.
- Fase 3 — Embedded Signup multi-tenant.
- V1.5 Customer 360 PDF — desbloquea la 4ª action "Estado de cuenta"
(placeholder URL al PDF generado).